From 420c20a112acd75af463d8930d8d59f2d25e9cd5 Mon Sep 17 00:00:00 2001 From: Kevin Backhouse Date: Thu, 1 Dec 2022 18:44:38 +0000 Subject: [PATCH] Add registration mechanism for custom node flags --- api_test/main.c | 1 + extensions/table.c | 4 ++++ src/main.c | 1 + src/node.c | 34 ++++++++++++++++++++++++++++++++++ src/node.h | 33 ++++++++++++++++++++++++++------- test/cmark.py | 2 ++ 6 files changed, 68 insertions(+), 7 deletions(-) diff --git a/api_test/main.c b/api_test/main.c index 62006eaa9..8b3973825 100644 --- a/api_test/main.c +++ b/api_test/main.c @@ -1133,6 +1133,7 @@ int main() { int retval; test_batch_runner *runner = test_batch_runner_new(); + cmark_init_standard_node_flags(); version(runner); constructor(runner); accessors(runner); diff --git a/extensions/table.c b/extensions/table.c index 9fce51e8d..f079e3fd7 100644 --- a/extensions/table.c +++ b/extensions/table.c @@ -11,6 +11,9 @@ #include "table.h" #include "cmark-gfm-core-extensions.h" +// Custom node flag, initialized in `create_table_extension`. +static cmark_node__internal_flags CMARK_NODE__TABLE_VISITED; + cmark_node_type CMARK_NODE_TABLE, CMARK_NODE_TABLE_ROW, CMARK_NODE_TABLE_CELL; @@ -790,6 +793,7 @@ static int escape(cmark_syntax_extension *self, cmark_node *node, int c) { cmark_syntax_extension *create_table_extension(void) { cmark_syntax_extension *self = cmark_syntax_extension_new("table"); + cmark_register_node_flag(&CMARK_NODE__TABLE_VISITED); cmark_syntax_extension_set_match_block_func(self, matches); cmark_syntax_extension_set_open_block_func(self, try_opening_table_block); cmark_syntax_extension_set_get_type_string_func(self, get_type_string); diff --git a/src/main.c b/src/main.c index a62c4f2ca..caed771b4 100644 --- a/src/main.c +++ b/src/main.c @@ -139,6 +139,7 @@ int main(int argc, char *argv[]) { } #endif + cmark_init_standard_node_flags(); cmark_gfm_core_extensions_ensure_registered(); #ifdef USE_PLEDGE diff --git a/src/node.c b/src/node.c index 0118d6511..a28214195 100644 --- a/src/node.c +++ b/src/node.c @@ -9,6 +9,40 @@ static void S_node_unlink(cmark_node *node); #define NODE_MEM(node) cmark_node_mem(node) +cmark_node__internal_flags CMARK_NODE__OPEN = 0; +cmark_node__internal_flags CMARK_NODE__LAST_LINE_BLANK = 0; +cmark_node__internal_flags CMARK_NODE__LAST_LINE_CHECKED = 0; + +void cmark_register_node_flag(cmark_node__internal_flags *flags) { + static uint8_t shift = 0; + + // flags should be a pointer to a global variable and this function + // should only be called once to initialize its value. + if (*flags) { + fprintf(stderr, "flag initialization error in cmark_register_node_flag\n"); + abort(); + } + + // Check that we haven't run out of bits. + if (shift >= 8 * sizeof(cmark_node__internal_flags)) { + fprintf(stderr, "too many flags in cmark_register_node_flag\n"); + abort(); + } + + *flags = (cmark_node__internal_flags)1 << shift; + shift++; +} + +void cmark_init_standard_node_flags() { + static int initialized = 0; + if (!initialized) { + initialized = 1; + cmark_register_node_flag(&CMARK_NODE__OPEN); + cmark_register_node_flag(&CMARK_NODE__LAST_LINE_BLANK); + cmark_register_node_flag(&CMARK_NODE__LAST_LINE_CHECKED); + } +} + bool cmark_node_can_contain_type(cmark_node *node, cmark_node_type child_type) { if (child_type == CMARK_NODE_DOCUMENT) { return false; diff --git a/src/node.h b/src/node.h index 7ee0d6523..281258f7a 100644 --- a/src/node.h +++ b/src/node.h @@ -48,12 +48,7 @@ typedef struct { cmark_chunk on_exit; } cmark_custom; -enum cmark_node__internal_flags { - CMARK_NODE__OPEN = (1 << 0), - CMARK_NODE__LAST_LINE_BLANK = (1 << 1), - CMARK_NODE__LAST_LINE_CHECKED = (1 << 2), - CMARK_NODE__TABLE_VISITED = (1 << 3), -}; +typedef uint16_t cmark_node__internal_flags; struct cmark_node { cmark_strbuf content; @@ -73,7 +68,7 @@ struct cmark_node { int end_column; int internal_offset; uint16_t type; - uint16_t flags; + cmark_node__internal_flags flags; cmark_syntax_extension *extension; @@ -96,6 +91,30 @@ struct cmark_node { } as; }; +/** + * Syntax extensions can use this function to register a custom node + * flag. The flags are stored in the `flags` field of the `cmark_node` + * struct. The `flags` parameter should be the address of a global variable + * which will store the flag value. + */ +CMARK_GFM_EXPORT +void cmark_register_node_flag(cmark_node__internal_flags *flags); + +/** + * Standard node flags. (Initialized using `cmark_init_standard_node_flags`.) + */ +extern cmark_node__internal_flags CMARK_NODE__OPEN; +extern cmark_node__internal_flags CMARK_NODE__LAST_LINE_BLANK; +extern cmark_node__internal_flags CMARK_NODE__LAST_LINE_CHECKED; + +/** + * Uses `cmark_register_node_flag` to initialize the standard node flags. + * This function should be called at program startup time. Calling it + * multiple times has no additional effect. + */ +CMARK_GFM_EXPORT +void cmark_init_standard_node_flags(); + static CMARK_INLINE cmark_mem *cmark_node_mem(cmark_node *node) { return node->content.mem; } diff --git a/test/cmark.py b/test/cmark.py index b0eb20dbe..9d16f014d 100644 --- a/test/cmark.py +++ b/test/cmark.py @@ -13,6 +13,7 @@ def pipe_through_prog(prog, text): def parse(lib, extlib, text, extensions): cmark_gfm_core_extensions_ensure_registered = extlib.cmark_gfm_core_extensions_ensure_registered + cmark_init_standard_node_flags = lib.cmark_init_standard_node_flags find_syntax_extension = lib.cmark_find_syntax_extension find_syntax_extension.restype = c_void_p @@ -32,6 +33,7 @@ def parse(lib, extlib, text, extensions): parser_finish.restype = c_void_p parser_finish.argtypes = [c_void_p] + cmark_init_standard_node_flags() cmark_gfm_core_extensions_ensure_registered() parser = parser_new(0)