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.
*