Created
May 2, 2016 07:30
-
-
Save nwellnhof/89077f57e8da0cf13631bf4a4a62ba9f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Add two functions `can_contain` and `can_be_contained` to the | |
// node type metaobject. (This is just an example. It's possible | |
// to split the work between these two functions differently.) | |
struct cmark_ext_node_type { | |
cmark_syntax_extension *extension; | |
char *name; | |
void (*free)(cmark_node *node); | |
// Used to check document structure. | |
int (*can_contain)(cmark_node *node, cmark_node *child); | |
// Note that this function has a node type parameter. | |
// `parent_type` must not be `CMARK_NODE_EXT`. | |
int (*can_be_contained)(cmark_node *node, cmark_node_type parent_type); | |
}; | |
// Expose `can_be_contained`. | |
int | |
cmark_node_can_be_contained(cmark_node *node, cmark_node_type parent_type) { | |
if (node->type == CMARK_NODE_EXT) { | |
return node->as.ext.type->can_be_contained(node, parent_type); | |
} | |
else { | |
// This is only needed for extension nodes, but it could do | |
// something useful for core nodes, too. | |
... | |
} | |
} | |
// Now cmark's internal `S_can_contain` function can handle extension | |
// nodes like this: | |
static bool | |
S_can_contain(cmark_node *node, cmark_node *child) { | |
if (node->type == CMARK_NODE_EXT) { | |
return node->as.ext.type->can_contain(node, child); | |
} | |
if (child->type == CMARK_NODE_EXT) { | |
return child->as.ext.type->can_be_contained(child, node->type); | |
} | |
// Both `node` and `child` aren't extension nodes. | |
// Continue as usual. | |
... | |
} | |
// A "table" node would implement these functions like this. | |
static int | |
S_table_can_contain(cmark_node *table, cmark_node *child) { | |
// This is just an example, but I think it's a good idea | |
// to use something similar to XML's namespace URIs to | |
// identify extension nodes. | |
const char *child_type_uri = child_get_node_type_string(child); | |
// Only allow table rows. | |
if (strcmp(child_type_uri, "http://myext.com/table-row") == 0) { | |
return 1; | |
} | |
else { | |
return 0; | |
} | |
} | |
static int | |
S_table_can_be_contained(cmark_node *table, cmark_node_type parent_type) { | |
return parent_type == CMARK_NODE_DOCUMENT | |
|| parent_type == CMARK_NODE_BLOCKQUOTE | |
|| parent_type == CMARK_NODE_LIST | |
|| ...; | |
} | |
// A "table cell" node would implement these functions like this. | |
static int | |
S_table_cell_can_contain(cmark_node *cell, cmark_node *child) { | |
cmark_node_type child_type = cmark_node_get_type(child); | |
if (child_type == CMARK_NODE_EXT) { | |
// Two extension nodes. | |
// | |
// HERE'S THE TRICK: `can_contain` calls `can_be_contained` | |
// with a standard node type. This means that table cells | |
// can contain the same node types as documents, i. e. all | |
// top-level block types. | |
// | |
// This makes it possible to handle nodes from separate | |
// extensions that don't know about each other. | |
return cmark_node_can_be_contained(child, CMARK_NODE_DOCUMENT); | |
} | |
return child_type == CMARK_NODE_PARAGRAPH | |
|| child_type == CMARK_NODE_BLOCK_QUOTE | |
|| child_type == CMARK_NODE_LIST | |
|| ...; | |
} | |
static int | |
S_table_cell_can_be_contained(cmark_node *cell, cmark_node_type parent_type) { | |
// Table cells can't be contained in non-extension nodes. | |
return 0; | |
} | |
// To understand how this works, this is the call chain when testing | |
// whether a table cell can contain a table. | |
// | |
// S_can_contain(table_cell, table); | |
// S_table_call_can_contain(table_cell, table); | |
// S_table_can_be_contained(table, CMARK_NODE_DOCUMENT); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment