diff --git a/Makefile b/Makefile index 4380f7b..82a2f9d 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ $(BUILD_DIR)/%.o: $(LIB_DIR)/%.cpp $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ -pie test: all - ./$(TARGET) + ./$(TARGET) test/journal.md -o output.html clean: rm -rf $(BUILD_DIR) $(TARGET) diff --git a/lib/inlineNode.cpp b/lib/inlineNode.cpp index e0cced9..c69a482 100644 --- a/lib/inlineNode.cpp +++ b/lib/inlineNode.cpp @@ -24,4 +24,7 @@ string BoldItalicNode::ToHtml() const { string CodeNode::ToHtml() const { return "" + this->content + ""; } -string RawTextNode::ToHtml() const { return this->content; }; +string LinkNode::ToHtml() const { + return "link + "\" target=\"_blank\">" + this->content + + ""; +} diff --git a/lib/inlineNode.h b/lib/inlineNode.h index 9866b6e..a412297 100644 --- a/lib/inlineNode.h +++ b/lib/inlineNode.h @@ -118,15 +118,20 @@ public: }; /** - * @desc A raw text node. + * @desc An anchor/link container node. * - * This node returns only it content, with no formatting at all. + * This node is used to create an inline anchor tag. This node returns the + * following {content}. * * @author Hayden Hargreaves (hhargreaves2006@gmail.com) */ -class RawTextNode : public InlineNode { +class LinkNode : public InlineNode { +protected: + std::string link; + public: - RawTextNode(std::string content) : InlineNode(content) {}; + LinkNode(std::string link, std::string content) + : InlineNode(content), link(link) {}; std::string ToHtml() const; }; diff --git a/lib/parser.cpp b/lib/parser.cpp index 094f301..a0cc6af 100644 --- a/lib/parser.cpp +++ b/lib/parser.cpp @@ -158,12 +158,28 @@ std::unique_ptr Parser::ParseList(bool ordered) { Consume(ordered ? 2 : 1); ConsumeWhiteSpace(); + // std::unique_ptr Parser::ParseParagraph() { + // auto node = std::make_unique(); + // + // // This should call parse inline + // auto text_nodes = ParseInline(); + // for (auto &text_node : text_nodes) { + // node->AddChild(std::move(text_node)); + // } + // + // if (node->IsEmpty()) + // return nullptr; + // + // return node; + // } + // Parse until either '\n\n' (exit) or the next list element is found ('* ' // or '1.') If '\n\n', then create a node and exit - auto children = ParseInlineListContent(); - for (auto &child : children) { - node->AddChild(std::move(child)); - } + auto element = ParseInlineListContent(); + node->AddChild(std::move(element)); + // for (auto &child : children) { + // node->AddChild(std::move(child)); + // } char c = Peek(); char c_next = Peek(1); @@ -212,7 +228,7 @@ std::unique_ptr Parser::ParseCodeBlock() { Consume(); } - auto text_node = std::make_unique(str); + auto text_node = std::make_unique(str); node->AddChild(std::move(text_node)); return node; @@ -229,6 +245,14 @@ vector> Parser::ParseInline() { if (c == '\n' && Peek(1) == '\n') break; + 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(); @@ -277,6 +301,14 @@ vector> Parser::ParseInlineHeading() { if (c == '\n') break; + 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(); @@ -315,50 +347,70 @@ vector> Parser::ParseInlineHeading() { return nodes; } -vector> Parser::ParseInlineListContent() { - vector> nodes; +std::unique_ptr Parser::ParseInlineListContent() { + vector> children; string str; while (!IsEOF()) { char c = Peek(); - char c_next = Peek(1); + // char c_next = Peek(1); // If this char and next char are both newlines: then we have an empty line, // we should stop. if (c == '\n' && Peek(1) == '\n') break; - // Check if a list block has been found - if ((c == '*' || c == '-' || c == '+') && (c_next == ' ' || c_next == '\t')) - break; + // A single newline: We should consume whitespace and check if the next + // character is a list item and the following item is a space + if (c == '\n') { + PushTextNode(children, str); + ConsumeWhiteSpace(); + char new_c = Peek(); + char new_c_next = Peek(1); - if (std::isdigit(c) && c_next == '.') - break; + if ((new_c == '*' || new_c == '-' || new_c == '+') && + (new_c_next == ' ' || new_c_next == '\t')) + break; + + if (std::isdigit(new_c) && new_c_next == '.') + break; + + str += ' '; + continue; + } + + if (c == '[') { + PushTextNode(children, str); + auto node = ParseLink(); + if (!node->IsEmpty()) + children.push_back(std::move(node)); + continue; + } if (c == '*' && Peek(1) == '*' && Peek(2) == '*') { - PushTextNode(nodes, str); + PushTextNode(children, str); auto node = ParseBoldItalic(); if (!node->IsEmpty()) - nodes.push_back(std::move(node)); + children.push_back(std::move(node)); continue; } else if (c == '*' && Peek(1) == '*') { - PushTextNode(nodes, str); + PushTextNode(children, str); auto node = ParseBold(); if (!node->IsEmpty()) - nodes.push_back(std::move(node)); + children.push_back(std::move(node)); continue; } else if (c == '*') { - PushTextNode(nodes, str); + PushTextNode(children, str); auto node = ParseItalic(); if (!node->IsEmpty()) - nodes.push_back(std::move(node)); + children.push_back(std::move(node)); continue; } if (c == '`') { - PushTextNode(nodes, str); + PushTextNode(children, str); auto node = ParseCode(); if (!node->IsEmpty()) - nodes.push_back(std::move(node)); + children.push_back(std::move(node)); continue; } @@ -368,8 +420,15 @@ vector> Parser::ParseInlineListContent() { } // Push the last node, if the string is not empty - PushTextNode(nodes, str); - return nodes; + PushTextNode(children, str); + + // Create the list node with the children appended + auto element = std::make_unique(); + for (auto &child : children) { + element->AddChild(std::move(child)); + } + + return element; } std::unique_ptr Parser::ParseItalic() { @@ -460,6 +519,38 @@ std::unique_ptr Parser::ParseCode() { return std::make_unique(str); } +std::unique_ptr Parser::ParseLink() { + // Consume '[' + Consume(); + + string content; + while (!IsEOF()) { + char c = Peek(); + if (c == ']') + break; + + content += c; + Consume(); + } + + // Consume '](' + Consume(2); + + string link; + while (!IsEOF()) { + char c = Peek(); + if (c == ')') { + Consume(); + break; + } + + link += c; + Consume(); + } + + return std::make_unique(link, content); +} + void Parser::PushTextNode(vector> &nodes, string &str) { if (!str.empty()) nodes.push_back(std::move(std::make_unique(str))); diff --git a/lib/parser.h b/lib/parser.h index 12ef8b3..9298aa8 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -127,7 +127,7 @@ private: // The only differences are the exit condition vector> ParseInlineHeading(); - vector> ParseInlineListContent(); + std::unique_ptr ParseInlineListContent(); void PushTextNode(vector> &nodes, string &str); @@ -135,6 +135,7 @@ private: std::unique_ptr ParseBold(); std::unique_ptr ParseBoldItalic(); std::unique_ptr ParseCode(); + std::unique_ptr ParseLink(); char Peek(size_t offset = 0); void Consume(size_t count = 1); diff --git a/lib/structureNode.cpp b/lib/structureNode.cpp index 2c5740f..6e73409 100644 --- a/lib/structureNode.cpp +++ b/lib/structureNode.cpp @@ -61,13 +61,26 @@ string ListNode::ToHtml() const { ss << (this->ordered ? "
    " : "
      ") << "\n"; for (const auto &child : this->GetChilren()) { - ss << "
    • " << *child << "
    • " << "\n"; + ss << *child; } ss << (this->ordered ? "
" : "") << "\n"; return ss.str(); } +string ListElementNode::ToHtml() const { + std::stringstream ss; + ss << "
  • "; + + for (const auto &child : this->GetChilren()) { + ss << *child; + } + + ss << "
  • " << "\n"; + + return ss.str(); +} + string CodeBlockNode::ToHtml() const { std::stringstream ss; diff --git a/lib/structureNode.h b/lib/structureNode.h index 186268e..f803bef 100644 --- a/lib/structureNode.h +++ b/lib/structureNode.h @@ -111,6 +111,11 @@ public: std::string ToHtml() const; }; +class ListElementNode : public StructureNode { +public: + std::string ToHtml() const; +}; + /** * @desc A code block container node. *