タグで囲まれ、その中に
11 | //
タグが入る.
12 | struct CodeBlock : public ASTNode {
13 | private:
14 | // コードの中身。
15 | std::string code;
16 |
17 | // Highlight.js
18 | // によって正確にハイライトするために、言語を指定する必要がある。
19 | // ```python -> python を持っておき、 `to_html` する際に として出力する。
21 | std::string language;
22 |
23 | public:
24 | CodeBlock(std::string code, std::string language)
25 | : code(code), language(language) {
26 | set_uuid();
27 | }
28 |
29 | std::string to_html() const override {
30 | std::string code_class;
31 |
32 | if (language == "") {
33 | code_class = "language-plaintext";
34 | } else {
35 | code_class = "language-" + language;
36 | }
37 |
38 | return " " + escape_for_html(code) + "
";
40 | }
41 |
42 | std::map get_properties() const override {
43 | return {{"code", code}, {"language", language}};
44 | }
45 |
46 | std::string get_classname() const override { return "CodeBlock"; }
47 | };
48 |
49 | struct CodeBlockSyntax : public BlockSyntax {
50 | bool operator()(Reader &read) const override {
51 | if (!read.is_line_begin()) return false;
52 | return read.get_row().starts_with("```");
53 | }
54 | void operator()(Reader &read, ASTNode &ast) const override {
55 | // BEGIN : '```(.*)'
56 | // END : '```\s*'
57 |
58 | std::string language = rtrim(read.get_row().substr(3));
59 | read.move_next_line();
60 | std::string code;
61 | while (!read.is_eof()) {
62 | if (rtrim(read.get_row()) == "```") {
63 | read.move_next_line();
64 | break;
65 | }
66 | code += read.get_row() + "\n";
67 | read.move_next_line();
68 | }
69 |
70 | CodeBlock node(code, language);
71 | ast.pushback_child(std::make_shared(node));
72 | }
73 | };
74 |
75 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/DivBlock.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 | #include "Markdown.hpp"
8 |
9 | namespace almo {
10 |
11 | struct DivBlock : public ASTNode {
12 | private:
13 | std::string div_class;
14 |
15 | public:
16 | DivBlock(std::string div_class) : div_class(div_class) { set_uuid(); }
17 |
18 | std::string to_html() const override {
19 | return "" +
20 | concatenated_childs_html() + "
";
21 | }
22 |
23 | std::map get_properties() const override {
24 | return {
25 | {"div_class", div_class},
26 | };
27 | }
28 | std::string get_classname() const override { return "DivBlock"; }
29 | };
30 |
31 | struct DivBlockSyntax : public BlockSyntax {
32 | bool operator()(Reader &read) const override {
33 | if (!read.is_line_begin()) return false;
34 | if (!read.get_row().starts_with(":::")) return false;
35 | if (rtrim(read.get_row()) != ":::") return true;
36 | return false;
37 | }
38 | void operator()(Reader &read, ASTNode &ast) const override {
39 | std::stack> scopes;
40 | std::string div_class = read.get_row().substr(3);
41 |
42 | DivBlock root_div(div_class);
43 | scopes.push(std::make_shared(root_div));
44 |
45 | read.move_next_line();
46 | std::vector text;
47 |
48 | while (!read.is_eof()) {
49 | // Invariant : scopes.size() >= 1
50 | if (rtrim(read.get_row()) == ":::") {
51 | Markdown inner_md;
52 | MarkdownParser parser(text);
53 | parser.process(inner_md);
54 | auto inner_md_ptr = std::make_shared(inner_md);
55 | scopes.top()->pushback_child(inner_md_ptr);
56 |
57 | text = {};
58 | read.move_next_line();
59 |
60 | if (scopes.size() == 1u) {
61 | ast.pushback_child(scopes.top());
62 | scopes.pop();
63 | break;
64 | }
65 | scopes.pop();
66 | continue;
67 | }
68 | if (read.get_row().starts_with(":::")) {
69 | Markdown inner_md;
70 | MarkdownParser parser(text);
71 | parser.process(inner_md);
72 | auto inner_md_ptr = std::make_shared(inner_md);
73 | scopes.top()->pushback_child(inner_md_ptr);
74 |
75 | DivBlock new_node(read.get_row().substr(3));
76 | auto new_node_ptr = std::make_shared(new_node);
77 | scopes.top()->pushback_child(new_node_ptr);
78 |
79 | scopes.push(new_node_ptr);
80 |
81 | text = {};
82 | read.move_next_line();
83 | continue;
84 | }
85 | text.emplace_back(read.get_row());
86 | read.move_next_line();
87 | }
88 | // scopes.empty() iff completed DivBlock
89 | while (!scopes.empty()) {
90 | Markdown inner_md;
91 | MarkdownParser parser(text);
92 | parser.process(inner_md);
93 | auto inner_md_ptr = std::make_shared(inner_md);
94 | scopes.top()->pushback_child(inner_md_ptr);
95 |
96 | text = {};
97 |
98 | if (scopes.size() == 1u) {
99 | ast.pushback_child(scopes.top());
100 | scopes.pop();
101 | break;
102 | }
103 | scopes.pop();
104 | continue;
105 | }
106 | assert(scopes.empty());
107 | }
108 | };
109 |
110 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/EOF.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 |
7 | namespace almo {
8 |
9 | struct EOFSyntax : public BlockSyntax {
10 | bool operator()(Reader &read) const override { return read.is_eof(); }
11 | void operator()(Reader &read, ASTNode &ast) const override {
12 | read.read_eof();
13 | }
14 | };
15 |
16 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/EnumerateBlock.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 | #include "EOF.hpp"
8 | #include "Item.hpp"
9 | #include "NewLine.hpp"
10 |
11 | namespace almo {
12 |
13 | // 番号付き箇条書きを表すクラス
14 | struct EnumerateBlock : public ASTNode {
15 | public:
16 | EnumerateBlock() { set_uuid(); }
17 |
18 | std::string to_html() const override {
19 | return "" + concatenated_childs_html() + "
";
20 | }
21 |
22 | std::map get_properties() const override {
23 | return {};
24 | }
25 | std::string get_classname() const override { return "EnumerateBlock"; }
26 | };
27 |
28 | struct EnumerateBlockSyntax : public BlockSyntax {
29 | static inline const std::regex rex = std::regex(R"(\d+\. (.*))");
30 | bool operator()(Reader &read) const override {
31 | if (!read.is_line_begin()) return false;
32 | return std::regex_match(read.get_row(), rex);
33 | }
34 | void operator()(Reader &read, ASTNode &ast) const override {
35 | // BEGIN : rex match
36 | // END : NewLine or EOF
37 |
38 | std::stack> scopes;
39 | EnumerateBlock root_enum;
40 | scopes.push(std::make_shared(root_enum));
41 |
42 | std::string current_match = R"(\d+\. (.*))";
43 | // width : 3
44 | const std::string INDENT = " ";
45 |
46 | // if s is considered a enum definition, return its depth"
47 | // otherwise return std::string::npos
48 | auto enum_depth = [](std::string s) -> std::size_t {
49 | std::size_t pos = s.find_first_not_of(" \n\r\t");
50 | // s is consist of only space
51 | if (pos == std::string::npos) return std::string::npos;
52 | // s is enum definition
53 | if (std::regex_match(s.substr(pos), rex)) return pos;
54 | // otherwise
55 | return std::string::npos;
56 | };
57 |
58 | std::string text = "";
59 |
60 | while (true) {
61 | // Invariant : read.is_eof() == false
62 | // scopes.size() >= 1
63 | text += [&] {
64 | std::smatch sm;
65 | // smatch manage an iterator of the matched string `line`
66 | // --> the string should not be a temporary object
67 | std::string line = read.get_row();
68 | std::regex_search(line, sm, std::regex(current_match));
69 | return sm.format("$1");
70 | }();
71 |
72 | // within while loop, read changes only here
73 | read.move_next_line();
74 |
75 | // END
76 | if (EOFSyntax{}(read) || NewLineSyntax{}(read)) {
77 | Item item;
78 | InlineParser::process(text, item);
79 | scopes.top()->pushback_child(std::make_shared- (item));
80 | text = "";
81 | break;
82 | }
83 | // same depth
84 | if (std::regex_match(read.get_row(), std::regex(current_match))) {
85 | Item item;
86 | InlineParser::process(text, item);
87 | scopes.top()->pushback_child(std::make_shared
- (item));
88 | text = "";
89 | continue;
90 | }
91 | // +3 depth
92 | if (std::regex_match(read.get_row(),
93 | std::regex(INDENT + current_match))) {
94 | Item item;
95 | InlineParser::process(text, item);
96 | scopes.top()->pushback_child(std::make_shared
- (item));
97 | text = "";
98 |
99 | // nested EnumerateBlock
100 | EnumerateBlock new_node;
101 | auto new_node_ptr = std::make_shared(new_node);
102 | scopes.top()->pushback_child(new_node_ptr);
103 | scopes.push(new_node_ptr);
104 |
105 | // update match
106 | current_match = INDENT + current_match;
107 | continue;
108 | }
109 | // -3n depth (n >= 1)
110 | if ([&] {
111 | std::size_t depth = enum_depth(read.get_row());
112 | if (depth == std::string::npos) return false;
113 | std::size_t current = (scopes.size() - 1u) * 3u;
114 | // depth == current, depth == current + 3 is already
115 | // ditected depth has decreased and the difference is
116 | // multiple of 3
117 | if (depth < current && (current - depth) % 3 == 0)
118 | return true;
119 | // ambiguous enum definition detected
120 | std::cerr << "Warning: ambiguous enum\n ... \n"
121 | << read.near() << "\n^^^ parsing line"
122 | << std::endl;
123 | std::cerr << "indent width must be 3. this line is "
124 | "considered as raw text."
125 | << std::endl;
126 | return false;
127 | }()) {
128 | std::size_t delete_indent =
129 | (scopes.size() - 1u) * 3u - enum_depth(read.get_row());
130 | current_match = current_match.substr(delete_indent);
131 |
132 | Item item;
133 | InlineParser::process(text, item);
134 | scopes.top()->pushback_child(std::make_shared
- (item));
135 | text = "";
136 |
137 | for (std::size_t del = 0; del < delete_indent; del += 3u) {
138 | scopes.pop();
139 | }
140 | continue;
141 | }
142 | // Ensure : read.get_row() is considered a continuation of the
143 | // previous item
144 | }
145 | while (scopes.size() > 1u) {
146 | scopes.pop();
147 | }
148 | ast.pushback_child(scopes.top());
149 | }
150 | };
151 |
152 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/ExecutableCodeBlock.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 |
8 | namespace almo {
9 |
10 | // 実行可能なコードブロックを表すクラス.
11 | // `Judge` と異なり、こちらはジャッジを提供せず、単に実行のみを行う。
12 | struct ExecutableCodeBlock : public ASTNode {
13 | private:
14 | std::string code;
15 | std::string editor_theme;
16 |
17 | public:
18 | ExecutableCodeBlock(std::string code, std::string editor_theme)
19 | : code(code), editor_theme(editor_theme) {
20 | set_uuid();
21 | }
22 |
23 | std::string to_html() const override {
24 | // コード全体を表示するために、何行のコードかを調べておく
25 | int n_line = std::count(code.begin(), code.end(), '\n');
26 |
27 | std::string uuid_str = get_uuid_str();
28 |
29 | // `minLines` と `maxLines`
30 | // を適切に設定してコード全体を表示するようにする。
31 | std::string ace_editor =
32 | ""
33 | "\n";
60 |
61 | // 通常の出力に加えて、matplotlib のプロットを表示するための div
62 | // を作っておく。
63 | std::string editor_div = "
\n \n";
66 | std::string out_area =
67 | "\n";
68 | std::string plot_area =
69 | "\n";
70 |
71 | std::string run_button =
72 | "\n";
74 |
75 | std::string output =
76 | editor_div + ace_editor + out_area + plot_area + run_button;
77 |
78 | return output;
79 | }
80 |
81 | std::map get_properties() const override {
82 | return {{"code", code}};
83 | }
84 | std::string get_classname() const override { return "ExecutableCodeBlock"; }
85 | };
86 |
87 | struct ExecutableCodeBlockSyntax : public BlockSyntax {
88 | bool operator()(Reader &read) const override {
89 | if (!read.is_line_begin()) return false;
90 | if (rtrim(read.get_row()) == ":::code") return true;
91 | return false;
92 | }
93 | void operator()(Reader &read, ASTNode &ast) const override {
94 | std::string code = "";
95 |
96 | // skip :::code
97 | read.move_next_line();
98 |
99 | while (!read.is_eof()) {
100 | if (rtrim(read.get_row()) == ":::") {
101 | read.move_next_line();
102 | break;
103 | }
104 | code += read.get_row() + "\n";
105 | read.move_next_line();
106 | }
107 |
108 | ExecutableCodeBlock node(code, "{{ editor_theme }}");
109 | ast.pushback_child(std::make_shared(node));
110 | }
111 | };
112 |
113 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/FootnoteDefinition.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 |
8 | namespace almo {
9 | struct FootnoteDefinition : public ASTNode {
10 | private:
11 | std::string symbol;
12 |
13 | public:
14 | FootnoteDefinition(std::string symbol_) : symbol(symbol_) { set_uuid(); }
15 | std::string to_html() const override {
16 | std::string childs_html = concatenated_childs_html();
17 | std::string label = "label_" + symbol;
18 | std::string jump_to = "ref_" + symbol;
19 |
20 | return "";
22 | }
23 | std::map get_properties() const override {
24 | return {{"symbol", symbol}};
25 | }
26 | std::string get_classname() const override { return "FootnoteDefinition"; }
27 | };
28 |
29 | struct FootnoteDefinitionSyntax : public BlockSyntax {
30 | /*
31 | "[^" + symbol + "]: + hoge"
32 | */
33 | static inline const std::regex rex = std::regex(R"(\[\^(.*)\]:(.*))");
34 | bool operator()(Reader &read) const override {
35 | if (!read.is_line_begin()) return false;
36 | std::string row = read.get_row();
37 | std::smatch sm;
38 | return std::regex_match(row, sm, rex);
39 | }
40 | void operator()(Reader &read, ASTNode &ast) const override {
41 | std::string row = read.get_row();
42 | std::smatch sm;
43 | std::regex_search(row, sm, rex);
44 | std::string symbol = sm.format("$1");
45 | std::string suffix = sm.format("$2");
46 | if (symbol.empty()) {
47 | std::cerr << "Warning: Footnote symbol is empty. This may cause "
48 | "unexpected behavior. \n... \n"
49 | << read.near() << "\n^^^ parsing line" << std::endl;
50 | }
51 |
52 | FootnoteDefinition node(symbol);
53 | InlineParser::process(suffix, node);
54 |
55 | ast.pushback_child(std::make_shared(node));
56 | read.move_next_line();
57 | }
58 | };
59 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/Header.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 |
7 | namespace almo {
8 |
9 | struct Header : public ASTNode {
10 | private:
11 | int level;
12 |
13 | public:
14 | Header(int _level) : level(_level) { set_uuid(); }
15 | std::string to_html() const override {
16 | std::string childs_html = concatenated_childs_html();
17 | std::string contents_push =
18 | ""
19 | "\n";
32 |
33 | return contents_push + "" + childs_html + "";
36 | }
37 |
38 | std::map get_properties() const override {
39 | return {{"level", std::to_string(level)}};
40 | }
41 | std::string get_classname() const override { return "Header"; }
42 | };
43 |
44 | struct HeaderSyntax : BlockSyntax {
45 | bool operator()(Reader &read) const override {
46 | if (!read.is_line_begin()) return false;
47 | if (read.get_row().starts_with("# ")) return true;
48 | if (read.get_row().starts_with("## ")) return true;
49 | if (read.get_row().starts_with("### ")) return true;
50 | if (read.get_row().starts_with("#### ")) return true;
51 | if (read.get_row().starts_with("##### ")) return true;
52 | if (read.get_row().starts_with("###### ")) return true;
53 | return false;
54 | }
55 | void operator()(Reader &read, ASTNode &ast) const override {
56 | int level = 0;
57 | if (read.get_row().starts_with("# ")) level = 1;
58 | if (read.get_row().starts_with("## ")) level = 2;
59 | if (read.get_row().starts_with("### ")) level = 3;
60 | if (read.get_row().starts_with("#### ")) level = 4;
61 | if (read.get_row().starts_with("##### ")) level = 5;
62 | if (read.get_row().starts_with("###### ")) level = 6;
63 | assert(1 <= level && level <= 6);
64 | Header node(level);
65 | InlineParser::process(read.get_row().substr(level + 1), node);
66 | ast.pushback_child(std::make_shared(node));
67 | read.move_next_line();
68 | }
69 | };
70 |
71 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/HorizontalLine.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 |
8 | namespace almo {
9 |
10 | struct HorizontalLine : public ASTNode {
11 | public:
12 | HorizontalLine() { set_uuid(); }
13 |
14 | std::string to_html() const override { return "
"; }
15 |
16 | std::map get_properties() const override {
17 | return {};
18 | }
19 | std::string get_classname() const override { return "HorizontalLine"; }
20 | };
21 |
22 | struct HorizontalLineSyntax : BlockSyntax {
23 | bool operator()(Reader &read) const override {
24 | if (!read.is_line_begin()) return false;
25 | if (rtrim(read.get_row()) == "---") return true;
26 | if (rtrim(read.get_row()) == "___") return true;
27 | if (rtrim(read.get_row()) == "***") return true;
28 | return false;
29 | }
30 | void operator()(Reader &read, ASTNode &ast) const override {
31 | HorizontalLine node;
32 | ast.pushback_child(std::make_shared(node));
33 | read.move_next_line();
34 | }
35 | };
36 |
37 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/InlineCodeBlock.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 |
8 | namespace almo {
9 |
10 | struct InlineCodeBlock : public ASTNode {
11 | private:
12 | std::string code;
13 |
14 | public:
15 | InlineCodeBlock(std::string code) : code(code) { set_uuid(); }
16 |
17 | std::string to_html() const override {
18 | return " " + escape_for_html(code) +
19 | "
";
20 | }
21 |
22 | std::map get_properties() const override {
23 | return {{"code", code}};
24 | }
25 | std::string get_classname() const override { return "InlineCodeBlock"; }
26 | };
27 |
28 | struct InlineCodeBlockSyntax : public InlineSyntax {
29 | static inline const std::regex rex = std::regex(R"((.*?)\`(.*?)\`(.*))");
30 | int operator()(const std::string &str) const override {
31 | std::smatch sm;
32 | if (std::regex_search(str, sm, rex)) {
33 | return sm.position(2) - 1;
34 | }
35 | return std::numeric_limits::max();
36 | }
37 | void operator()(const std::string &str, ASTNode &ast) const override {
38 | std::smatch sm;
39 | std::regex_search(str, sm, rex);
40 | std::string prefix = sm.format("$1");
41 | std::string code = sm.format("$2");
42 | std::string suffix = sm.format("$3");
43 | InlineParser::process(prefix, ast);
44 | InlineCodeBlock node(code);
45 | ast.pushback_child(std::make_shared(node));
46 | InlineParser::process(suffix, ast);
47 | }
48 | };
49 |
50 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/InlineFootnoteReference.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 |
8 | namespace almo {
9 | struct InlineFootnoteReference : public ASTNode {
10 | private:
11 | std::string symbol;
12 |
13 | public:
14 | InlineFootnoteReference(std::string _symbol) : symbol(_symbol) {
15 | set_uuid();
16 | }
17 |
18 | std::string to_html() const override {
19 | // This is correct.
20 | // `label_` is setted in `FootnoteDefinition` (which we have to jump to)
21 | std::string label = "ref_" + symbol;
22 | std::string jump_to = "label_" + symbol;
23 |
24 | return "";
27 | }
28 |
29 | std::map get_properties() const override {
30 | return {{"symbol", symbol}};
31 | }
32 | std::string get_classname() const override {
33 | return "InlineFootnoteReference";
34 | }
35 | };
36 | struct InlineFootnoteReferenceSyntax : public InlineSyntax {
37 | static inline const std::regex rex = std::regex(R"((.*?)\[\^(.*?)\](.*))");
38 | int operator()(const std::string &str) const override {
39 | std::smatch sm;
40 | if (std::regex_search(str, sm, rex)) {
41 | return sm.position(2) - 1;
42 | }
43 | return std::numeric_limits::max();
44 | }
45 | void operator()(const std::string &str, ASTNode &ast) const override {
46 | std::smatch sm;
47 | std::regex_search(str, sm, rex);
48 | std::string prefix = sm.format("$1");
49 | std::string symbol = sm.format("$2");
50 | std::string suffix = sm.format("$3");
51 | InlineParser::process(prefix, ast);
52 | InlineFootnoteReference node(symbol);
53 | ast.pushback_child(std::make_shared(node));
54 | InlineParser::process(suffix, ast);
55 | }
56 | };
57 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/InlineImage.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 |
8 | namespace almo {
9 |
10 | struct InlineImage : public ASTNode {
11 | private:
12 | std::string url;
13 | std::string caption;
14 |
15 | public:
16 | InlineImage(std::string url, std::string caption)
17 | : url(url), caption(caption) {
18 | set_uuid();
19 | }
20 |
21 | // タグを使うことで キャプションなどをつける。
22 | std::string to_html() const override {
23 | std::string output = "
";
24 | std::string figcaption = "" + caption + "";
25 | return "" + output + figcaption + "";
26 | }
27 |
28 | std::map get_properties() const override {
29 | return {{"url", url}, {"caption", caption}};
30 | }
31 | std::string get_classname() const override { return "InlineImage"; }
32 | };
33 |
34 | struct InlineImageSyntax : public InlineSyntax {
35 | static inline const std::regex rex =
36 | std::regex(R"((.*?)\!\[(.*?)\]\((.*?)\)(.*))");
37 | int operator()(const std::string &str) const override {
38 | std::smatch sm;
39 | if (std::regex_search(str, sm, rex)) {
40 | return sm.position(2) - 2;
41 | }
42 | return std::numeric_limits::max();
43 | }
44 | void operator()(const std::string &str, ASTNode &ast) const override {
45 | std::smatch sm;
46 | std::regex_search(str, sm, rex);
47 | std::string prefix = sm.format("$1");
48 | std::string caption = sm.format("$2");
49 | std::string url = sm.format("$3");
50 | std::string suffix = sm.format("$4");
51 | InlineParser::process(prefix, ast);
52 | InlineImage node(url, caption);
53 | ast.pushback_child(std::make_shared(node));
54 | InlineParser::process(suffix, ast);
55 | }
56 | };
57 |
58 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/InlineItalic.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 |
7 | namespace almo {
8 |
9 | struct InlineItalic : public ASTNode {
10 | public:
11 | InlineItalic() { set_uuid(); }
12 |
13 | std::string to_html() const override {
14 | return " " + concatenated_childs_html() +
15 | " ";
16 | }
17 |
18 | std::map get_properties() const override {
19 | return {};
20 | }
21 | std::string get_classname() const override { return "InlineItalic"; }
22 | };
23 |
24 | struct InlineItalicSyntax : public InlineSyntax {
25 | static inline const std::regex rex = std::regex(R"((.*?)\*(.*?)\*(.*))");
26 | int operator()(const std::string &str) const override {
27 | std::smatch sm;
28 | if (std::regex_search(str, sm, rex)) {
29 | return sm.position(2) - 1;
30 | }
31 | return std::numeric_limits::max();
32 | }
33 | void operator()(const std::string &str, ASTNode &ast) const override {
34 | std::smatch sm;
35 | std::regex_search(str, sm, rex);
36 | std::string prefix = sm.format("$1");
37 | std::string content = sm.format("$2");
38 | std::string suffix = sm.format("$3");
39 | InlineParser::process(prefix, ast);
40 | InlineItalic node;
41 | InlineParser::process(content, node);
42 | ast.pushback_child(std::make_shared(node));
43 | InlineParser::process(suffix, ast);
44 | }
45 | };
46 |
47 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/InlineMath.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 |
8 | namespace almo {
9 |
10 | struct InlineMath : public ASTNode {
11 | private:
12 | std::string expr;
13 |
14 | public:
15 | InlineMath(std::string _expr) : expr(_expr) { set_uuid(); }
16 |
17 | // mathjax の インライン数式用に \( \) で囲む
18 | std::string to_html() const override {
19 | return " \\( " + expr + " \\) ";
20 | }
21 |
22 | std::map get_properties() const override {
23 | return {{"expr", expr}};
24 | }
25 | std::string get_classname() const override { return "InlineMath"; }
26 | };
27 |
28 | struct InlineMathSyntax : public InlineSyntax {
29 | static inline const std::regex rex = std::regex(R"((.*?)\$(.*?)\$(.*))");
30 | int operator()(const std::string &str) const override {
31 | std::smatch sm;
32 | if (std::regex_search(str, sm, rex)) {
33 | return sm.position(2) - 1;
34 | }
35 | return std::numeric_limits::max();
36 | }
37 | void operator()(const std::string &str, ASTNode &ast) const override {
38 | std::smatch sm;
39 | std::regex_search(str, sm, rex);
40 | std::string prefix = sm.format("$1");
41 | std::string expr = sm.format("$2");
42 | std::string suffix = sm.format("$3");
43 | InlineParser::process(prefix, ast);
44 | InlineMath node(expr);
45 | ast.pushback_child(std::make_shared(node));
46 | InlineParser::process(suffix, ast);
47 | }
48 | };
49 |
50 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/InlineOverline.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 |
7 | namespace almo {
8 |
9 | struct InlineOverline : public ASTNode {
10 | public:
11 | InlineOverline() { set_uuid(); }
12 | std::string to_html() const override {
13 | return " " + concatenated_childs_html() +
14 | " ";
15 | }
16 |
17 | std::map get_properties() const override {
18 | return {};
19 | }
20 | std::string get_classname() const override { return "InlineOverline"; }
21 | };
22 |
23 | struct InlineOverlineSyntax : public InlineSyntax {
24 | static inline const std::regex rex =
25 | std::regex(R"((.*?)\~\~(.*?)\~\~(.*))");
26 | int operator()(const std::string &str) const override {
27 | std::smatch sm;
28 | if (std::regex_search(str, sm, rex)) {
29 | return sm.position(2) - 2;
30 | }
31 | return std::numeric_limits::max();
32 | }
33 | void operator()(const std::string &str, ASTNode &ast) const override {
34 | std::smatch sm;
35 | std::regex_search(str, sm, rex);
36 | std::string prefix = sm.format("$1");
37 | std::string content = sm.format("$2");
38 | std::string suffix = sm.format("$3");
39 | InlineParser::process(prefix, ast);
40 | InlineOverline node;
41 | InlineParser::process(content, node);
42 | ast.pushback_child(std::make_shared(node));
43 | InlineParser::process(suffix, ast);
44 | }
45 | };
46 |
47 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/InlineStrong.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 |
7 | namespace almo {
8 |
9 | struct InlineStrong : public ASTNode {
10 | public:
11 | InlineStrong() { set_uuid(); }
12 |
13 | std::string to_html() const override {
14 | return " " + concatenated_childs_html() +
15 | " ";
16 | }
17 |
18 | std::map get_properties() const override {
19 | return {};
20 | }
21 | std::string get_classname() const override { return "InlineStrong"; }
22 | };
23 |
24 | struct InlineStrongSyntax : public InlineSyntax {
25 | static inline const std::regex rex =
26 | std::regex(R"((.*?)\*\*(.*?)\*\*(.*))");
27 | int operator()(const std::string &str) const override {
28 | std::smatch sm;
29 | if (std::regex_search(str, sm, rex)) {
30 | return sm.position(2) - 2;
31 | }
32 | return std::numeric_limits::max();
33 | }
34 | void operator()(const std::string &str, ASTNode &ast) const override {
35 | std::smatch sm;
36 | std::regex_search(str, sm, rex);
37 | std::string prefix = sm.format("$1");
38 | std::string content = sm.format("$2");
39 | std::string suffix = sm.format("$3");
40 | InlineParser::process(prefix, ast);
41 | InlineStrong node;
42 | InlineParser::process(content, node);
43 | ast.pushback_child(std::make_shared(node));
44 | InlineParser::process(suffix, ast);
45 | }
46 | };
47 |
48 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/InlineUrl.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 |
8 | namespace almo {
9 |
10 | struct InlineUrl : public ASTNode {
11 | private:
12 | std::string url;
13 | std::string alt;
14 |
15 | public:
16 | InlineUrl(std::string url, std::string alt) : url(url), alt(alt) {
17 | set_uuid();
18 | }
19 |
20 | std::string to_html() const override {
21 | return " " + alt + " ";
22 | }
23 |
24 | std::map get_properties() const override {
25 | return {{"url", url}, {"alt", alt}};
26 | }
27 | std::string get_classname() const override { return "InlineUrl"; }
28 | };
29 |
30 | struct InlineUrlSyntax : public InlineSyntax {
31 | static inline const std::regex rex =
32 | std::regex(R"((.*?)\[(.*?)\]\((.*?)\)(.*))");
33 | int operator()(const std::string &str) const override {
34 | std::smatch sm;
35 | if (std::regex_search(str, sm, rex)) {
36 | return sm.position(2) - 1;
37 | }
38 | return std::numeric_limits::max();
39 | }
40 | void operator()(const std::string &str, ASTNode &ast) const override {
41 | std::smatch sm;
42 | std::regex_search(str, sm, rex);
43 | std::string prefix = sm.format("$1");
44 | std::string alt = sm.format("$2");
45 | std::string url = sm.format("$3");
46 | std::string suffix = sm.format("$4");
47 | InlineParser::process(prefix, ast);
48 | InlineUrl node(url, alt);
49 | ast.pushback_child(std::make_shared(node));
50 | InlineParser::process(suffix, ast);
51 | }
52 | };
53 |
54 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/Item.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 |
8 | namespace almo {
9 |
10 | // 箇条書きの要素を表すクラス
11 | struct Item : public ASTNode {
12 | public:
13 | Item() { set_uuid(); }
14 |
15 | std::string to_html() const override {
16 | return "" + concatenated_childs_html() + "";
17 | }
18 |
19 | std::map get_properties() const override {
20 | return {};
21 | }
22 | std::string get_classname() const override { return "Item"; }
23 | };
24 |
25 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/Judge.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 |
8 | namespace almo {
9 |
10 | struct Judge : public ASTNode {
11 | private:
12 | // 問題のタイトル
13 | std::string title;
14 |
15 | // サンプル入力(単一ファイルのパス)
16 | std::string sample_in_path;
17 |
18 | // サンプル出力(単一ファイルのパス)
19 | std::string sample_out_path;
20 |
21 | // ジャッジの種類. 誤差ジャッジ (err_{rate}) または 完全一致ジャッジ (equal)
22 | // が指定される. C++側では特に処理は変わらず、 JS側に `judge_type[uuid]`
23 | // を指定することで伝えれば切り替わる。
24 | std::string judge_type;
25 |
26 | // エディタにデフォルトで入力されてあるコード。
27 | std::string source;
28 |
29 | // 入力ファイルを表す glob パターン
30 | std::string in_files_glob;
31 |
32 | // 出力ファイルを表す glob パターン
33 | std::string out_files_glob;
34 |
35 | // エディタのテーマ
36 | std::string editor_theme;
37 |
38 | public:
39 | Judge(std::string title, std::string sample_in_path,
40 | std::string sample_out_path, std::string in_files_glob,
41 | std::string out_files_glob, std::string judge_type,
42 | std::string source, std::string editor_theme)
43 | : title(title),
44 | sample_in_path(sample_in_path),
45 | sample_out_path(sample_out_path),
46 | in_files_glob(in_files_glob),
47 | out_files_glob(out_files_glob),
48 | judge_type(judge_type),
49 | source(source),
50 | editor_theme(editor_theme) {
51 | // ジャッジが`err_{rate}` または `equal` 以外の場合はエラーを出す.
52 | if (judge_type.substr(0, 4) != "err_" && judge_type != "equal") {
53 | throw ParseError(
54 | "Invalid judge type. Expected `err_{rate}` or `equal`, but `" +
55 | judge_type + "` is given.");
56 | }
57 | set_uuid();
58 | }
59 |
60 | std::string to_html() const override {
61 | std::string uuid_str = get_uuid_str();
62 |
63 | // タイトルを作る
64 | std::string title_h3 =
65 | "
WJ
" + title + " \n";
67 |
68 | // ここから ace editor を作る.
69 | // まず editor を入れる用の div を作る.
70 | std::string editor_div = " \n";
72 |
73 | // ace editor の設定をする.
74 |
75 | std::string source_code = join(read_file(source), "\n");
76 |
77 | std::string ace_editor =
78 | ""
79 | "\n";
102 |
103 | // サンプル入力を読み込む.
104 | std::string sample_in = join(read_file(sample_in_path));
105 | std::string sample_out = join(read_file(sample_out_path));
106 |
107 | // サンプル入力と出力を表示する用の div を作る.
108 | std::string sample_in_area =
109 | " サンプルの入力
"
110 | "" + sample_in + "
\n";
112 |
113 | std::string sample_out_area =
114 | " 出力
"
115 | "\n";
117 |
118 | std::string expect_out_area =
119 | " サンプルの答え
"
120 | "" + sample_out + "
\n";
122 |
123 | // データを定義する。 all_input, all_output, all_sample_input,
124 | // all_sample_output, problem_status は JS側で使うために定義する.
125 | // また、目次生成のために page_contents にも追加する.
126 | std::string define_data =
127 | " \n";
171 |
172 | // テスト実行ボタンと提出ボタンを作る.
173 | std::string test_run_button =
174 | "\n";
176 |
177 | std::string submit_button =
178 | "\n";
180 |
181 | // ジャッジの種類を JS側に伝えるためのコードを作る.
182 | std::string judge_code =
183 | "\n";
188 |
189 | std::string output = title_h3 + editor_div + ace_editor +
190 | sample_in_area + sample_out_area +
191 | expect_out_area + define_data + test_run_button +
192 | submit_button + judge_code;
193 |
194 | return output;
195 | }
196 |
197 | std::map get_properties() const override {
198 | return {{"title", title},
199 | {"sample_in_path", sample_in_path},
200 | {"sample_out_path", sample_out_path},
201 | {"in_files_glob", in_files_glob},
202 | {"out_files_glob", out_files_glob},
203 | {"judge_type", judge_type},
204 | {"source", source}};
205 | }
206 | std::string get_classname() const override { return "Judge"; }
207 | };
208 |
209 | struct JudgeSyntax : BlockSyntax {
210 | bool operator()(Reader &read) const override {
211 | if (!read.is_line_begin()) return false;
212 | if (rtrim(read.get_row()) == ":::judge") return true;
213 | return false;
214 | }
215 | void operator()(Reader &read, ASTNode &ast) const override {
216 | // BEGIN : ':::judge\s*'
217 | // END : ':::\s*'
218 | std::map judge_info;
219 |
220 | const std::vector required_args = {
221 | "title", "sample_in", "sample_out", "in", "out",
222 | };
223 |
224 | std::map optional_args = {
225 | {"judge", "equal"},
226 | {"source", ""},
227 | };
228 |
229 | // skip :::judge
230 | read.move_next_line();
231 |
232 | while (!read.is_eof()) {
233 | if (rtrim(read.get_row()) == ":::") {
234 | read.move_next_line();
235 | break;
236 | }
237 | std::size_t mid = read.get_row().find("=");
238 | if (mid == std::string::npos) {
239 | throw SyntaxError("judge option must be separated with '='");
240 | }
241 | std::string key = rtrim(read.get_row().substr(0u, mid));
242 | std::string value = rtrim(ltrim(read.get_row().substr(mid + 1)));
243 | if (judge_info.contains(key)) {
244 | throw SyntaxError("Duplicate judge options : " + key);
245 | }
246 | judge_info[key] = value;
247 | read.move_next_line();
248 | }
249 |
250 | // required args check
251 | for (std::string arg : required_args) {
252 | if (judge_info.contains(arg)) continue;
253 | // lost args
254 | // set title
255 | if (judge_info.contains("title")) {
256 | throw SyntaxError("問題" + judge_info["title"] + "の引数 " +
257 | arg +
258 | " がありません. 引数を追加してください.");
259 | } else {
260 | throw SyntaxError(
261 | "問題タイトルがありません. 引数を追加してください.");
262 | }
263 | }
264 |
265 | // optional args check
266 | for (auto [arg, default_value] : optional_args) {
267 | if (judge_info.contains(arg)) continue;
268 | judge_info[arg] = default_value;
269 | }
270 |
271 | Judge node(judge_info["title"], judge_info["sample_in"],
272 | judge_info["sample_out"], judge_info["in"],
273 | judge_info["out"], judge_info["judge"], judge_info["source"],
274 | "{{ editor_theme }}");
275 |
276 | ast.pushback_child(std::make_shared(node));
277 | }
278 | };
279 |
280 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/ListBlock.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 | #include "EOF.hpp"
8 | #include "Item.hpp"
9 | #include "NewLine.hpp"
10 |
11 | namespace almo {
12 |
13 | // 箇条書き(番号なし) を表すクラス
14 | struct ListBlock : public ASTNode {
15 | public:
16 | ListBlock() { set_uuid(); }
17 |
18 | std::string to_html() const override {
19 | return "" + concatenated_childs_html() + "
";
20 | }
21 |
22 | std::map get_properties() const override {
23 | return {};
24 | }
25 | std::string get_classname() const override { return "ListBlock"; }
26 | };
27 |
28 | struct ListBlockSyntax : public BlockSyntax {
29 | bool operator()(Reader &read) const override {
30 | if (!read.is_line_begin()) return false;
31 | return read.get_row().starts_with("- ");
32 | }
33 | void operator()(Reader &read, ASTNode &ast) const override {
34 | // BEGIN : '- .*'
35 | // END : NewLine or EOF
36 |
37 | std::stack> scopes;
38 | ListBlock root_list;
39 | scopes.push(std::make_shared(root_list));
40 |
41 | std::string current_prefix = "- ";
42 | const std::string INDENT = " ";
43 |
44 | // if s is considered a list definition, return its depth"
45 | // otherwise return std::string::npos
46 | auto list_depth = [](std::string s) -> std::size_t {
47 | std::size_t pos = s.find_first_not_of(" \n\r\t");
48 | // s is consist of only space
49 | if (pos == std::string::npos) return std::string::npos;
50 | // s is list definition
51 | if (s.substr(pos).starts_with("- ")) return pos;
52 | // otherwise
53 | return std::string::npos;
54 | };
55 |
56 | std::string text = "";
57 |
58 | while (true) {
59 | // Invariant : read.is_eof() == false
60 | // scopes.size() >= 1
61 | text += remove_listdef(ltrim(read.get_row()));
62 |
63 | // within while loop, read changes only here
64 | read.move_next_line();
65 |
66 | // END
67 | if (EOFSyntax{}(read) || NewLineSyntax{}(read)) {
68 | Item item;
69 | InlineParser::process(text, item);
70 | scopes.top()->pushback_child(std::make_shared- (item));
71 | text = "";
72 | break;
73 | }
74 | // same depth
75 | if (read.get_row().starts_with(current_prefix)) {
76 | Item item;
77 | InlineParser::process(text, item);
78 | scopes.top()->pushback_child(std::make_shared
- (item));
79 | text = "";
80 | continue;
81 | }
82 | // +2 depth
83 | if (read.get_row().starts_with(INDENT + current_prefix)) {
84 | Item item;
85 | InlineParser::process(text, item);
86 | scopes.top()->pushback_child(std::make_shared
- (item));
87 | text = "";
88 |
89 | // nested ListBlock
90 | ListBlock new_node;
91 | auto new_node_ptr = std::make_shared(new_node);
92 | scopes.top()->pushback_child(new_node_ptr);
93 | scopes.push(new_node_ptr);
94 |
95 | // update prefix
96 | current_prefix = INDENT + current_prefix;
97 | continue;
98 | }
99 | // -2n depth (n >= 1)
100 | if ([&] {
101 | std::size_t depth = list_depth(read.get_row());
102 | if (depth == std::string::npos) return false;
103 | std::size_t current = (scopes.size() - 1u) * 2u;
104 | // depth == current, depth == current + 2 is already
105 | // ditected depth has decreased and the difference is even
106 | if (depth < current && (current - depth) % 2 == 0)
107 | return true;
108 | // ambiguous list definition detected
109 | std::cerr << "Warning: ambiguous list\n ... \n"
110 | << read.near() << "\n^^^ parsing line"
111 | << std::endl;
112 | std::cerr << "indent width must be 2. this line is "
113 | "considered as raw text."
114 | << std::endl;
115 | return false;
116 | }()) {
117 | std::size_t delete_indent =
118 | (scopes.size() - 1u) * 2u - list_depth(read.get_row());
119 | current_prefix = current_prefix.substr(delete_indent);
120 |
121 | Item item;
122 | InlineParser::process(text, item);
123 | scopes.top()->pushback_child(std::make_shared
- (item));
124 | text = "";
125 |
126 | for (std::size_t del = 0; del < delete_indent; del += 2u) {
127 | scopes.pop();
128 | }
129 | continue;
130 | }
131 | // Ensure : read.get_row() is considered a continuation of the
132 | // previous item
133 | }
134 | while (scopes.size() > 1u) {
135 | scopes.pop();
136 | }
137 | ast.pushback_child(scopes.top());
138 | }
139 | };
140 |
141 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/LoadLib.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 |
8 | namespace almo {
9 |
10 | // ライブラリの読み込みをする独自記法.
11 | struct LoadLib : public ASTNode {
12 | private:
13 | // 読み込むライブラリの名前のリスト
14 | std::vector libs;
15 |
16 | public:
17 | LoadLib(std::vector libs) : libs(libs) { set_uuid(); }
18 |
19 | // use_libs に追加しておくと JS側で読み込み処理を行う。
20 | std::string to_html() const override {
21 | std::string output = "";
22 | for (std::string lib : libs) {
23 | output += "";
24 | }
25 | return output;
26 | };
27 |
28 | std::map get_properties() const override {
29 | return {{"libs", join(libs)}};
30 | }
31 | std::string get_classname() const override { return "LoadLib"; }
32 | };
33 |
34 | struct LoadLibSyntax : public BlockSyntax {
35 | bool operator()(Reader &read) const override {
36 | if (!read.is_line_begin()) return false;
37 | if (rtrim(read.get_row()) == ":::loadlib") return true;
38 | return false;
39 | }
40 | void operator()(Reader &read, ASTNode &ast) const override {
41 | std::vector libs;
42 |
43 | // skip :::loadlib
44 | read.move_next_line();
45 |
46 | if (read.is_eof()) {
47 | throw SyntaxError("Empty Library");
48 | }
49 |
50 | while (!read.is_eof()) {
51 | if (rtrim(read.get_row()) == ":::") {
52 | read.move_next_line();
53 | break;
54 | }
55 | libs.push_back(rtrim(read.get_row()));
56 | read.move_next_line();
57 | }
58 |
59 | LoadLib node(libs);
60 | ast.pushback_child(std::make_shared(node));
61 | }
62 | };
63 |
64 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/Markdown.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 |
7 | namespace almo {
8 |
9 | struct Markdown : public ASTNode {
10 | public:
11 | Markdown() { set_uuid(); }
12 |
13 | std::string to_html() const override { return concatenated_childs_html(); }
14 |
15 | std::map get_properties() const override {
16 | return {};
17 | }
18 | std::string get_classname() const override { return "Markdown"; }
19 |
20 | std::string to_dot() const {
21 | std::string childs_dot = concatenated_childs_dot();
22 | std::string dot =
23 | "digraph G {\n graph [labelloc=\"t\"; \n ]\n" + childs_dot + "}";
24 | return dot;
25 | }
26 | };
27 |
28 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/MathBlock.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 |
8 | namespace almo {
9 |
10 | // 数式ブロック. 内容はMathJaxでレンダリングされる. 内容は `math-block`
11 | // というクラスが付与されたdivタグで囲まれる
12 | struct MathBlock : public ASTNode {
13 | private:
14 | // markdownで渡された式をそのまま string で持つ。
15 | std::string expression;
16 |
17 | public:
18 | MathBlock(std::string expression) : expression(expression) { set_uuid(); }
19 |
20 | // mathjax の 複数行数式用に \[ \] で囲む
21 | std::string to_html() const override {
22 | return "
\\[ \n" + expression +
23 | "\n \\]
";
24 | }
25 |
26 | std::map get_properties() const override {
27 | return {{"expression", expression}};
28 | }
29 | std::string get_classname() const override { return "MathBlock"; }
30 | };
31 |
32 | struct MathBlockSyntax : public BlockSyntax {
33 | bool operator()(Reader &read) const override {
34 | if (!read.is_line_begin()) return false;
35 | return read.get_row().starts_with("$$");
36 | }
37 | void operator()(Reader &read, ASTNode &ast) const override {
38 | // BEGIN : '$$.*'
39 | // END : '.*$$\s*'
40 | // note : The mathematical rendering library is responsible for
41 | // the treatment of newlines and whitespace.
42 |
43 | std::string expression = "";
44 |
45 | read.move_next_char(2);
46 |
47 | while (!read.is_eof()) {
48 | if (rtrim(read.get_rest_row()).ends_with("$$")) {
49 | std::string tail = rtrim(read.get_rest_row());
50 | expression += tail.substr(0, tail.size() - 2u);
51 | read.move_next_line();
52 | break;
53 | }
54 | expression += read.get_rest_row() + "\n";
55 | read.move_next_line();
56 | }
57 |
58 | MathBlock node(expression);
59 | ast.pushback_child(std::make_shared(node));
60 | }
61 | };
62 |
63 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/NewLine.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 |
8 | namespace almo {
9 |
10 | struct NewLine : public ASTNode {
11 | public:
12 | NewLine() { set_uuid(); }
13 |
14 | std::string to_html() const override { return "
"; }
15 |
16 | std::map get_properties() const override {
17 | return {};
18 | }
19 | std::string get_classname() const override { return "NewLine"; }
20 | };
21 |
22 | struct NewLineSyntax : public BlockSyntax {
23 | bool operator()(Reader &read) const override {
24 | if (!read.is_line_begin()) return false;
25 | if (rtrim(read.get_row()) == "") return true;
26 | return false;
27 | }
28 | void operator()(Reader &read, ASTNode &ast) const override {
29 | NewLine node;
30 | ast.pushback_child(std::make_shared(node));
31 | read.move_next_line();
32 | }
33 | };
34 |
35 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/Quote.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 | #include "Markdown.hpp"
8 |
9 | namespace almo {
10 |
11 | // 引用を表すクラス
12 | struct Quote : public ASTNode {
13 | public:
14 | Quote() { set_uuid(); }
15 |
16 | std::string to_html() const override {
17 | return "" + concatenated_childs_html() + "
";
18 | }
19 |
20 | std::map get_properties() const override {
21 | return {};
22 | }
23 | std::string get_classname() const override { return "Quote"; }
24 | };
25 |
26 | struct QuoteSyntax : public BlockSyntax {
27 | bool operator()(Reader &read) const override {
28 | if (!read.is_line_begin()) return false;
29 | return read.get_row().starts_with("> ");
30 | }
31 | void operator()(Reader &read, ASTNode &ast) const override {
32 | // BEGIN : '> .*'
33 | // END : '(?!> ).*'
34 |
35 | std::vector quote_contents;
36 | while (!read.is_eof()) {
37 | if (read.get_row().starts_with("> ")) {
38 | quote_contents.emplace_back(read.get_row().substr(2));
39 | read.move_next_line();
40 | continue;
41 | }
42 | break;
43 | }
44 |
45 | // uuid order is node --> inner_md
46 | Quote node;
47 | Markdown inner_md;
48 | MarkdownParser parser(quote_contents);
49 | parser.process(inner_md);
50 | node.pushback_child(std::make_shared(inner_md));
51 | ast.pushback_child(std::make_shared(node));
52 | }
53 | };
54 |
55 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/RawText.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "../interfaces/ast.hpp"
6 | #include "../interfaces/parse.hpp"
7 | #include "../interfaces/syntax.hpp"
8 |
9 | namespace almo {
10 |
11 | struct RawText : public ASTNode {
12 | private:
13 | std::string content;
14 |
15 | public:
16 | RawText(std::string _content) : content(_content) { set_uuid(); }
17 |
18 | std::string to_html() const override { return content; }
19 |
20 | std::map get_properties() const override {
21 | return {{"content", content}};
22 | }
23 | std::string get_classname() const override { return "RawText"; }
24 | };
25 |
26 | struct RawTextSyntax : InlineSyntax {
27 | // All string matches Rawtext syntax
28 | // but matches infinity position
29 | // because Rawtext syntax is weakest.
30 | // If the string matches other Inline syntax
31 | // RawText does not match it.
32 | int operator()(const std::string &str) const override {
33 | return std::numeric_limits::max();
34 | }
35 | void operator()(const std::string &str, ASTNode &ast) const override {
36 | RawText node(str);
37 | ast.pushback_child(std::make_shared(node));
38 | }
39 | };
40 |
41 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax/Table.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "../interfaces/ast.hpp"
4 | #include "../interfaces/parse.hpp"
5 | #include "../interfaces/syntax.hpp"
6 | #include "../utils.hpp"
7 | #include "EOF.hpp"
8 | #include "Markdown.hpp"
9 | #include "NewLine.hpp"
10 |
11 | namespace almo {
12 |
13 | struct Table : public ASTNode {
14 | private:
15 | std::vector> columns;
16 | int n_row;
17 | int n_col;
18 |
19 | // 各列の名前をインライン記法のパースをしていない素の文字列として持つ.
20 | std::vector col_names;
21 | // 各列について、左寄せ(0), 中央寄せ(1), 右寄せ(2) のいずれかを指定する.
22 | std::vector col_format;
23 |
24 | public:
25 | Table(std::vector> columns, int n_row, int n_col,
26 | std::vector col_names, std::vector col_format)
27 | : columns(columns),
28 | n_row(n_row),
29 | n_col(n_col),
30 | col_names(col_names),
31 | col_format(col_format) {
32 | set_uuid();
33 | }
34 |
35 | // テーブル用の特別な to_html. テーブルの to_html
36 | // でしか呼ばれないので引数が違ってもOK.
37 | std::string to_html(std::vector headers_html,
38 | std::vector childs_html) const {
39 | std::string output = "\n";
40 | output += "\n";
41 | output += "\n";
42 | for (int i = 0; i < n_col; i++) {
43 | std::string align = col_format[i] == 0 ? "left"
44 | : col_format[i] == 1 ? "center"
45 | : "right";
46 | output +=
47 | "" + headers_html[i] + " | \n";
48 | }
49 | output += "
\n";
50 | output += "\n";
51 | output += "\n";
52 | for (int i = 0; i < n_row; i++) {
53 | output += "\n";
54 | for (int j = 0; j < n_col; j++) {
55 | std::string align = col_format[j] == 0 ? "left"
56 | : col_format[j] == 1 ? "center"
57 | : "right";
58 | output += "" +
59 | childs_html[i * n_col + j] + " | \n";
60 | }
61 | output += "
\n";
62 | }
63 | output += "\n";
64 | output += "
\n";
65 | return output;
66 | }
67 |
68 | std::string to_html() const override {
69 | std::vector columns_html;
70 | for (auto child : columns) {
71 | columns_html.push_back(child->to_html());
72 | }
73 |
74 | std::vector contents_html;
75 |
76 | for (auto child : childs) {
77 | contents_html.push_back(child->to_html());
78 | }
79 |
80 | return to_html(columns_html, contents_html);
81 | }
82 |
83 | std::map get_properties() const override {
84 | std::string col_format_str = "";
85 | for (int i = 0; i < n_col; i++) {
86 | std::string align = col_format[i] == 0 ? "l"
87 | : col_format[i] == 1 ? "c"
88 | : "r";
89 | col_format_str += align;
90 | }
91 |
92 | std::string col_names_str = "[";
93 |
94 | for (int i = 0; i < n_col; i++) {
95 | col_names_str += "\"" + col_names[i] + "\"";
96 | if (i != n_col - 1) {
97 | col_names_str += ", ";
98 | }
99 | }
100 |
101 | col_names_str += "]";
102 |
103 | return {{"n_row", std::to_string(n_row)},
104 | {"n_col", std::to_string(n_col)},
105 | {"col_format", col_format_str},
106 | {"col_names", col_names_str}};
107 | }
108 | std::string get_classname() const override { return "Table"; }
109 | };
110 |
111 | struct TableSyntax : public BlockSyntax {
112 | bool operator()(Reader &read) const override {
113 | if (!read.is_line_begin()) return false;
114 | if (std::regex_match(rtrim(read.get_row()),
115 | std::regex(R"((\|[^\|]+).+\|)")))
116 | return true;
117 | return false;
118 | }
119 | void operator()(Reader &read, ASTNode &ast) const override {
120 | // BEGIN : R"((\|[^\|]+).+\|)"
121 | // END : EOF or NewLine
122 | const std::regex each_col_regex(R"(\|[^\|]+)");
123 |
124 | int n_col = 0;
125 | std::vector col_names;
126 | std::smatch sm;
127 |
128 | std::string line = read.get_row();
129 |
130 | while (std::regex_search(line, sm, each_col_regex)) {
131 | col_names.push_back(sm[0].str().substr(1));
132 | line = sm.suffix();
133 | n_col++;
134 | }
135 |
136 | // 0 --> left (default), 1 --> center, 2 --> right
137 | std::vector col_format;
138 | std::regex Lrex(R"(\|\s*:-+\s*)");
139 | std::regex Crex(R"(\|\s*:-+:\s*)");
140 | std::regex Rrex(R"(\|\s*-+:\s*)");
141 |
142 | read.move_next_line();
143 |
144 | line = read.get_row();
145 |
146 | while (std::regex_search(line, sm, each_col_regex)) {
147 | if (std::regex_match(sm[0].str(), Lrex)) {
148 | col_format.emplace_back(0);
149 | } else if (std::regex_match(sm[0].str(), Crex)) {
150 | col_format.emplace_back(1);
151 | } else if (std::regex_match(sm[0].str(), Rrex)) {
152 | col_format.emplace_back(2);
153 | } else {
154 | col_format.emplace_back(0);
155 | }
156 |
157 | line = sm.suffix();
158 | }
159 |
160 | if (col_names.size() != col_format.size()) {
161 | throw SyntaxError("Number of columns of Table must be same");
162 | }
163 |
164 | read.move_next_line();
165 |
166 | int n_row = 0;
167 | std::vector table;
168 |
169 | while (true) {
170 | if (EOFSyntax{}(read) || NewLineSyntax{}(read)) break;
171 | n_row++;
172 | line = read.get_row();
173 | while (std::regex_search(line, sm, each_col_regex)) {
174 | table.push_back(sm[0].str().substr(1));
175 | line = sm.suffix();
176 | }
177 | read.move_next_line();
178 | }
179 |
180 | std::vector> columns_markdowns;
181 |
182 | for (int i = 0; i < n_col; i++) {
183 | Markdown inner_md;
184 | InlineParser::process(col_names[i], inner_md);
185 | columns_markdowns.push_back(std::make_shared(inner_md));
186 | }
187 |
188 | Table node(columns_markdowns, n_row, n_col, col_names, col_format);
189 |
190 | for (auto cell : table) {
191 | Markdown inner_md;
192 | InlineParser::process(cell, inner_md);
193 | node.pushback_child(std::make_shared(inner_md));
194 | }
195 |
196 | ast.pushback_child(std::make_shared(node));
197 | }
198 | };
199 |
200 | } // namespace almo
--------------------------------------------------------------------------------
/src/syntax_all.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "syntax/CodeBlock.hpp"
4 | #include "syntax/DivBlock.hpp"
5 | #include "syntax/EOF.hpp"
6 | #include "syntax/EnumerateBlock.hpp"
7 | #include "syntax/ExecutableCodeBlock.hpp"
8 | #include "syntax/Header.hpp"
9 | #include "syntax/HorizontalLine.hpp"
10 | #include "syntax/InlineCodeBlock.hpp"
11 | #include "syntax/InlineImage.hpp"
12 | #include "syntax/InlineItalic.hpp"
13 | #include "syntax/InlineMath.hpp"
14 | #include "syntax/InlineOverline.hpp"
15 | #include "syntax/InlineStrong.hpp"
16 | #include "syntax/InlineUrl.hpp"
17 | #include "syntax/Item.hpp"
18 | #include "syntax/Judge.hpp"
19 | #include "syntax/ListBlock.hpp"
20 | #include "syntax/LoadLib.hpp"
21 | #include "syntax/Markdown.hpp"
22 | #include "syntax/MathBlock.hpp"
23 | #include "syntax/NewLine.hpp"
24 | #include "syntax/Quote.hpp"
25 | #include "syntax/RawText.hpp"
26 | #include "syntax/Table.hpp"
27 |
28 | #include "syntax/InlineFootnoteReference.hpp"
29 | #include "syntax/FootnoteDefinition.hpp"
--------------------------------------------------------------------------------
/src/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{title}}
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
40 |
41 |
42 | {{style}}
43 |
44 |
45 |
48 |
49 | {{runner}}
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |

62 |
63 |
64 |
65 |
66 | {{title}}
67 |
68 |
69 |
72 |
73 |
74 |
75 | {{date}}
76 |
77 |
78 |
79 | {{tag}}
80 |
81 |
82 |
83 |
84 |
85 | {{contents}}
86 |
87 |
88 |
89 |
90 |
91 |
172 |
173 |
174 |
175 |
176 |
--------------------------------------------------------------------------------
/src/utils.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include