(FEAT): Block quotes implemented #42

Merged
azpect merged 1 commits from feature/blockquote into main 2025-11-10 22:21:27 -07:00
6 changed files with 155 additions and 3 deletions

View File

@ -40,7 +40,7 @@ $(BUILD_DIR)/%.o: $(LIB_DIR)/%.cpp
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ -pie
test: all
./$(TARGET) test/journal.md -o output.html
./$(TARGET) test/input.md -o output.html
clean:
rm -rf $(BUILD_DIR) $(TARGET)

View File

@ -8,6 +8,13 @@
#include <memory>
#include <string>
//
// Please please PLEASSSEEEE ignore the "wetness" of this...
// The architecture is under construction...
//
// wetness means not DRY, I just made it up :)
//
using std::string;
using std::vector;
@ -103,7 +110,12 @@ std::unique_ptr<Node> Parser::ParseBlock() {
return ParseImage();
}
// 6. Parser paragraph
// 6. Parse block quote
if (c == '>') {
return ParseBlockQuote();
}
// 7. Parser paragraph
return ParseParagraph();
}
@ -201,6 +213,24 @@ std::unique_ptr<Node> Parser::ParseList(bool ordered) {
return node;
};
std::unique_ptr<Node> Parser::ParseBlockQuote() {
auto node = std::make_unique<BlockQuoteNode>();
char c = Peek();
while (c == '>') {
Consume(); // Consume the '>' character
ConsumeWhiteSpace(); // Consume whitespace
c = Peek();
}
auto children = ParseInlineBlockQuote();
for (auto &child : children) {
node->AddChild(std::move(child));
}
return node;
}
std::unique_ptr<Node> Parser::ParseCodeBlock() {
auto node = std::make_unique<CodeBlockNode>();
string str;
@ -455,6 +485,81 @@ std::unique_ptr<Node> Parser::ParseInlineListContent() {
return element;
}
vector<std::unique_ptr<Node>> Parser::ParseInlineBlockQuote() {
vector<std::unique_ptr<Node>> nodes;
string str;
while (!IsEOF()) {
char c = Peek();
char c_next = Peek(1);
if (c == '\n') {
if (c_next == '\n')
break;
Consume(); // consume the '\n'
if (Peek() == '>') {
Consume(); // consume the '>'
str += " ";
continue;
}
}
if (c == '!' && c_next == '[') {
PushTextNode(nodes, str);
auto node = ParseImage();
if (!node->IsEmpty())
nodes.push_back(std::move(node));
continue;
}
if (c == '[') {
PushTextNode(nodes, str);
auto node = ParseLink();
if (!node->IsEmpty())
nodes.push_back(std::move(node));
continue;
}
if (c == '*' && Peek(1) == '*' && Peek(2) == '*') {
PushTextNode(nodes, str);
auto node = ParseBoldItalic();
if (!node->IsEmpty())
nodes.push_back(std::move(node));
continue;
} else if (c == '*' && Peek(1) == '*') {
PushTextNode(nodes, str);
auto node = ParseBold();
if (!node->IsEmpty())
nodes.push_back(std::move(node));
continue;
} else if (c == '*') {
PushTextNode(nodes, str);
auto node = ParseItalic();
if (!node->IsEmpty())
nodes.push_back(std::move(node));
continue;
}
if (c == '`') {
PushTextNode(nodes, str);
auto node = ParseCode();
if (!node->IsEmpty())
nodes.push_back(std::move(node));
continue;
}
// If a newline, use a space instead
str += (c == '\n' ? ' ' : c); // TODO: Maybe we don't need this...?
Consume();
}
// Push the last node, if the string is not empty
PushTextNode(nodes, str);
return nodes;
}
std::unique_ptr<Node> Parser::ParseItalic() {
string str;
Consume(1);

View File

@ -123,11 +123,13 @@ private:
std::unique_ptr<Node> ParseHeading();
std::unique_ptr<Node> ParseList(bool ordered);
std::unique_ptr<Node> ParseCodeBlock();
std::unique_ptr<Node> ParseBlockQuote();
vector<std::unique_ptr<Node>> ParseInline();
// The only differences are the exit condition
vector<std::unique_ptr<Node>> ParseInlineHeading();
std::unique_ptr<Node> ParseInlineListContent();
vector<std::unique_ptr<Node>> ParseInlineBlockQuote();
void PushTextNode(vector<std::unique_ptr<Node>> &nodes, string &str);

View File

@ -99,3 +99,15 @@ string ImageNode::ToHtml() const {
ss << "<img src=\"" << this->src << "\" alt=\"" << this->alt << "\" />\n";
return ss.str();
}
string BlockQuoteNode::ToHtml() const {
std::stringstream ss;
ss << "<blockquote>";
for (const auto &child : this->GetChilren()) {
ss << *child;
}
ss << "</blockquote>\n";
return ss.str();
}

View File

@ -143,4 +143,9 @@ public:
std::string ToHtml() const;
};
class BlockQuoteNode : public StructureNode {
public:
std::string ToHtml() const;
};
#endif

View File

@ -1 +1,29 @@
![alt text](image source)
hello world
>
>
>
> hello world
> hello world
>
>
>
>
>
>
>
> **a final line**
>
>
>
>
>
> hello
hi mom
> hello world\n>\n>\n>\n> a new line