From e2178604dd0ee4f3d8fd97ad26af60b9b7c2fae1 Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Mon, 10 Nov 2025 22:16:12 -0700 Subject: [PATCH] (FEAT): Block quotes implemented However, an issue has been outlines, deeper recursion. Recursive functions should by nature...recurse. Which they do not... --- Makefile | 2 +- lib/parser.cpp | 107 +++++++++++++++++++++++++++++++++++++++++- lib/parser.h | 2 + lib/structureNode.cpp | 12 +++++ lib/structureNode.h | 5 ++ test/input.md | 30 +++++++++++- 6 files changed, 155 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 82a2f9d..19ea4f2 100644 --- a/Makefile +++ b/Makefile @@ -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) diff --git a/lib/parser.cpp b/lib/parser.cpp index 776ac79..da9630f 100644 --- a/lib/parser.cpp +++ b/lib/parser.cpp @@ -8,6 +8,13 @@ #include #include +// +// 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 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 Parser::ParseList(bool ordered) { return node; }; +std::unique_ptr Parser::ParseBlockQuote() { + auto node = std::make_unique(); + + 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 Parser::ParseCodeBlock() { auto node = std::make_unique(); string str; @@ -455,6 +485,81 @@ std::unique_ptr Parser::ParseInlineListContent() { return element; } +vector> Parser::ParseInlineBlockQuote() { + vector> 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 Parser::ParseItalic() { string str; Consume(1); diff --git a/lib/parser.h b/lib/parser.h index 5407b5c..ce806e3 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -123,11 +123,13 @@ private: std::unique_ptr ParseHeading(); std::unique_ptr ParseList(bool ordered); std::unique_ptr ParseCodeBlock(); + std::unique_ptr ParseBlockQuote(); vector> ParseInline(); // The only differences are the exit condition vector> ParseInlineHeading(); std::unique_ptr ParseInlineListContent(); + vector> ParseInlineBlockQuote(); void PushTextNode(vector> &nodes, string &str); diff --git a/lib/structureNode.cpp b/lib/structureNode.cpp index 2e3dc67..0579f20 100644 --- a/lib/structureNode.cpp +++ b/lib/structureNode.cpp @@ -99,3 +99,15 @@ string ImageNode::ToHtml() const { ss << "src << "\" alt=\"" << this->alt << "\" />\n"; return ss.str(); } + +string BlockQuoteNode::ToHtml() const { + std::stringstream ss; + + ss << "
"; + for (const auto &child : this->GetChilren()) { + ss << *child; + } + + ss << "
\n"; + return ss.str(); +} diff --git a/lib/structureNode.h b/lib/structureNode.h index dbeceaa..debfb4a 100644 --- a/lib/structureNode.h +++ b/lib/structureNode.h @@ -143,4 +143,9 @@ public: std::string ToHtml() const; }; +class BlockQuoteNode : public StructureNode { +public: + std::string ToHtml() const; +}; + #endif diff --git a/test/input.md b/test/input.md index 274733b..71225b8 100644 --- a/test/input.md +++ b/test/input.md @@ -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