Skip to content

Commit cf05987

Browse files
committed
Expand visit function to return an enum
When visiting, sometimes you want to stop the whole walk, not just stop at a certain level. For example when you find a particular node.
1 parent a7071e9 commit cf05987

5 files changed

Lines changed: 66 additions & 38 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Note that for this major version, almost every change listed below is breaking.
6767
- Most allocations are now performed through an arena.
6868
- You may now build side tables of comment attachments through the `attach_comments` option.
6969
- `newline_list` was renamed to `list_offset_list`.
70+
- Visitor callbacks passed to `pm_visit_node` and `pm_visit_child_nodes` now return a `pm_visit_result_t` instead of a `bool`. Returning `PM_VISIT_DESCEND` or `PM_VISIT_SKIP` replaces returning `true` or `false`, and returning `PM_VISIT_STOP` ends the walk. Both functions now return `false` if the walk was stopped.
7071

7172
#### Rust
7273

‎include/prism/node.h‎

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,41 @@ extern "C" {
3030
*/
3131
PRISM_EXPORTED_FUNCTION const char * pm_node_type(pm_node_type_t node_type);
3232

33+
/**
34+
* The value returned by a visitor callback, which determines where the walk
35+
* goes next.
36+
*/
37+
typedef enum {
38+
/** Continue the walk without visiting this node's children. */
39+
PM_VISIT_SKIP,
40+
41+
/** Continue the walk by visiting this node's children. */
42+
PM_VISIT_DESCEND,
43+
44+
/** End the walk without visiting any more nodes. */
45+
PM_VISIT_STOP
46+
} pm_visit_result_t;
47+
48+
/**
49+
* A callback that is called for each node visited by pm_visit_node and
50+
* pm_visit_child_nodes. The data parameter is the opaque pointer that was
51+
* passed to the function that started the walk.
52+
*/
53+
typedef pm_visit_result_t (*pm_visit_callback_t)(const pm_node_t *node, void *data);
54+
3355
/**
3456
* Visit each of the nodes in this subtree using the given visitor callback. The
35-
* callback function will be called for each node in the subtree. If it returns
36-
* false, then that node's children will not be visited. If it returns true,
37-
* then the children will be visited. The data parameter is treated as an opaque
38-
* pointer and is passed to the visitor callback for consumers to use as they
39-
* see fit.
57+
* callback function will be called for each node in the subtree, and its
58+
* return value determines whether the walk visits that node's children, skips
59+
* them, or stops entirely. The data parameter is treated as an opaque pointer
60+
* and is passed to the visitor callback for consumers to use as they see fit.
4061
*
4162
* As an example:
4263
*
4364
* ```c
4465
* #include "prism.h"
4566
*
46-
* bool visit(const pm_node_t *node, void *data) {
67+
* pm_visit_result_t visit(const pm_node_t *node, void *data) {
4768
* size_t *indent = (size_t *) data;
4869
* for (size_t i = 0; i < *indent * 2; i++) putc(' ', stdout);
4970
* printf("%s\n", pm_node_type(node->type));
@@ -52,7 +73,7 @@ PRISM_EXPORTED_FUNCTION const char * pm_node_type(pm_node_type_t node_type);
5273
* size_t *next_data = &next_indent;
5374
* pm_visit_child_nodes(node, visit, next_data);
5475
*
55-
* return false;
76+
* return PM_VISIT_SKIP;
5677
* }
5778
*
5879
* int main(void) {
@@ -81,19 +102,23 @@ PRISM_EXPORTED_FUNCTION const char * pm_node_type(pm_node_type_t node_type);
81102
* @param node The root node to start visiting from.
82103
* @param visitor The callback to call for each node in the subtree.
83104
* @param data An opaque pointer that is passed to the visitor callback.
105+
* @returns false if the visitor stopped the walk by returning PM_VISIT_STOP,
106+
* true otherwise.
84107
*/
85-
PRISM_EXPORTED_FUNCTION void pm_visit_node(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) PRISM_NONNULL(1);
108+
PRISM_EXPORTED_FUNCTION bool pm_visit_node(const pm_node_t *node, pm_visit_callback_t visitor, void *data) PRISM_NONNULL(1);
86109

87110
/**
88111
* Visit the children of the given node with the given callback. This is the
89112
* default behavior for walking the tree that is called from pm_visit_node if
90-
* the callback returns true.
113+
* the callback returns PM_VISIT_DESCEND.
91114
*
92115
* @param node The node to visit the children of.
93116
* @param visitor The callback to call for each child node.
94117
* @param data An opaque pointer that is passed to the visitor callback.
118+
* @returns false if the visitor stopped the walk by returning PM_VISIT_STOP,
119+
* true otherwise.
95120
*/
96-
PRISM_EXPORTED_FUNCTION void pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) PRISM_NONNULL(1);
121+
PRISM_EXPORTED_FUNCTION bool pm_visit_child_nodes(const pm_node_t *node, pm_visit_callback_t visitor, void *data) PRISM_NONNULL(1);
97122

