(FEAT): Block quotes implemented #42
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) test/journal.md -o output.html
|
./$(TARGET) test/input.md -o output.html
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(BUILD_DIR) $(TARGET)
|
rm -rf $(BUILD_DIR) $(TARGET)
|
||||||
|
|||||||
107
lib/parser.cpp
107
lib/parser.cpp
@ -8,6 +8,13 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
//
|
||||||
|
// Please please PLEASSSEEEE ignore the "wetness" of this...
|
||||||
|
// The architecture is under construction...
|
||||||
|
//
|
||||||
|
// wetness means not DRY, I just made it up :)
|
||||||
|
//
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
|
||||||
@ -103,7 +110,12 @@ std::unique_ptr<Node> Parser::ParseBlock() {
|
|||||||
return ParseImage();
|
return ParseImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Parser paragraph
|
// 6. Parse block quote
|
||||||
|
if (c == '>') {
|
||||||
|
return ParseBlockQuote();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Parser paragraph
|
||||||
return ParseParagraph();
|
return ParseParagraph();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,6 +213,24 @@ std::unique_ptr<Node> Parser::ParseList(bool ordered) {
|
|||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<Node> Parser::ParseBlockQuote() {
|
||||||
|
auto node = std::make_unique<BlockQuoteNode>();
|
||||||
|
|
||||||
|
char c = Peek();
|
||||||
|
while (c == '>') {
|
||||||
|
Consume(); // Consume the '>' character
|
||||||
|
ConsumeWhiteSpace(); // Consume whitespace
|
||||||
|
c = Peek();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto children = ParseInlineBlockQuote();
|
||||||
|
for (auto &child : children) {
|
||||||
|
node->AddChild(std::move(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<Node> Parser::ParseCodeBlock() {
|
std::unique_ptr<Node> Parser::ParseCodeBlock() {
|
||||||
auto node = std::make_unique<CodeBlockNode>();
|
auto node = std::make_unique<CodeBlockNode>();
|
||||||
string str;
|
string str;
|
||||||
@ -455,6 +485,81 @@ std::unique_ptr<Node> Parser::ParseInlineListContent() {
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vector<std::unique_ptr<Node>> Parser::ParseInlineBlockQuote() {
|
||||||
|
vector<std::unique_ptr<Node>> nodes;
|
||||||
|
string str;
|
||||||
|
|
||||||
|
while (!IsEOF()) {
|
||||||
|
char c = Peek();
|
||||||
|
char c_next = Peek(1);
|
||||||
|
|
||||||
|
if (c == '\n') {
|
||||||
|
if (c_next == '\n')
|
||||||
|
break;
|
||||||
|
|
||||||
|
Consume(); // consume the '\n'
|
||||||
|
|
||||||
|
if (Peek() == '>') {
|
||||||
|
Consume(); // consume the '>'
|
||||||
|
str += " ";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c == '!' && c_next == '[') {
|
||||||
|
PushTextNode(nodes, str);
|
||||||
|
auto node = ParseImage();
|
||||||
|
if (!node->IsEmpty())
|
||||||
|
nodes.push_back(std::move(node));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
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); // TODO: Maybe we don't need this...?
|
||||||
|
Consume();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push the last node, if the string is not empty
|
||||||
|
PushTextNode(nodes, str);
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<Node> Parser::ParseItalic() {
|
std::unique_ptr<Node> Parser::ParseItalic() {
|
||||||
string str;
|
string str;
|
||||||
Consume(1);
|
Consume(1);
|
||||||
|
|||||||
@ -123,11 +123,13 @@ private:
|
|||||||
std::unique_ptr<Node> ParseHeading();
|
std::unique_ptr<Node> ParseHeading();
|
||||||
std::unique_ptr<Node> ParseList(bool ordered);
|
std::unique_ptr<Node> ParseList(bool ordered);
|
||||||
std::unique_ptr<Node> ParseCodeBlock();
|
std::unique_ptr<Node> ParseCodeBlock();
|
||||||
|
std::unique_ptr<Node> ParseBlockQuote();
|
||||||
vector<std::unique_ptr<Node>> ParseInline();
|
vector<std::unique_ptr<Node>> ParseInline();
|
||||||
|
|
||||||
// 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();
|
||||||
std::unique_ptr<Node> ParseInlineListContent();
|
std::unique_ptr<Node> ParseInlineListContent();
|
||||||
|
vector<std::unique_ptr<Node>> ParseInlineBlockQuote();
|
||||||
|
|
||||||
void PushTextNode(vector<std::unique_ptr<Node>> &nodes, string &str);
|
void PushTextNode(vector<std::unique_ptr<Node>> &nodes, string &str);
|
||||||
|
|
||||||
|
|||||||
@ -99,3 +99,15 @@ string ImageNode::ToHtml() const {
|
|||||||
ss << "<img src=\"" << this->src << "\" alt=\"" << this->alt << "\" />\n";
|
ss << "<img src=\"" << this->src << "\" alt=\"" << this->alt << "\" />\n";
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string BlockQuoteNode::ToHtml() const {
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
ss << "<blockquote>";
|
||||||
|
for (const auto &child : this->GetChilren()) {
|
||||||
|
ss << *child;
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << "</blockquote>\n";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|||||||
@ -143,4 +143,9 @@ public:
|
|||||||
std::string ToHtml() const;
|
std::string ToHtml() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class BlockQuoteNode : public StructureNode {
|
||||||
|
public:
|
||||||
|
std::string ToHtml() const;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1 +1,29 @@
|
|||||||

|
hello world
|
||||||
|
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
> hello world
|
||||||
|
> hello world
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
> **a final line**
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>
|
||||||
|
|
||||||
|
|
||||||
|
> hello
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
hi mom
|
||||||
|
|
||||||
|
> hello world\n>\n>\n>\n> a new line
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user