Skip to content

Commit 537947a

Browse files
committed
Provide API for visiting in C
1 parent ef870ca commit 537947a

File tree

2 files changed

+117
-0
lines changed

2 files changed

+117
-0
lines changed

include/prism/node.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,66 @@ PRISM_EXPORTED_FUNCTION void pm_node_memsize(pm_node_t *node, pm_memsize_t *mems
8282
*/
8383
PRISM_EXPORTED_FUNCTION const char * pm_node_type_to_str(pm_node_type_t node_type);
8484

85+
/**
86+
* Visit each of the nodes in this subtree using the given visitor callback. The
87+
* callback function will be called for each node in the subtree. If it returns
88+
* false, then that node's children will not be visited. If it returns true,
89+
* then the children will be visited. The data parameter is treated as an opaque
90+
* pointer and is passed to the visitor callback for consumers to use as they
91+
* see fit.
92+
*
93+
* As an example:
94+
*
95+
* ```c
96+
* #include "prism.h"
97+
*
98+
* bool visit(const pm_node_t *node, void *data) {
99+
* size_t *indent = (size_t *) data;
100+
* for (size_t i = 0; i < *indent * 2; i++) putc(' ', stdout);
101+
* printf("%s\n", pm_node_type_to_str(node->type));
102+
*
103+
* size_t next_indent = *indent + 1;
104+
* size_t *next_data = &next_indent;
105+
* pm_visit_child_nodes(node, visit, next_data);
106+
*
107+
* return false;
108+
* }
109+
*
110+
* int main(void) {
111+
* const char *source = "1 + 2; 3 + 4";
112+
* size_t size = strlen(source);
113+
*
114+
* pm_parser_t parser;
115+
* pm_options_t options = { 0 };
116+
* pm_parser_init(&parser, (const uint8_t *) source, size, &options);
117+
*
118+
* size_t indent = 0;
119+
* pm_node_t *node = pm_parse(&parser);
120+
*
121+
* size_t *data = &indent;
122+
* pm_visit_node(node, visit, data);
123+
*
124+
* pm_node_destroy(&parser, node);
125+
* pm_parser_free(&parser);
126+
* return EXIT_SUCCESS;
127+
* }
128+
* ```
129+
*
130+
* @param node The root node to start visiting from.
131+
* @param visitor The callback to call for each node in the subtree.
132+
* @param data An opaque pointer that is passed to the visitor callback.
133+
*/
134+
PRISM_EXPORTED_FUNCTION void pm_visit_node(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data);
135+
136+
/**
137+
* Visit the children of the given node with the given callback. This is the
138+
* default behavior for walking the tree that is called from pm_visit_node if
139+
* the callback returns true.
140+
*
141+
* @param node The node to visit the children of.
142+
* @param visitor The callback to call for each child node.
143+
* @param data An opaque pointer that is passed to the visitor callback.
144+
*/
145+
PRISM_EXPORTED_FUNCTION void pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data);
146+
85147
#endif

templates/src/node.c.erb

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,61 @@ pm_node_type_to_str(pm_node_type_t node_type)
192192
return "";
193193
}
194194

195+
/**
196+
* Visit each of the nodes in this subtree using the given visitor callback. The
197+
* callback function will be called for each node in the subtree. If it returns
198+
* false, then that node's children will not be visited. If it returns true,
199+
* then the children will be visited. The data parameter is treated as an opaque
200+
* pointer and is passed to the visitor callback for consumers to use as they
201+
* see fit.
202+
*/
203+
PRISM_EXPORTED_FUNCTION void
204+
pm_visit_node(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) {
205+
if (visitor(node, data)) pm_visit_child_nodes(node, visitor, data);
206+
}
207+
208+
/**
209+
* Visit the children of the given node with the given callback. This is the
210+
* default behavior for walking the tree that is called from pm_visit_node if
211+
* the callback returns true.
212+
*/
213+
PRISM_EXPORTED_FUNCTION void
214+
pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) {
215+
switch (PM_NODE_TYPE(node)) {
216+
<%- nodes.each do |node| -%>
217+
<%- if (fields = node.fields.select { |field| field.is_a?(Prism::NodeField) || field.is_a?(Prism::OptionalNodeField) || field.is_a?(Prism::NodeListField) }).any? -%>
218+
case <%= node.type %>: {
219+
const pm_<%= node.human %>_t *cast = (const pm_<%= node.human %>_t *) node;
220+
<%- fields.each do |field| -%>
221+
222+
// Visit the <%= field.name %> field
223+
<%- case field -%>
224+
<%- when Prism::NodeField -%>
225+
pm_visit_node((const pm_node_t *) cast-><%= field.name %>, visitor, data);
226+
<%- when Prism::OptionalNodeField -%>
227+
if (cast-><%= field.name %> != NULL) {
228+
pm_visit_node((const pm_node_t *) cast-><%= field.name %>, visitor, data);
229+
}
230+
<%- when Prism::NodeListField -%>
231+
const pm_node_list_t *<%= field.name %> = &cast-><%= field.name %>;
232+
for (size_t index = 0; index < <%= field.name %>->size; index++) {
233+
pm_visit_node(<%= field.name %>->nodes[index], visitor, data);
234+
}
235+
<%- end -%>
236+
<%- end -%>
237+
238+
break;
239+
}
240+
<%- else -%>
241+
case <%= node.type %>:
242+
break;
243+
<%- end -%>
244+
<%- end -%>
245+
case PM_SCOPE_NODE:
246+
break;
247+
}
248+
}
249+
195250
static void
196251
pm_dump_json_constant(pm_buffer_t *buffer, const pm_parser_t *parser, pm_constant_id_t constant_id) {
197252
const pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, constant_id);

0 commit comments

Comments
 (0)