98123
#ifdef __cplusplus
99124
}

‎src/comments.c‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ pm_comments_blank_after(const pm_comments_walk_t *walk, uint32_t offset, uint32_
223223
* descends itself, so that it can visit children in source order and handle
224224
* each node after all of its descendants.
225225
*/
226-
static bool
226+
static pm_visit_result_t
227227
pm_comments_push(const pm_node_t *node, void *data) {
228228
pm_comments_walk_t *walk = (pm_comments_walk_t *) data;
229229

@@ -237,7 +237,7 @@ pm_comments_push(const pm_node_t *node, void *data) {
237237
}
238238

239239
walk->stack.nodes[walk->stack.size++] = node;
240-
return false;
240+
return PM_VISIT_SKIP;
241241
}
242242

243243
/*

‎src/prism.c‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13187,7 +13187,7 @@ parse_starred_expression(pm_parser_t *parser, pm_binding_power_t binding_power,
1318713187
return parse_value_expression(parser, binding_power, flags, diag_id, depth);
1318813188
}
1318913189

13190-
static bool
13190+
static pm_visit_result_t
1319113191
pm_node_unreference_each(const pm_node_t *node, void *data) {
1319213192
switch (PM_NODE_TYPE(node)) {
1319313193
/* When we are about to destroy a set of nodes that could potentially
@@ -13213,15 +13213,15 @@ pm_node_unreference_each(const pm_node_t *node, void *data) {
1321313213
}
1321413214
parser->current_block_exits->size--;
1321513215

13216-
/* Note returning true here because these nodes could have
13216+
/* Note descending here because these nodes could have
1321713217
* arguments that are themselves block exits. */
13218-
return true;
13218+
return PM_VISIT_DESCEND;
1321913219
}
1322013220

1322113221
index++;
1322213222
}
1322313223

13224-
return true;
13224+
return PM_VISIT_DESCEND;
1322513225
}
1322613226
/* When an implicit local variable is written to or targeted, it becomes
1322713227
* a regular, named local variable. This branch removes it from the list
@@ -13245,10 +13245,10 @@ pm_node_unreference_each(const pm_node_t *node, void *data) {
1324513245
}
1324613246
}
1324713247

13248-
return false;
13248+
return PM_VISIT_SKIP;
1324913249
}
1325013250
default:
13251-
return true;
13251+
return PM_VISIT_DESCEND;
1325213252
}
1325313253
}
1325413254

@@ -17808,7 +17808,7 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_set_t *captures, pm_
1780817808
* are not captures of this pattern. A nested alternation is not visited
1780917809
* either: its operands were already checked when it was parsed.
1781017810
*/
17811-
static bool
17811+
static pm_visit_result_t
1781217812
parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
1781317813
pm_parser_t *parser = (pm_parser_t *) data;
1781417814

