From a3030f985a973b3b835645313fdad1a8a72ff432 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Sat, 13 Dec 2014 14:13:58 -0800 Subject: Added iterator interface to API, removed cmark_walk. * Added `iterator.c`, `iterator.h`. * Removed `cmark_walk`. * Replaced `cmark_walk` with iterator in HTML renderer. * Replaced API test for `cmark_walk` with simple iterator test. --- src/CMakeLists.txt | 2 ++ src/cmark.h | 60 ++++++++++++++++++++++++------------- src/html.c | 18 ++++++++---- src/iterator.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/iterator.h | 21 +++++++++++++ src/node.c | 66 ----------------------------------------- 6 files changed, 160 insertions(+), 93 deletions(-) create mode 100644 src/iterator.c create mode 100644 src/iterator.h (limited to 'src') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aef1b14..e6a578c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,7 @@ set(HEADERS parser.h buffer.h node.h + iterator.h chunk.h references.h debug.h @@ -18,6 +19,7 @@ set(HEADERS set(LIBRARY_SOURCES cmark.c node.c + iterator.c blocks.c inlines.c print.c diff --git a/src/cmark.h b/src/cmark.h index a43703a..42ffa81 100644 --- a/src/cmark.h +++ b/src/cmark.h @@ -14,7 +14,9 @@ extern "C" { * \- CommonMark parsing, manipulating, and rendering */ -/** .SH SIMPLE INTERFACE +/** .SH DESCRIPTION + * + * .SS Simple Interface */ /** Current version of library. @@ -28,7 +30,7 @@ extern "C" { CMARK_EXPORT char *cmark_markdown_to_html(const char *text, int len); -/** .SH NODE STRUCTURE +/** .SS Node Structure */ /** @@ -84,6 +86,7 @@ typedef enum { typedef struct cmark_node cmark_node; typedef struct cmark_parser cmark_parser; +typedef struct cmark_iter cmark_iter; typedef enum { CMARK_EVENT_DONE, @@ -95,7 +98,7 @@ typedef int (*cmark_node_handler)(cmark_node *node, cmark_event_type ev_type, void *state); /** - * .SH CREATING AND DESTROYING NODES + * .SS Creating and Destroying Nodes */ /** @@ -109,7 +112,7 @@ CMARK_EXPORT void cmark_node_free(cmark_node *node); /** - * .SH TREE TRAVERSAL + * .SS Tree Traversal */ CMARK_EXPORT cmark_node* cmark_node_next(cmark_node *node); @@ -135,7 +138,35 @@ CMARK_EXPORT cmark_node* cmark_node_last_child(cmark_node *node); /** - * .SH ACCESSORS + * .SS Iterator + */ + +/** + */ +CMARK_EXPORT +cmark_iter* +cmark_iter_new(cmark_node *root); + +/** + */ +CMARK_EXPORT +void +cmark_iter_free(cmark_iter *iter); + +/** + */ +CMARK_EXPORT +cmark_event_type +cmark_iter_next(cmark_iter *iter); + +/** + */ +CMARK_EXPORT +cmark_node* +cmark_iter_get_node(cmark_iter *iter); + +/** + * .SS Accessors */ /** @@ -239,7 +270,7 @@ CMARK_EXPORT int cmark_node_get_end_line(cmark_node *node); /** - * .SH TREE MANIPULATION + * .SS Tree Manipulation */ /** @@ -268,7 +299,7 @@ CMARK_EXPORT int cmark_node_append_child(cmark_node *node, cmark_node *child); /** - * .SH PARSING + * .SS Parsing */ /** @@ -302,7 +333,7 @@ CMARK_EXPORT cmark_node *cmark_parse_file(FILE *f); /** - * .SH RENDERING + * .SS Rendering */ /** @@ -315,19 +346,6 @@ char *cmark_render_ast(cmark_node *root); CMARK_EXPORT char *cmark_render_html(cmark_node *root); -/** Walks the tree starting from root, applying handler to each node. - * Nodes that can have children are visited twice, once on the way in - * and once on the way out. handler is a function that takes a node - * pointer, a cmark_event_type, - * and a pointer to a state structure that can be consulted and - * updated by the handler. The handler should return 1 on success, - * 0 on failure. cmark_walk returns 1 if it traversed the entire - * tree, 0 if it quit early in response to a 0 status from the - * handler. - */ -CMARK_EXPORT -int cmark_walk(cmark_node *root, cmark_node_handler handler, void *state); - /** .SH AUTHORS * * John MacFarlane, Vicent Marti, Kārlis Gaņģis, Nick Wellnhofer. diff --git a/src/html.c b/src/html.c index 1ccb57a..f719405 100644 --- a/src/html.c +++ b/src/html.c @@ -271,12 +271,18 @@ char *cmark_render_html(cmark_node *root) { char *result; strbuf html = GH_BUF_INIT; + cmark_event_type ev_type; + cmark_node *cur; struct render_state state = { &html, NULL }; - if (cmark_walk(root, S_render_node, &state)) { - result = (char *)strbuf_detach(&html); - strbuf_free(&html); - return result; - } else { - return NULL; + cmark_iter *iter = cmark_iter_new(root); + + while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { + cur = cmark_iter_get_node(iter); + S_render_node(cur, ev_type, &state); } + result = (char *)strbuf_detach(&html); + + cmark_iter_free(iter); + strbuf_free(&html); + return result; } diff --git a/src/iterator.c b/src/iterator.c new file mode 100644 index 0000000..a9e6d20 --- /dev/null +++ b/src/iterator.c @@ -0,0 +1,86 @@ +#include + +#include "config.h" +#include "node.h" +#include "cmark.h" +#include "iterator.h" + +cmark_iter* +cmark_iter_new(cmark_node *root) +{ + 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; + } +} + +void +cmark_iter_free(cmark_iter *iter) +{ + free(iter); +} + +cmark_event_type +cmark_iter_next(cmark_iter *iter) +{ + return iter->event_type; +} + +int S_is_leaf(cmark_node *node) +{ + 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_INLINE_CODE: + case CMARK_NODE_INLINE_HTML: + return 1; + default: + return 0; + } +} + +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 cur; +} diff --git a/src/iterator.h b/src/iterator.h new file mode 100644 index 0000000..bf53112 --- /dev/null +++ b/src/iterator.h @@ -0,0 +1,21 @@ +#ifndef CMARK_ITERATOR_H +#define CMARK_ITERATOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "cmark.h" +#include "node.h" + +struct cmark_iter { + cmark_node *current; + cmark_node *root; + cmark_event_type event_type; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/node.c b/src/node.c index 72eb619..ab3771b 100644 --- a/src/node.c +++ b/src/node.c @@ -766,69 +766,3 @@ cmark_node_check(cmark_node *node, FILE *out) return errors; } - -int S_is_leaf_node(cmark_node *current_node) -{ - switch (cmark_node_get_type(current_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_INLINE_CODE: - case CMARK_NODE_INLINE_HTML: - return 1; - default: - return 0; - } -} - -int cmark_walk(cmark_node *root, cmark_node_handler handler, void *state) -{ - int ev_type = CMARK_EVENT_ENTER; - cmark_node *current_node = root; - int depth = 0; - cmark_node *next, *parent, *first_child; - - while (current_node != NULL && depth >= 0) { - - next = current_node->next; - parent = current_node->parent; - - if (!handler(current_node, ev_type, state)) { - return 0; - } - - if (ev_type == CMARK_EVENT_ENTER && - !S_is_leaf_node(current_node)) { - first_child = current_node->first_child; - if (first_child == NULL) { - ev_type = CMARK_EVENT_EXIT; // stay on this node - } else { - depth += 1; - current_node = first_child; - } - } else { - if (current_node) { - next = current_node->next; - parent = current_node->parent; - } - if (next) { - // don't go past root: - if (current_node == root) { - ev_type = CMARK_EVENT_DONE; - return 1; - } else { - ev_type = CMARK_EVENT_ENTER; - current_node = next; - } - } else { - ev_type = CMARK_EVENT_EXIT; - depth -= 1; - current_node = parent; - } - } - } - return 1; -} -- cgit v1.2.3