(FIX): Fixed the stupid link issue. And implemented anchors
yay :)
This commit is contained in:
parent
8ece788253
commit
23a95164b7
2
Makefile
2
Makefile
@ -40,7 +40,7 @@ $(BUILD_DIR)/%.o: $(LIB_DIR)/%.cpp
|
|||||||
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ -pie
|
$(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ -pie
|
||||||
|
|
||||||
test: all
|
test: all
|
||||||
./$(TARGET)
|
./$(TARGET) test/journal.md -o output.html
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILD_DIR) $(TARGET)
|
rm -rf $(BUILD_DIR) $(TARGET)
|
||||||
|
|||||||
@ -24,4 +24,7 @@ string BoldItalicNode::ToHtml() const {
|
|||||||
|
|
||||||
string CodeNode::ToHtml() const { return "<code>" + this->content + "</code>"; }
|
string CodeNode::ToHtml() const { return "<code>" + this->content + "</code>"; }
|
||||||
|
|
||||||
string RawTextNode::ToHtml() const { return this->content; };
|
string LinkNode::ToHtml() const {
|
||||||
|
return "<a href=\"" + this->link + "\" target=\"_blank\">" + this->content +
|
||||||
|
"</a>";
|
||||||
|
}
|
||||||
|
|||||||
@ -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 <a href="{link}" target="_blank">{content}</a>.
|
||||||
*
|
*
|
||||||
* @author Hayden Hargreaves (hhargreaves2006@gmail.com)
|
* @author Hayden Hargreaves (hhargreaves2006@gmail.com)
|
||||||
*/
|
*/
|
||||||
class RawTextNode : public InlineNode {
|
class LinkNode : public InlineNode {
|
||||||
|
protected:
|
||||||
|
std::string link;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
RawTextNode(std::string content) : InlineNode(content) {};
|
LinkNode(std::string link, std::string content)
|
||||||
|
: InlineNode(content), link(link) {};
|
||||||
std::string ToHtml() const;
|
std::string ToHtml() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
137
lib/parser.cpp
137
lib/parser.cpp
@ -158,12 +158,28 @@ std::unique_ptr<Node> Parser::ParseList(bool ordered) {
|
|||||||
Consume(ordered ? 2 : 1);
|
Consume(ordered ? 2 : 1);
|
||||||
ConsumeWhiteSpace();
|
ConsumeWhiteSpace();
|
||||||
|
|
||||||
|
// std::unique_ptr<Node> Parser::ParseParagraph() {
|
||||||
|
// auto node = std::make_unique<ParagraphNode>();
|
||||||
|
//
|
||||||
|
// // 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 ('* '
|
// Parse until either '\n\n' (exit) or the next list element is found ('* '
|
||||||
// or '1.') If '\n\n', then create a node and exit
|
// or '1.') If '\n\n', then create a node and exit
|
||||||
auto children = ParseInlineListContent();
|
auto element = ParseInlineListContent();
|
||||||
for (auto &child : children) {
|
node->AddChild(std::move(element));
|
||||||
node->AddChild(std::move(child));
|
// for (auto &child : children) {
|
||||||
}
|
// node->AddChild(std::move(child));
|
||||||
|
// }
|
||||||
|
|
||||||
char c = Peek();
|
char c = Peek();
|
||||||
char c_next = Peek(1);
|
char c_next = Peek(1);
|
||||||
@ -212,7 +228,7 @@ std::unique_ptr<Node> Parser::ParseCodeBlock() {
|
|||||||
Consume();
|
Consume();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto text_node = std::make_unique<RawTextNode>(str);
|
auto text_node = std::make_unique<TextNode>(str);
|
||||||
node->AddChild(std::move(text_node));
|
node->AddChild(std::move(text_node));
|
||||||
|
|
||||||
return node;
|
return node;
|
||||||
@ -229,6 +245,14 @@ vector<std::unique_ptr<Node>> Parser::ParseInline() {
|
|||||||
if (c == '\n' && Peek(1) == '\n')
|
if (c == '\n' && Peek(1) == '\n')
|
||||||
break;
|
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) == '*') {
|
if (c == '*' && Peek(1) == '*' && Peek(2) == '*') {
|
||||||
PushTextNode(nodes, str);
|
PushTextNode(nodes, str);
|
||||||
auto node = ParseBoldItalic();
|
auto node = ParseBoldItalic();
|
||||||
@ -277,6 +301,14 @@ vector<std::unique_ptr<Node>> Parser::ParseInlineHeading() {
|
|||||||
if (c == '\n')
|
if (c == '\n')
|
||||||
break;
|
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) == '*') {
|
if (c == '*' && Peek(1) == '*' && Peek(2) == '*') {
|
||||||
PushTextNode(nodes, str);
|
PushTextNode(nodes, str);
|
||||||
auto node = ParseBoldItalic();
|
auto node = ParseBoldItalic();
|
||||||
@ -315,50 +347,70 @@ vector<std::unique_ptr<Node>> Parser::ParseInlineHeading() {
|
|||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<std::unique_ptr<Node>> Parser::ParseInlineListContent() {
|
std::unique_ptr<Node> Parser::ParseInlineListContent() {
|
||||||
vector<std::unique_ptr<Node>> nodes;
|
vector<std::unique_ptr<Node>> children;
|
||||||
string str;
|
string str;
|
||||||
|
|
||||||
while (!IsEOF()) {
|
while (!IsEOF()) {
|
||||||
char c = Peek();
|
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,
|
// If this char and next char are both newlines: then we have an empty line,
|
||||||
// we should stop.
|
// we should stop.
|
||||||
if (c == '\n' && Peek(1) == '\n')
|
if (c == '\n' && Peek(1) == '\n')
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Check if a list block has been found
|
// A single newline: We should consume whitespace and check if the next
|
||||||
if ((c == '*' || c == '-' || c == '+') && (c_next == ' ' || c_next == '\t'))
|
// character is a list item and the following item is a space
|
||||||
break;
|
if (c == '\n') {
|
||||||
|
PushTextNode(children, str);
|
||||||
|
ConsumeWhiteSpace();
|
||||||
|
char new_c = Peek();
|
||||||
|
char new_c_next = Peek(1);
|
||||||
|
|
||||||
if (std::isdigit(c) && c_next == '.')
|
if ((new_c == '*' || new_c == '-' || new_c == '+') &&
|
||||||
break;
|
(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) == '*') {
|
if (c == '*' && Peek(1) == '*' && Peek(2) == '*') {
|
||||||
PushTextNode(nodes, str);
|
PushTextNode(children, str);
|
||||||
auto node = ParseBoldItalic();
|
auto node = ParseBoldItalic();
|
||||||
if (!node->IsEmpty())
|
if (!node->IsEmpty())
|
||||||
nodes.push_back(std::move(node));
|
children.push_back(std::move(node));
|
||||||
continue;
|
continue;
|
||||||
} else if (c == '*' && Peek(1) == '*') {
|
} else if (c == '*' && Peek(1) == '*') {
|
||||||
PushTextNode(nodes, str);
|
PushTextNode(children, str);
|
||||||
auto node = ParseBold();
|
auto node = ParseBold();
|
||||||
if (!node->IsEmpty())
|
if (!node->IsEmpty())
|
||||||
nodes.push_back(std::move(node));
|
children.push_back(std::move(node));
|
||||||
continue;
|
continue;
|
||||||
} else if (c == '*') {
|
} else if (c == '*') {
|
||||||
PushTextNode(nodes, str);
|
PushTextNode(children, str);
|
||||||
auto node = ParseItalic();
|
auto node = ParseItalic();
|
||||||
if (!node->IsEmpty())
|
if (!node->IsEmpty())
|
||||||
nodes.push_back(std::move(node));
|
children.push_back(std::move(node));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c == '`') {
|
if (c == '`') {
|
||||||
PushTextNode(nodes, str);
|
PushTextNode(children, str);
|
||||||
auto node = ParseCode();
|
auto node = ParseCode();
|
||||||
if (!node->IsEmpty())
|
if (!node->IsEmpty())
|
||||||
nodes.push_back(std::move(node));
|
children.push_back(std::move(node));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,8 +420,15 @@ vector<std::unique_ptr<Node>> Parser::ParseInlineListContent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Push the last node, if the string is not empty
|
// Push the last node, if the string is not empty
|
||||||
PushTextNode(nodes, str);
|
PushTextNode(children, str);
|
||||||
return nodes;
|
|
||||||
|
// Create the list node with the children appended
|
||||||
|
auto element = std::make_unique<ListElementNode>();
|
||||||
|
for (auto &child : children) {
|
||||||
|
element->AddChild(std::move(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Node> Parser::ParseItalic() {
|
std::unique_ptr<Node> Parser::ParseItalic() {
|
||||||
@ -460,6 +519,38 @@ std::unique_ptr<Node> Parser::ParseCode() {
|
|||||||
return std::make_unique<CodeNode>(str);
|
return std::make_unique<CodeNode>(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Node> 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<LinkNode>(link, content);
|
||||||
|
}
|
||||||
|
|
||||||
void Parser::PushTextNode(vector<std::unique_ptr<Node>> &nodes, string &str) {
|
void Parser::PushTextNode(vector<std::unique_ptr<Node>> &nodes, string &str) {
|
||||||
if (!str.empty())
|
if (!str.empty())
|
||||||
nodes.push_back(std::move(std::make_unique<TextNode>(str)));
|
nodes.push_back(std::move(std::make_unique<TextNode>(str)));
|
||||||
|
|||||||
@ -127,7 +127,7 @@ private:
|
|||||||
|
|
||||||
// The only differences are the exit condition
|
// The only differences are the exit condition
|
||||||
vector<std::unique_ptr<Node>> ParseInlineHeading();
|
vector<std::unique_ptr<Node>> ParseInlineHeading();
|
||||||
vector<std::unique_ptr<Node>> ParseInlineListContent();
|
std::unique_ptr<Node> ParseInlineListContent();
|
||||||
|
|
||||||
void PushTextNode(vector<std::unique_ptr<Node>> &nodes, string &str);
|
void PushTextNode(vector<std::unique_ptr<Node>> &nodes, string &str);
|
||||||
|
|
||||||
@ -135,6 +135,7 @@ private:
|
|||||||
std::unique_ptr<Node> ParseBold();
|
std::unique_ptr<Node> ParseBold();
|
||||||
std::unique_ptr<Node> ParseBoldItalic();
|
std::unique_ptr<Node> ParseBoldItalic();
|
||||||
std::unique_ptr<Node> ParseCode();
|
std::unique_ptr<Node> ParseCode();
|
||||||
|
std::unique_ptr<Node> ParseLink();
|
||||||
|
|
||||||
char Peek(size_t offset = 0);
|
char Peek(size_t offset = 0);
|
||||||
void Consume(size_t count = 1);
|
void Consume(size_t count = 1);
|
||||||
|
|||||||
@ -61,13 +61,26 @@ string ListNode::ToHtml() const {
|
|||||||
ss << (this->ordered ? "<ol>" : "<ul>") << "\n";
|
ss << (this->ordered ? "<ol>" : "<ul>") << "\n";
|
||||||
|
|
||||||
for (const auto &child : this->GetChilren()) {
|
for (const auto &child : this->GetChilren()) {
|
||||||
ss << "<li>" << *child << "</li>" << "\n";
|
ss << *child;
|
||||||
}
|
}
|
||||||
|
|
||||||
ss << (this->ordered ? "</ol>" : "</ul>") << "\n";
|
ss << (this->ordered ? "</ol>" : "</ul>") << "\n";
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string ListElementNode::ToHtml() const {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "<li>";
|
||||||
|
|
||||||
|
for (const auto &child : this->GetChilren()) {
|
||||||
|
ss << *child;
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << "</li>" << "\n";
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
string CodeBlockNode::ToHtml() const {
|
string CodeBlockNode::ToHtml() const {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
|
|||||||
@ -111,6 +111,11 @@ public:
|
|||||||
std::string ToHtml() const;
|
std::string ToHtml() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ListElementNode : public StructureNode {
|
||||||
|
public:
|
||||||
|
std::string ToHtml() const;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @desc A code block container node.
|
* @desc A code block container node.
|
||||||
*
|
*
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user