@@ -17819,7 +17819,7 @@ parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
1781917819
if (peek_at(parser, parser->start + PM_NODE_START(node)) != '_') {
1782017820
pm_parser_err(parser, PM_NODE_START(node), PM_NODE_LENGTH(node), PM_ERR_PATTERN_CAPTURE_IN_ALTERNATIVE);
1782117821
}
17822-
return false;
17822+
return PM_VISIT_SKIP;
1782317823
case PM_ARRAY_PATTERN_NODE:
1782417824
case PM_ASSOC_NODE:
1782517825
case PM_ASSOC_SPLAT_NODE:
@@ -17829,9 +17829,9 @@ parse_pattern_alternation_error_each(const pm_node_t *node, void *data) {
1782917829
case PM_IMPLICIT_NODE:
1783017830
case PM_PARENTHESES_NODE:
1783117831
case PM_SPLAT_NODE:
17832-
return true;
17832+
return PM_VISIT_DESCEND;
1783317833
default:
17834-
return false;
17834+
return PM_VISIT_SKIP;
1783517835
}
1783617836
}
1783717837

‎templates/src/node.c.erb‎

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -92,24 +92,26 @@ pm_node_type(pm_node_type_t node_type)
9292

9393
/**
9494
* Visit each of the nodes in this subtree using the given visitor callback. The
95-
* callback function will be called for each node in the subtree. If it returns
96-
* false, then that node's children will not be visited. If it returns true,
97-
* then the children will be visited. The data parameter is treated as an opaque
98-
* pointer and is passed to the visitor callback for consumers to use as they
99-
* see fit.
95+
* callback function will be called for each node in the subtree, and its
96+
* return value determines whether the walk visits that node's children, skips
97+
* them, or stops entirely. The data parameter is treated as an opaque pointer
98+
* and is passed to the visitor callback for consumers to use as they see fit.
10099
*/
101-
void
102-
pm_visit_node(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) {
103-
if (visitor(node, data)) pm_visit_child_nodes(node, visitor, data);
100+
bool
101+
pm_visit_node(const pm_node_t *node, pm_visit_callback_t visitor, void *data) {
102+
pm_visit_result_t result = visitor(node, data);
103+
if (result == PM_VISIT_DESCEND) return pm_visit_child_nodes(node, visitor, data);
104+
return result != PM_VISIT_STOP;
104105
}
105106

106107
/**
107108
* Visit the children of the given node with the given callback. This is the
108109
* default behavior for walking the tree that is called from pm_visit_node if
109-
* the callback returns true.
110+
* the callback returns PM_VISIT_DESCEND. A stop from any child returns false
111+
* immediately, so that it propagates up through every enclosing call.
110112
*/
111-
void
112-
pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) {
113+
bool
114+
pm_visit_child_nodes(const pm_node_t *node, pm_visit_callback_t visitor, void *data) {
113115
switch (PM_NODE_TYPE(node)) {
114116
<%- nodes.each do |node| -%>
115117
<%- if (fields = node.fields.select { |field| field.is_a?(Prism::Template::NodeField) || field.is_a?(Prism::Template::OptionalNodeField) || field.is_a?(Prism::Template::NodeListField) }).any? -%>
@@ -120,15 +122,13 @@ pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *nod
120122
// Visit the <%= field.name %> field
121123
<%- case field -%>
122124
<%- when Prism::Template::NodeField -%>
123-
pm_visit_node((const pm_node_t *) cast-><%= field.name %>, visitor, data);
125+
if (!pm_visit_node((const pm_node_t *) cast-><%= field.name %>, visitor, data)) return false;
124126
<%- when Prism::Template::OptionalNodeField -%>
125-
if (cast-><%= field.name %> != NULL) {
126-
pm_visit_node((const pm_node_t *) cast-><%= field.name %>, visitor, data);
127-
}
127+
if (cast-><%= field.name %> != NULL && !pm_visit_node((const pm_node_t *) cast-><%= field.name %>, visitor, data)) return false;
128128
<%- when Prism::Template::NodeListField -%>
129129
const pm_node_list_t *<%= field.name %> = &cast-><%= field.name %>;
130130
for (size_t index = 0; index < <%= field.name %>->size; index++) {
131-
pm_visit_node(<%= field.name %>->nodes[index], visitor, data);
131+
if (!pm_visit_node(<%= field.name %>->nodes[index], visitor, data)) return false;
132132
}
133133
<%- end -%>
134134
<%- end -%>
@@ -143,6 +143,8 @@ pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *nod
143143
case PM_SCOPE_NODE:
144144
break;
145145
}
146+
147+
return true;
146148
}
147149
<%- nodes.each do |node| -%>
148150

0 commit comments

Comments
 (0)