Skip to content

Commit 6944c16

Browse files
authored
Merge pull request #1 from vnotex/dev
[vnotex] support display formula
2 parents de809d9 + 4bf3e1e commit 6944c16

12 files changed

Lines changed: 17806 additions & 6930 deletions

File tree

parser_test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ set(TEST_TARGETS
55
block_quote_tests
66
mark_tests
77
formula_inline_tests
8+
formula_block_tests
89
code_tests)
910

1011
foreach(TARGET ${TEST_TARGETS})

parser_test/formula_block_tests.c

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#include <cmark.h>
2+
3+
#include "test_utils.h"
4+
5+
int test_formula_block_simple() {
6+
return test_xml("$$\nE=mc^2\n$$",
7+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
8+
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
9+
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
10+
" <formula_block xml:space=\"preserve\">E=mc^2\n</formula_block>\n"
11+
"</document>\n",
12+
CMARK_OPT_DEFAULT);
13+
}
14+
15+
int test_formula_block_raw() {
16+
return test_xml("\\begin{equa}\nE=mc^2\n\\end{equa}",
17+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
18+
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
19+
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
20+
" <formula_block xml:space=\"preserve\">E=mc^2\n</formula_block>\n"
21+
"</document>\n",
22+
CMARK_OPT_DEFAULT);
23+
}
24+
25+
int test_formula_block_multiple() {
26+
return test_xml("$$\na+b\n$$\n\n$$\nc+d\n$$",
27+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
28+
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
29+
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
30+
" <formula_block xml:space=\"preserve\">a+b\n</formula_block>\n"
31+
" <formula_block xml:space=\"preserve\">c+d\n</formula_block>\n"
32+
"</document>\n",
33+
CMARK_OPT_DEFAULT);
34+
}
35+
36+
int test_formula_block_with_escape() {
37+
return test_xml("$$\na\\$\\$b\n$$",
38+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
39+
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
40+
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
41+
" <formula_block xml:space=\"preserve\">a\\$\\$b\n</formula_block>\n"
42+
"</document>\n",
43+
CMARK_OPT_DEFAULT);
44+
}
45+
46+
int test_formula_block_not_closed() {
47+
return test_xml("$$\nformula",
48+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
49+
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
50+
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
51+
" <formula_block xml:space=\"preserve\">formula\n</formula_block>\n"
52+
"</document>\n",
53+
CMARK_OPT_DEFAULT);
54+
}
55+
56+
int test_formula_block_empty() {
57+
return test_xml("$$\n$$",
58+
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
59+
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
60+
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
61+
" <formula_block xml:space=\"preserve\"></formula_block>\n"
62+
"</document>\n",
63+
CMARK_OPT_DEFAULT);
64+
}
65+
66+
int main() {
67+
CASE(test_formula_block_simple);
68+
CASE(test_formula_block_raw);
69+
CASE(test_formula_block_multiple);
70+
CASE(test_formula_block_with_escape);
71+
CASE(test_formula_block_not_closed);
72+
CASE(test_formula_block_empty);
73+
return 0;
74+
}

parser_test/formula_inline_tests.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ int test_formula_inline_not_closed() {
5353
}
5454

