diff --git a/input.md b/input.md
index f66ff5f..cd608b8 100644
--- a/input.md
+++ b/input.md
@@ -20,3 +20,28 @@ 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
+
+```
+int x = 5;
+int y = 10;
+
+console.log(x + y); // '15'
+```
diff --git a/lib/inlineNode.cpp b/lib/inlineNode.cpp
index 625de65..e0cced9 100644
--- a/lib/inlineNode.cpp
+++ b/lib/inlineNode.cpp
@@ -23,3 +23,5 @@ string BoldItalicNode::ToHtml() const {
}
string CodeNode::ToHtml() const { return "" + this->content + ""; }
+
+string RawTextNode::ToHtml() const { return this->content; };
diff --git a/lib/inlineNode.h b/lib/inlineNode.h
index af825a7..9866b6e 100644
--- a/lib/inlineNode.h
+++ b/lib/inlineNode.h
@@ -117,4 +117,17 @@ public:
std::string ToHtml() const;
};
+/**
+ * @desc A raw text node.
+ *
+ * This node returns only it content, with no formatting at all.
+ *
+ * @author Hayden Hargreaves (hhargreaves2006@gmail.com)
+ */
+class RawTextNode : public InlineNode {
+public:
+ RawTextNode(std::string content) : InlineNode(content) {};
+ std::string ToHtml() const;
+};
+
#endif
diff --git a/lib/parser.cpp b/lib/parser.cpp
index 1d5a7e5..f01c505 100644
--- a/lib/parser.cpp
+++ b/lib/parser.cpp
@@ -4,6 +4,7 @@
#include "structureNode.h"
#include
#include
+#include
#include
#include
@@ -72,11 +73,35 @@ 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. Parse code block
+ if (c == '`' && c_next == '`' && Peek(2) == '`') {
+ return ParseCodeBlock();
+ }
+
+ // 5. Parser paragraph
return ParseParagraph();
}
@@ -120,6 +145,75 @@ 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;
+};
+
+std::unique_ptr Parser::ParseCodeBlock() {
+ auto node = std::make_unique();
+ string str;
+
+ // Remove the first three characters, the '```'
+ Consume(3);
+
+ // Parse text into a single text node until '```' is found, include everything
+ // else
+ while (!IsEOF()) {
+ char c = Peek();
+ if (c == '`' && Peek(1) == '`' && Peek(2) == '`') {
+ Consume(3);
+ break;
+ }
+
+ // Swap any '\n' with BR tags, so it will visually break
+ if (c == '\n')
+ str += "\n
\n";
+ else
+ str += c;
+
+ Consume();
+ }
+
+ auto text_node = std::make_unique(str);
+ node->AddChild(std::move(text_node));
+
+ return node;
+}
+
vector> Parser::ParseInline() {
vector> nodes;
string str;
@@ -217,6 +311,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..12ef8b3 100644
--- a/lib/parser.h
+++ b/lib/parser.h
@@ -121,10 +121,13 @@ private:
std::unique_ptr ParseParagraph();
std::unique_ptr ParseHeading();
+ std::unique_ptr ParseList(bool ordered);
+ std::unique_ptr ParseCodeBlock();
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..9516c3b 100644
--- a/lib/structureNode.cpp
+++ b/lib/structureNode.cpp
@@ -55,11 +55,28 @@ string ParagraphNode::ToHtml() const {
return ss.str();
}
-// TODO: Implement
string ListNode::ToHtml() const {
std::stringstream ss;
- ss << (this->ordered ? "NOT YET IMPLEMENTED
"
- : "");
+ ss << (this->ordered ? "" : "") << "\n";
+
+ for (const auto &child : this->GetChilren()) {
+ ss << "- " << child->ToHtml() << "
" << "\n";
+ }
+
+ ss << (this->ordered ? "
" : "") << "\n";
+ return ss.str();
+}
+
+string CodeBlockNode::ToHtml() const {
+ std::stringstream ss;
+
+ ss << "\n";
+
+ for (const auto &child : this->GetChilren()) {
+ ss << child->ToHtml() << "\n";
+ }
+
+ ss << "\n";
return ss.str();
}
diff --git a/lib/structureNode.h b/lib/structureNode.h
index 7e07bf8..186268e 100644
--- a/lib/structureNode.h
+++ b/lib/structureNode.h
@@ -111,4 +111,19 @@ public:
std::string ToHtml() const;
};
+/**
+ * @desc A code block container node.
+ *
+ * This node is used to wrap a code block node. When three '`' are used a
+ * code block should be created. This node's children are expected to be simple
+ * text nodes - containing no formatting at all. Since code blocks are not
+ * parsed any deeper then their parents.
+ *
+ * @author Hayden Hargreaves (hhargreaves2006@gmail.com)
+ */
+class CodeBlockNode : public StructureNode {
+public:
+ std::string ToHtml() const;
+};
+
#endif