diff --git a/input.md b/input.md index f66ff5f..a4cf29c 100644 --- a/input.md +++ b/input.md @@ -20,3 +20,21 @@ this is too far` # ***This is both!*** ###### This is neither + +- Hello world +- This is a list + + +* this is also a list +* this is still a list + + +1. This list is ordered +2. This is **number two** + +- hello +world + +- hello + +world number two diff --git a/lib/parser.cpp b/lib/parser.cpp index 1d5a7e5..4e8b052 100644 --- a/lib/parser.cpp +++ b/lib/parser.cpp @@ -72,11 +72,30 @@ std::unique_ptr Parser::ParseBlock() { // std::unique_ptr block = std::make_unique(ch); // Consume(); - if (Peek() == '#') { + char c = Peek(); + char c_next = Peek(1); + + // 1. Parse heading + if (c == '#') { return ParseHeading(); } - // this is the default case + // 2. Parser unordered list + if (c == '*' || c == '-' || c == '+') { + // Next character must be space or tab + if (c_next == ' ' || c_next == '\t') { + return ParseList(false); + } + } + + // 3. Parse ordered list + // TODO: This only checks a single digit, should check for 'n' digits + if (std::isdigit(c) && c_next == '.') { + // TODO: Do we need to check for white space? + return ParseList(true); + } + + // 4. Parser paragraph return ParseParagraph(); } @@ -120,6 +139,44 @@ std::unique_ptr Parser::ParseHeading() { return node; } +std::unique_ptr Parser::ParseList(bool ordered) { + auto node = std::make_unique(ordered); + + // Consume the required white space and list char ('* ' or '1.') + while (true) { + + Consume(ordered ? 2 : 1); + ConsumeWhiteSpace(); + + // 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)); + } + + char c = Peek(); + char c_next = Peek(1); + + // 2. Parser unordered list + if (c == '*' || c == '-' || c == '+') { + if (c_next == ' ' || c_next == '\t') { + continue; + } + } + + // 3. Parse ordered list + // TODO: This only checks a single digit, should check for 'n' digits + if (std::isdigit(c) && c_next == '.') { + continue; + } + + break; + } + + return node; +}; + vector> Parser::ParseInline() { vector> nodes; string str; @@ -217,6 +274,63 @@ vector> Parser::ParseInlineHeading() { return nodes; } +vector> Parser::ParseInlineListContent() { + vector> nodes; + string str; + + while (!IsEOF()) { + char c = Peek(); + 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; + + if (std::isdigit(c) && c_next == '.') + break; + + 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); + 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 02ccb35..1305c02 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -121,10 +121,12 @@ private: std::unique_ptr ParseParagraph(); std::unique_ptr ParseHeading(); + std::unique_ptr ParseList(bool ordered); vector> ParseInline(); - // The only difference is the exit condition + // The only differences are the exit condition vector> ParseInlineHeading(); + vector> ParseInlineListContent(); void PushTextNode(vector> &nodes, string &str); diff --git a/lib/structureNode.cpp b/lib/structureNode.cpp index 55ac4d1..ca4b00f 100644 --- a/lib/structureNode.cpp +++ b/lib/structureNode.cpp @@ -55,11 +55,15 @@ string ParagraphNode::ToHtml() const { return ss.str(); } -// TODO: Implement string ListNode::ToHtml() const { std::stringstream ss; - ss << (this->ordered ? "
    NOT YET IMPLEMENTED
" - : "
    NOT YET IMPLEMENTED
"); + ss << (this->ordered ? "
    " : "
      ") << "\n"; + + for (const auto &child : this->GetChilren()) { + ss << "
    • " << child->ToHtml() << "
    • " << "\n"; + } + + ss << (this->ordered ? "
" : "") << "\n"; return ss.str(); }