Feature: Implemented code blocks and list elements #30
18
input.md
18
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
|
||||
|
||||
118
lib/parser.cpp
118
lib/parser.cpp
@ -72,11 +72,30 @@ std::unique_ptr<Node> Parser::ParseBlock() {
|
||||
// std::unique_ptr<Node> block = std::make_unique<TextNode>(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<Node> Parser::ParseHeading() {
|
||||
return node;
|
||||
}
|
||||
|
||||
std::unique_ptr<Node> Parser::ParseList(bool ordered) {
|
||||
auto node = std::make_unique<ListNode>(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<std::unique_ptr<Node>> Parser::ParseInline() {
|
||||
vector<std::unique_ptr<Node>> nodes;
|
||||
string str;
|
||||
@ -217,6 +274,63 @@ vector<std::unique_ptr<Node>> Parser::ParseInlineHeading() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
vector<std::unique_ptr<Node>> Parser::ParseInlineListContent() {
|
||||
vector<std::unique_ptr<Node>> 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<Node> Parser::ParseItalic() {
|
||||
string str;
|
||||
Consume(1);
|
||||
|
||||
@ -121,10 +121,12 @@ private:
|
||||
|
||||
std::unique_ptr<Node> ParseParagraph();
|
||||
std::unique_ptr<Node> ParseHeading();
|
||||
std::unique_ptr<Node> ParseList(bool ordered);
|
||||
vector<std::unique_ptr<Node>> ParseInline();
|
||||
|
||||
// The only difference is the exit condition
|
||||
// The only differences are the exit condition
|
||||
vector<std::unique_ptr<Node>> ParseInlineHeading();
|
||||
vector<std::unique_ptr<Node>> ParseInlineListContent();
|
||||
|
||||
void PushTextNode(vector<std::unique_ptr<Node>> &nodes, string &str);
|
||||
|
||||
|
||||
@ -55,11 +55,15 @@ string ParagraphNode::ToHtml() const {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// TODO: Implement
|
||||
string ListNode::ToHtml() const {
|
||||
std::stringstream ss;
|
||||
ss << (this->ordered ? "<ol>NOT YET IMPLEMENTED</ol>"
|
||||
: "<ul>NOT YET IMPLEMENTED</ul>");
|
||||
|
||||
ss << (this->ordered ? "<ol>" : "<ul>") << "\n";
|
||||
|
||||
for (const auto &child : this->GetChilren()) {
|
||||
ss << "<li>" << child->ToHtml() << "</li>" << "\n";
|
||||
}
|
||||
|
||||
ss << (this->ordered ? "</ol>" : "</ul>") << "\n";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user