diff options
-rw-r--r-- | api_test/main.c | 41 | ||||
-rw-r--r-- | src/cmark.h | 52 | ||||
-rw-r--r-- | src/iterator.c | 123 | ||||
-rw-r--r-- | src/iterator.h | 10 |
4 files changed, 152 insertions, 74 deletions
diff --git a/api_test/main.c b/api_test/main.c index d2e41d3..af40a9f 100644 --- a/api_test/main.c +++ b/api_test/main.c @@ -320,6 +320,46 @@ iterator(test_batch_runner *runner) { } static void +iterator_delete(test_batch_runner *runner) { + static const char md[] = + "a *b* c\n" + "\n" + "* item1\n" + "* item2\n" + "\n" + "a `b` c\n" + "\n" + "* item1\n" + "* item2\n"; + cmark_node *doc = cmark_parse_document(md, sizeof(md) - 1); + cmark_iter *iter = cmark_iter_new(doc); + cmark_event_type ev_type; + + while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { + cmark_node *node = cmark_iter_get_node(iter); + // Delete list, emph, and code nodes. + if ((ev_type == CMARK_EVENT_EXIT && + node->type == CMARK_NODE_LIST) || + (ev_type == CMARK_EVENT_EXIT && + node->type == CMARK_NODE_EMPH) || + (ev_type == CMARK_EVENT_ENTER && + node->type == CMARK_NODE_CODE)) { + cmark_node_free(node); + } + } + + char *html = cmark_render_html(doc, CMARK_OPT_DEFAULT); + static const char expected[] = + "<p>a c</p>\n" + "<p>a c</p>\n"; + STR_EQ(runner, html, expected, "iterate and delete nodes"); + + free(html); + cmark_iter_free(iter); + cmark_node_free(doc); +} + +static void create_tree(test_batch_runner *runner) { char *html; @@ -630,6 +670,7 @@ int main() { accessors(runner); node_check(runner); iterator(runner); + iterator_delete(runner); create_tree(runner); hierarchy(runner); parser(runner); diff --git a/src/cmark.h b/src/cmark.h index 72650c9..04ca6d7 100644 --- a/src/cmark.h +++ b/src/cmark.h @@ -83,6 +83,7 @@ typedef struct cmark_parser cmark_parser; typedef struct cmark_iter cmark_iter; typedef enum { + CMARK_EVENT_NONE, CMARK_EVENT_DONE, CMARK_EVENT_ENTER, CMARK_EVENT_EXIT @@ -164,13 +165,24 @@ cmark_node_last_child(cmark_node *node); * cmark_iter_free(iter); * } * - * Note that if you delete the current node, its first child, or its - * next sibling, the iterator may point to a nonexistent note. - * Use 'cmark_iter_reset' to set its pointer to the next node that - * should be traversed. + * Iterators will never return `EXIT` events for leaf nodes, which are nodes + * of type: + * + * * CMARK_NODE_HTML + * * CMARK_NODE_HRULE + * * CMARK_NODE_CODE_BLOCK + * * CMARK_NODE_TEXT + * * CMARK_NODE_SOFTBREAK + * * CMARK_NODE_LINEBREAK + * * CMARK_NODE_CODE + * * CMARK_NODE_INLINE_HTML + * + * Nodes must only be modified after an `EXIT` event, or an `ENTER` event for + * leaf nodes. */ -/** Creates a new iterator starting at 'root'. +/** Creates a new iterator starting at 'root'. The current node and event + * type are undefined until `cmark_iter_next` is called for the first time. */ CMARK_EXPORT cmark_iter* @@ -182,28 +194,34 @@ CMARK_EXPORT void cmark_iter_free(cmark_iter *iter); -/** Resets the iterator so that the current node is 'current' and - the event type is 'event_type'. Use this to resume after destructively - modifying the tree structure. - */ -CMARK_EXPORT -void -cmark_iter_reset(cmark_iter *iter, cmark_node *current, - cmark_event_type event_type); - -/** Returns the event type (`CMARK_EVENT_ENTER`, `CMARK_EVENT_EXIT`, - * or `CMARK_EVENT_DONE`) for the next node. +/** Advances to the next node and returns the event type (`CMARK_EVENT_ENTER`, + * `CMARK_EVENT_EXIT` or `CMARK_EVENT_DONE`). */ CMARK_EXPORT cmark_event_type cmark_iter_next(cmark_iter *iter); -/** Returns the next node in the sequence described above. +/** Returns the current node. */ CMARK_EXPORT cmark_node* cmark_iter_get_node(cmark_iter *iter); +/** Returns the current event type. + */ +CMARK_EXPORT +cmark_event_type +cmark_iter_get_event_type(cmark_iter *iter); + +/** Resets the iterator so that the current node is 'current' and + * the event type is 'event_type'. The new current node must be a + * descendant of the root node or the root node itself. + */ +CMARK_EXPORT +void +cmark_iter_reset(cmark_iter *iter, cmark_node *current, + cmark_event_type event_type); + /** * ## Accessors */ diff --git a/src/iterator.c b/src/iterator.c index a3ae415..7f90cc7 100644 --- a/src/iterator.c +++ b/src/iterator.c @@ -1,3 +1,4 @@ +#include <assert.h> #include <stdlib.h> #include "config.h" @@ -5,18 +6,32 @@ #include "cmark.h" #include "iterator.h" +static const int S_leaf_mask = + (1 << CMARK_NODE_HTML) | + (1 << CMARK_NODE_HRULE) | + (1 << CMARK_NODE_CODE_BLOCK) | + (1 << CMARK_NODE_TEXT) | + (1 << CMARK_NODE_SOFTBREAK) | + (1 << CMARK_NODE_LINEBREAK) | + (1 << CMARK_NODE_CODE) | + (1 << CMARK_NODE_INLINE_HTML); + cmark_iter* cmark_iter_new(cmark_node *root) { + if (root == NULL) { + return NULL; + } cmark_iter *iter = (cmark_iter*)malloc(sizeof(cmark_iter)); if (iter == NULL) { return NULL; - } else { - iter->root = root; - iter->current = root; - iter->event_type = CMARK_EVENT_ENTER; - return iter; } + iter->root = root; + iter->cur.ev_type = CMARK_EVENT_NONE; + iter->cur.node = NULL; + iter->next.ev_type = CMARK_EVENT_ENTER; + iter->next.node = root; + return iter; } void @@ -25,72 +40,72 @@ cmark_iter_free(cmark_iter *iter) free(iter); } -cmark_event_type -cmark_iter_next(cmark_iter *iter) +static bool +S_is_leaf(cmark_node *node) { - return iter->event_type; + return (1 << node->type) & S_leaf_mask; } -int S_is_leaf(cmark_node *node) +cmark_event_type +cmark_iter_next(cmark_iter *iter) { - switch (cmark_node_get_type(node)) { - case CMARK_NODE_HTML: - case CMARK_NODE_HRULE: - case CMARK_NODE_CODE_BLOCK: - case CMARK_NODE_TEXT: - case CMARK_NODE_SOFTBREAK: - case CMARK_NODE_LINEBREAK: - case CMARK_NODE_CODE: - case CMARK_NODE_INLINE_HTML: - return 1; - default: - return 0; + cmark_event_type ev_type = iter->next.ev_type; + cmark_node *node = iter->next.node; + + iter->cur.ev_type = ev_type; + iter->cur.node = node; + + if (ev_type == CMARK_EVENT_DONE) { + return ev_type; } + + /* roll forward to next item, setting both fields */ + if (ev_type == CMARK_EVENT_ENTER && !S_is_leaf(node)) { + if (node->first_child == NULL) { + /* stay on this node but exit */ + iter->next.ev_type = CMARK_EVENT_EXIT; + } else { + iter->next.ev_type = CMARK_EVENT_ENTER; + iter->next.node = node->first_child; + } + } else if (node == iter->root) { + /* don't move past root */ + iter->next.ev_type = CMARK_EVENT_DONE; + iter->next.node = NULL; + } else if (node->next) { + iter->next.ev_type = CMARK_EVENT_ENTER; + iter->next.node = node->next; + } else if (node->parent) { + iter->next.ev_type = CMARK_EVENT_EXIT; + iter->next.node = node->parent; + } else { + assert(false); + iter->next.ev_type = CMARK_EVENT_DONE; + iter->next.node = NULL; + } + + return ev_type; } void cmark_iter_reset(cmark_iter *iter, cmark_node *current, cmark_event_type event_type) { - iter->event_type = event_type; - iter->current = current; + iter->next.ev_type = event_type; + iter->next.node = current; + cmark_iter_next(iter); } cmark_node* cmark_iter_get_node(cmark_iter *iter) { - /* we'll return current */ - cmark_node *cur = iter->current; - - if (cur == NULL || iter->event_type == CMARK_EVENT_DONE) { - return NULL; - } - - /* roll forward to next item, setting both fields */ - if (iter->event_type == CMARK_EVENT_ENTER && !S_is_leaf(cur)) { - if (cur->first_child == NULL) { - /* stay on this node but exit */ - iter->event_type = CMARK_EVENT_EXIT; - } else { - iter->current = cur->first_child; - iter->event_type = CMARK_EVENT_ENTER; - } - } else if (cur == iter->root) { - /* don't move past root */ - iter->event_type = CMARK_EVENT_DONE; - iter->current = NULL; - } else if (cur->next) { - iter->event_type = CMARK_EVENT_ENTER; - iter->current = cur->next; - } else if (cur->parent) { - iter->event_type = CMARK_EVENT_EXIT; - iter->current = cur->parent; - } else { - iter->event_type = CMARK_EVENT_DONE; - iter->current = NULL; - } + return iter->cur.node; +} - return cur; +cmark_event_type +cmark_iter_get_event_type(cmark_iter *iter) +{ + return iter->cur.ev_type; } diff --git a/src/iterator.h b/src/iterator.h index bf53112..027b10b 100644 --- a/src/iterator.h +++ b/src/iterator.h @@ -6,12 +6,16 @@ extern "C" { #endif #include "cmark.h" -#include "node.h" + +typedef struct { + cmark_event_type ev_type; + cmark_node *node; +} cmark_iter_state; struct cmark_iter { - cmark_node *current; cmark_node *root; - cmark_event_type event_type; + cmark_iter_state cur; + cmark_iter_state next; }; #ifdef __cplusplus |