5555
int test_formula_inline_empty() {
56-
return test_xml("$$",
56+
return test_xml("abc $$",
5757
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
5858
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"
5959
"<document xmlns=\"http://commonmark.org/xml/1.0\">\n"
6060
" <paragraph>\n"
61-
" <text xml:space=\"preserve\">$$</text>\n"
61+
" <text xml:space=\"preserve\">abc $$</text>\n"
6262
" </paragraph>\n"
6363
"</document>\n",
6464
CMARK_OPT_DEFAULT);
@@ -71,4 +71,4 @@ int main() {
7171
CASE(test_formula_inline_not_closed);
7272
CASE(test_formula_inline_empty);
7373
return 0;
74-
}
74+
}

src/blocks.c

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ static inline bool can_contain(cmark_node_type parent_type,
171171
static inline bool accepts_lines(cmark_node_type block_type) {
172172
return (block_type == CMARK_NODE_PARAGRAPH ||
173173
block_type == CMARK_NODE_HEADING ||
174-
block_type == CMARK_NODE_CODE_BLOCK);
174+
block_type == CMARK_NODE_CODE_BLOCK ||
175+
block_type == CMARK_NODE_FORMULA_BLOCK);
175176
}
176177

177178
static inline bool contains_inlines(cmark_node_type block_type) {
@@ -270,6 +271,7 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
270271
b->end_column = parser->last_line_length;
271272
} else if (S_type(b) == CMARK_NODE_DOCUMENT ||
272273
(S_type(b) == CMARK_NODE_CODE_BLOCK && b->as.code.fenced) ||
274+
S_type(b) == CMARK_NODE_FORMULA_BLOCK ||
273275
(S_type(b) == CMARK_NODE_HEADING && b->as.heading.setext)) {
274276
b->end_line = parser->line_number;
275277
b->end_column = parser->curline.size;
@@ -330,6 +332,11 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
330332
b->data = cmark_strbuf_detach(node_content);
331333
break;
332334

335+
case CMARK_NODE_FORMULA_BLOCK:
336+
b->len = node_content->size;
337+
b->data = cmark_strbuf_detach(node_content);
338+
break;
339+
333340
case CMARK_NODE_HEADING:
334341
case CMARK_NODE_HTML_BLOCK:
335342
b->len = node_content->size;
@@ -859,6 +866,37 @@ static bool parse_code_block_prefix(cmark_parser *parser, cmark_chunk *input,
859866
return res;
860867
}
861868

869+
static bool parse_formula_block_prefix(cmark_parser *parser, cmark_chunk *input,
870+
cmark_node *container, bool *should_continue) {
871+
bool res = false;
872+
bufsize_t matched = 0;
873+
874+
if (parser->indent <= 3) {
875+
matched = scan_formula_block_end(input, parser->first_nonspace);
876+
}
877+
878+
if (matched &&
879+
(matched == container->as.formula.fence_length ||
880+
matched == container->as.formula.fence_length - 2)) {
881+
// closing fence - and since we're at
882+
// the end of a line, we can stop processing it:
883+
*should_continue = false;
884+
S_advance_offset(parser, input, matched, false);
885+
parser->current = finalize(parser, container);
886+
} else {
887+
// skip opt. spaces of fence parser->offset
888+
int i = container->as.formula.fence_offset;
889+
890+
while (i > 0 && S_is_space_or_tab(peek_at(input, parser->offset))) {
891+
S_advance_offset(parser, input, 1, true);
892+
i--;
893+
}
894+
res = true;
895+
}
896+
897+
return res;
898+
}
899+
862900
static bool parse_html_block_prefix(cmark_parser *parser,
863901
cmark_node *container) {
864902
bool res = false;
@@ -924,6 +962,7 @@ static cmark_node *check_open_blocks(cmark_parser *parser, cmark_chunk *input,
924962
// first blank line was processed. Certain block types accept
925963
// empty lines as content, so add them here.
926964
if (parser->current->type == CMARK_NODE_CODE_BLOCK ||
965+
parser->current->type == CMARK_NODE_FORMULA_BLOCK ||
927966
parser->current->type == CMARK_NODE_HTML_BLOCK) {
928967
add_line(input, parser);
929968
}
@@ -942,6 +981,10 @@ static cmark_node *check_open_blocks(cmark_parser *parser, cmark_chunk *input,
942981
if (!parse_code_block_prefix(parser, input, container, &should_continue))
943982
goto done;
944983
break;
984+
case CMARK_NODE_FORMULA_BLOCK:
985+
if (!parse_formula_block_prefix(parser, input, container, &should_continue))
986+
goto done;
987+
break;
945988
case CMARK_NODE_HEADING:
946989
// a heading can never contain more than one line
947990
goto done;
@@ -986,6 +1029,7 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
9861029
int save_column;
9871030

9881031
while (cont_type != CMARK_NODE_CODE_BLOCK &&
1032+
cont_type != CMARK_NODE_FORMULA_BLOCK &&
9891033
cont_type != CMARK_NODE_HTML_BLOCK) {
9901034

9911035
S_find_first_nonspace(parser, input);
@@ -1041,6 +1085,17 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
10411085
parser->first_nonspace + matched - parser->offset,
10421086
false);
10431087

1088+
} else if (!indented && (matched = scan_formula_block_start(
1089+
input, parser->first_nonspace))) {
1090+
*container = add_child(parser, *container, CMARK_NODE_FORMULA_BLOCK,
1091+
parser->first_nonspace + 1);
1092+
(*container)->as.formula.fence_length = matched;
1093+
(*container)->as.formula.fence_offset =
1094+
(int8_t)(parser->first_nonspace - parser->offset);
1095+
S_advance_offset(parser, input,
1096+
parser->first_nonspace + matched - parser->offset,
1097+
false);
1098+
10441099
} else if (!indented && ((matched = scan_html_block_start(
10451100
input, parser->first_nonspace)) ||
10461101
(cont_type != CMARK_NODE_PARAGRAPH &&
@@ -1175,6 +1230,7 @@ static void add_text_to_container(cmark_parser *parser, cmark_node *container,
11751230
const bool last_line_blank =
11761231
(parser->blank && ctype != CMARK_NODE_BLOCK_QUOTE &&
11771232
ctype != CMARK_NODE_HEADING && ctype != CMARK_NODE_THEMATIC_BREAK &&
1233+
ctype != CMARK_NODE_FORMULA_BLOCK &&
11781234
!(ctype == CMARK_NODE_CODE_BLOCK && container->as.code.fenced) &&
11791235
!(ctype == CMARK_NODE_ITEM && container->first_child == NULL &&
11801236
container->start_line == parser->line_number));
@@ -1204,7 +1260,8 @@ static void add_text_to_container(cmark_parser *parser, cmark_node *container,
12041260
assert(parser->current != NULL);
12051261
}
12061262

1207-
if (S_type(container) == CMARK_NODE_CODE_BLOCK) {
1263+
if (S_type(container) == CMARK_NODE_CODE_BLOCK ||
1264+
S_type(container) == CMARK_NODE_FORMULA_BLOCK) {
12081265
add_line(input, parser);
12091266
} else if (S_type(container) == CMARK_NODE_HTML_BLOCK) {
12101267
add_line(input, parser);

src/cmark.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,10 @@ typedef enum {
4646
CMARK_NODE_PARAGRAPH,
4747
CMARK_NODE_HEADING,
4848
CMARK_NODE_THEMATIC_BREAK,
49+
CMARK_NODE_FORMULA_BLOCK,
4950

5051
CMARK_NODE_FIRST_BLOCK = CMARK_NODE_DOCUMENT,
51-
CMARK_NODE_LAST_BLOCK = CMARK_NODE_THEMATIC_BREAK,
52+
CMARK_NODE_LAST_BLOCK = CMARK_NODE_FORMULA_BLOCK,
5253

5354
/* Inline */
5455
CMARK_NODE_TEXT,
@@ -207,11 +208,13 @@ CMARK_EXPORT cmark_node *cmark_node_last_child(cmark_node *node);
207208
* * CMARK_NODE_HTML_BLOCK
208209
* * CMARK_NODE_THEMATIC_BREAK
209210
* * CMARK_NODE_CODE_BLOCK
211+
* * CMARK_NODE_FORMULA_BLOCK
210212
* * CMARK_NODE_TEXT
211213
* * CMARK_NODE_SOFTBREAK
212214
* * CMARK_NODE_LINEBREAK
213215
* * CMARK_NODE_CODE
214216
* * CMARK_NODE_HTML_INLINE
217+
* * CMARK_NODE_FORMULA_INLINE
215218
*
216219
* Nodes must only be modified after an `EXIT` event, or an `ENTER` event for
217220
* leaf nodes.
@@ -677,6 +680,7 @@ const char *cmark_version_string(void);
677680
#define NODE_LIST CMARK_NODE_LIST
678681
#define NODE_ITEM CMARK_NODE_ITEM
679682
#define NODE_CODE_BLOCK CMARK_NODE_CODE_BLOCK
683+
#define NODE_FORMULA_BLOCK CMARK_NODE_FORMULA_BLOCK
680684
#define NODE_HTML_BLOCK CMARK_NODE_HTML_BLOCK
681685
#define NODE_CUSTOM_BLOCK CMARK_NODE_CUSTOM_BLOCK
682686
#define NODE_PARAGRAPH CMARK_NODE_PARAGRAPH

src/iterator.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
static const int S_leaf_mask =
1010
(1 << CMARK_NODE_HTML_BLOCK) | (1 << CMARK_NODE_THEMATIC_BREAK) |
1111
(1 << CMARK_NODE_CODE_BLOCK) | (1 << CMARK_NODE_TEXT) |
12+
(1 << CMARK_NODE_FORMULA_BLOCK) | (1 << CMARK_NODE_FORMULA_INLINE) |
1213
(1 << CMARK_NODE_SOFTBREAK) | (1 << CMARK_NODE_LINEBREAK) |
1314
(1 << CMARK_NODE_CODE) | (1 << CMARK_NODE_HTML_INLINE);
1415

src/node.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ bool cmark_node_is_leaf(cmark_node *node) {
2929
switch (node->type) {
3030
case CMARK_NODE_THEMATIC_BREAK: return true;
3131
case CMARK_NODE_CODE_BLOCK : return true;
32+
case CMARK_NODE_FORMULA_BLOCK : return true;
3233
case CMARK_NODE_TEXT : return true;
3334
case CMARK_NODE_SOFTBREAK : return true;
3435
case CMARK_NODE_LINEBREAK : return true;
@@ -135,6 +136,7 @@ static void S_free_nodes(cmark_node *e) {
135136
case CMARK_NODE_CODE:
136137
case CMARK_NODE_HTML_BLOCK:
137138
case CMARK_NODE_FORMULA_INLINE:
139+
case CMARK_NODE_FORMULA_BLOCK:
138140
mem->free(e->data);
139141
break;
140142
case CMARK_NODE_LINK:
@@ -225,6 +227,8 @@ const char *cmark_node_get_type_string(cmark_node *node) {
225227
return "mark";
226228
case CMARK_NODE_FORMULA_INLINE:
227229
return "formula_inline";
230+
case CMARK_NODE_FORMULA_BLOCK:
231+
return "formula_block";
228232
case CMARK_NODE_LINK:
229233
return "link";
230234
case CMARK_NODE_IMAGE:
@@ -322,6 +326,7 @@ const char *cmark_node_get_literal(cmark_node *node) {
322326
case CMARK_NODE_CODE:
323327
case CMARK_NODE_CODE_BLOCK:
324328
case CMARK_NODE_FORMULA_INLINE:
329+
case CMARK_NODE_FORMULA_BLOCK:
325330
return node->data ? (char *)node->data : "";
326331

327332
default:
@@ -343,6 +348,7 @@ int cmark_node_set_literal(cmark_node *node, const char *content) {
343348
case CMARK_NODE_CODE:
344349
case CMARK_NODE_CODE_BLOCK:
345350
case CMARK_NODE_FORMULA_INLINE:
351+
case CMARK_NODE_FORMULA_BLOCK:
346352
node->len = cmark_set_cstr(node->mem, &node->data, content);
347353
return 1;
348354

src/node.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ typedef struct {
3030
int8_t fenced;
3131
} cmark_code;
3232

33+
typedef struct {
34+
uint8_t fence_length;
35+
uint8_t fence_offset;
36+
} cmark_formula;
37+
3338
typedef struct {
3439
int internal_offset;
3540
int8_t level;
@@ -82,6 +87,7 @@ struct cmark_node {
8287
cmark_heading heading;
8388
cmark_link link;
8489
cmark_custom custom;
90+
cmark_formula formula;
8591
int html_block_type;
8692
} as;
8793
};

0 commit comments

Comments
 (0)