ruby_tree_sitter 0.20.8.2-x86_64-darwin-20 → 1.0.0-x86_64-darwin-20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/LICENSE +2 -1
- data/README.md +32 -18
- data/ext/tree_sitter/extconf.rb +1 -43
- data/ext/tree_sitter/input.c +1 -0
- data/ext/tree_sitter/language.c +131 -46
- data/ext/tree_sitter/logger.c +28 -12
- data/ext/tree_sitter/node.c +438 -130
- data/ext/tree_sitter/parser.c +232 -37
- data/ext/tree_sitter/query.c +197 -72
- data/ext/tree_sitter/query_cursor.c +140 -28
- data/ext/tree_sitter/repo.rb +121 -0
- data/ext/tree_sitter/tree.c +118 -34
- data/ext/tree_sitter/tree_cursor.c +205 -33
- data/ext/tree_sitter/tree_sitter.c +12 -0
- data/lib/tree_sitter/node.rb +43 -9
- data/lib/tree_sitter/tree_sitter.bundle +0 -0
- data/lib/tree_sitter/version.rb +4 -2
- data/lib/tree_sitter.rb +1 -0
- data/lib/tree_stand/ast_modifier.rb +30 -0
- data/lib/tree_stand/breadth_first_visitor.rb +54 -0
- data/lib/tree_stand/config.rb +13 -0
- data/lib/tree_stand/node.rb +224 -0
- data/lib/tree_stand/parser.rb +67 -0
- data/lib/tree_stand/range.rb +55 -0
- data/lib/tree_stand/tree.rb +123 -0
- data/lib/tree_stand/utils/printer.rb +73 -0
- data/lib/tree_stand/version.rb +7 -0
- data/lib/tree_stand/visitor.rb +127 -0
- data/lib/tree_stand/visitors/tree_walker.rb +37 -0
- data/lib/tree_stand.rb +48 -0
- data/tree_sitter.gemspec +15 -12
- metadata +37 -107
- data/test/README.md +0 -15
- data/test/test_helper.rb +0 -9
- data/test/tree_sitter/js_test.rb +0 -48
- data/test/tree_sitter/language_test.rb +0 -73
- data/test/tree_sitter/logger_test.rb +0 -70
- data/test/tree_sitter/node_test.rb +0 -355
- data/test/tree_sitter/parser_test.rb +0 -140
- data/test/tree_sitter/query_test.rb +0 -153
- data/test/tree_sitter/tree_cursor_test.rb +0 -83
- data/test/tree_sitter/tree_test.rb +0 -51
@@ -17,58 +17,220 @@ DATA_UNWRAP(tree_cursor)
|
|
17
17
|
DATA_NEW(cTreeCursor, TSTreeCursor, tree_cursor)
|
18
18
|
DATA_FROM_VALUE(TSTreeCursor, tree_cursor)
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
return
|
20
|
+
/**
|
21
|
+
* Safely copy a tree cursor.
|
22
|
+
*
|
23
|
+
* @return [TreeCursor]
|
24
|
+
*/
|
25
|
+
static VALUE tree_cursor_copy(VALUE self) {
|
26
|
+
VALUE res = tree_cursor_allocate(cTreeCursor);
|
27
|
+
tree_cursor_t *ptr = unwrap(res);
|
28
|
+
ptr->data = ts_tree_cursor_copy(&SELF);
|
29
|
+
return res;
|
30
30
|
}
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
/**
|
33
|
+
* Get the depth of the cursor's current node relative to the original
|
34
|
+
* node that the cursor was constructed with.
|
35
|
+
*
|
36
|
+
* @return [Integer]
|
37
|
+
*/
|
38
|
+
static VALUE tree_cursor_current_depth(VALUE self) {
|
39
|
+
return UINT2NUM(ts_tree_cursor_current_depth(&SELF));
|
35
40
|
}
|
36
41
|
|
37
|
-
|
38
|
-
|
42
|
+
/**
|
43
|
+
* Get the index of the cursor's current node out of all of the
|
44
|
+
* descendants of the original node that the cursor was constructed with.
|
45
|
+
*
|
46
|
+
* @return [Integer]
|
47
|
+
*/
|
48
|
+
static VALUE tree_cursor_current_descendant_index(VALUE self) {
|
49
|
+
return UINT2NUM(ts_tree_cursor_current_descendant_index(&SELF));
|
39
50
|
}
|
40
51
|
|
52
|
+
/**
|
53
|
+
* Get the field id of the tree cursor's current node.
|
54
|
+
*
|
55
|
+
* This returns zero if the current node doesn't have a field.
|
56
|
+
*
|
57
|
+
* @see Node#child_by_field_id
|
58
|
+
* @see Node#field_id_for_name
|
59
|
+
*
|
60
|
+
* @return [Integer]
|
61
|
+
*/
|
41
62
|
static VALUE tree_cursor_current_field_id(VALUE self) {
|
42
63
|
return UINT2NUM(ts_tree_cursor_current_field_id(&SELF));
|
43
64
|
}
|
44
65
|
|
45
|
-
|
46
|
-
|
66
|
+
/**
|
67
|
+
* Get the field name of the tree cursor's current node.
|
68
|
+
*
|
69
|
+
* This returns +nil+ if the current node doesn't have a field.
|
70
|
+
*
|
71
|
+
* @see Node#child_by_field_name
|
72
|
+
*
|
73
|
+
* @return [String]
|
74
|
+
*/
|
75
|
+
static VALUE tree_cursor_current_field_name(VALUE self) {
|
76
|
+
return safe_str(ts_tree_cursor_current_field_name(&SELF));
|
47
77
|
}
|
48
78
|
|
49
|
-
|
50
|
-
|
79
|
+
/**
|
80
|
+
* Get the tree cursor's current node.
|
81
|
+
*
|
82
|
+
* @return [Node]
|
83
|
+
*/
|
84
|
+
static VALUE tree_cursor_current_node(VALUE self) {
|
85
|
+
TSNode node = ts_tree_cursor_current_node(&SELF);
|
86
|
+
return new_node(&node);
|
87
|
+
}
|
88
|
+
|
89
|
+
/**
|
90
|
+
* Move the cursor to the node that is the nth descendant of
|
91
|
+
* the original node that the cursor was constructed with, where
|
92
|
+
* zero represents the original node itself.
|
93
|
+
*
|
94
|
+
* @return [nil]
|
95
|
+
*/
|
96
|
+
static VALUE tree_cursor_goto_descendant(VALUE self, VALUE descendant_idx) {
|
97
|
+
uint32_t idx = NUM2UINT(descendant_idx);
|
98
|
+
ts_tree_cursor_goto_descendant(&SELF, idx);
|
99
|
+
return Qnil;
|
51
100
|
}
|
52
101
|
|
102
|
+
/**
|
103
|
+
* Move the cursor to the first child of its current node.
|
104
|
+
*
|
105
|
+
* This returns +true+ if the cursor successfully moved, and returns +false+
|
106
|
+
* if there were no children.
|
107
|
+
*
|
108
|
+
* @return [Boolean]
|
109
|
+
*/
|
53
110
|
static VALUE tree_cursor_goto_first_child(VALUE self) {
|
54
111
|
return ts_tree_cursor_goto_first_child(&SELF) ? Qtrue : Qfalse;
|
55
112
|
}
|
56
113
|
|
114
|
+
/**
|
115
|
+
* Move the cursor to the first child of its current node that extends beyond
|
116
|
+
* the given byte offset.
|
117
|
+
*
|
118
|
+
* This returns the index of the child node if one was found, and returns -1
|
119
|
+
* if no such child was found.
|
120
|
+
*
|
121
|
+
* @return [Integer]
|
122
|
+
*/
|
57
123
|
static VALUE tree_cursor_goto_first_child_for_byte(VALUE self, VALUE byte) {
|
58
124
|
return LL2NUM(
|
59
125
|
ts_tree_cursor_goto_first_child_for_byte(&SELF, NUM2UINT(byte)));
|
60
126
|
}
|
61
127
|
|
128
|
+
/**
|
129
|
+
* Move the cursor to the first child of its current node that extends beyond
|
130
|
+
* the given or point.
|
131
|
+
*
|
132
|
+
* This returns the index of the child node if one was found, and returns -1
|
133
|
+
* if no such child was found.
|
134
|
+
*
|
135
|
+
* @return [Integer]
|
136
|
+
*/
|
62
137
|
static VALUE tree_cursor_goto_first_child_for_point(VALUE self, VALUE point) {
|
63
138
|
return LL2NUM(
|
64
139
|
ts_tree_cursor_goto_first_child_for_point(&SELF, value_to_point(point)));
|
65
140
|
}
|
66
141
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
142
|
+
/**
|
143
|
+
* Move the cursor to the last child of its current node.
|
144
|
+
*
|
145
|
+
* This returns +true+ if the cursor successfully moved, and returns +false+ if
|
146
|
+
* there were no children.
|
147
|
+
*
|
148
|
+
* Note that this function may be slower than {#goto_first_child}
|
149
|
+
* because it needs to iterate through all the children to compute the child's
|
150
|
+
* position.
|
151
|
+
*/
|
152
|
+
static VALUE tree_cursor_goto_last_child(VALUE self) {
|
153
|
+
return ts_tree_cursor_goto_last_child(&SELF) ? Qtrue : Qfalse;
|
154
|
+
}
|
155
|
+
|
156
|
+
/**
|
157
|
+
* Move the cursor to the next sibling of its current node.
|
158
|
+
*
|
159
|
+
* This returns +true+ if the cursor successfully moved, and returns +false+
|
160
|
+
* if there was no next sibling node.
|
161
|
+
*
|
162
|
+
* @return Boolean
|
163
|
+
*/
|
164
|
+
static VALUE tree_cursor_goto_next_sibling(VALUE self) {
|
165
|
+
return ts_tree_cursor_goto_next_sibling(&SELF) ? Qtrue : Qfalse;
|
166
|
+
}
|
167
|
+
|
168
|
+
/**
|
169
|
+
* Move the cursor to the parent of its current node.
|
170
|
+
*
|
171
|
+
* This returns +true+ if the cursor successfully moved, and returns +false+
|
172
|
+
* if there was no parent node (the cursor was already on the root node).
|
173
|
+
*
|
174
|
+
* @return [Boolean]
|
175
|
+
*/
|
176
|
+
static VALUE tree_cursor_goto_parent(VALUE self) {
|
177
|
+
return ts_tree_cursor_goto_parent(&SELF) ? Qtrue : Qfalse;
|
178
|
+
}
|
179
|
+
|
180
|
+
/**
|
181
|
+
* Move the cursor to the previous sibling of its current node.
|
182
|
+
*
|
183
|
+
* This returns +true+ if the cursor successfully moved, and returns +false+ if
|
184
|
+
* there was no previous sibling node.
|
185
|
+
*
|
186
|
+
* Note, that this function may be slower than
|
187
|
+
* {#goto_next_sibling} due to how node positions are stored. In
|
188
|
+
* the worst case, this will need to iterate through all the children upto the
|
189
|
+
* previous sibling node to recalculate its position.
|
190
|
+
*
|
191
|
+
* @return [Boolean]
|
192
|
+
*/
|
193
|
+
static VALUE tree_cursor_goto_previous_sibling(VALUE self) {
|
194
|
+
return ts_tree_cursor_goto_previous_sibling(&SELF) ? Qtrue : Qfalse;
|
195
|
+
}
|
196
|
+
|
197
|
+
/**
|
198
|
+
* Create a new tree cursor starting from the given node.
|
199
|
+
*
|
200
|
+
* A tree cursor allows you to walk a syntax tree more efficiently than is
|
201
|
+
* possible using the {Node} functions. It is a mutable object that is always
|
202
|
+
* on a certain syntax node, and can be moved imperatively to different nodes.
|
203
|
+
*
|
204
|
+
* @return [TreeCursor]
|
205
|
+
*/
|
206
|
+
static VALUE tree_cursor_initialize(VALUE self, VALUE node) {
|
207
|
+
TSNode n = value_to_node(node);
|
208
|
+
tree_cursor_t *ptr = unwrap(self);
|
209
|
+
ptr->data = ts_tree_cursor_new(n);
|
210
|
+
return self;
|
211
|
+
}
|
212
|
+
|
213
|
+
/**
|
214
|
+
* Re-initialize a tree cursor to start at a different node.
|
215
|
+
*
|
216
|
+
* @return [nil]
|
217
|
+
*/
|
218
|
+
static VALUE tree_cursor_reset(VALUE self, VALUE node) {
|
219
|
+
ts_tree_cursor_reset(&SELF, value_to_node(node));
|
220
|
+
return Qnil;
|
221
|
+
}
|
222
|
+
|
223
|
+
/**
|
224
|
+
* Re-initialize a tree cursor to the same position as another cursor.
|
225
|
+
*
|
226
|
+
* Unlike {#reset}, this will not lose parent information and allows reusing
|
227
|
+
* already created cursors.
|
228
|
+
*
|
229
|
+
* @return [nil]
|
230
|
+
*/
|
231
|
+
VALUE tree_cursor_reset_to(VALUE self, VALUE src) {
|
232
|
+
ts_tree_cursor_reset_to(&SELF, &unwrap(src)->data);
|
233
|
+
return Qnil;
|
72
234
|
}
|
73
235
|
|
74
236
|
void init_tree_cursor(void) {
|
@@ -77,21 +239,31 @@ void init_tree_cursor(void) {
|
|
77
239
|
rb_define_alloc_func(cTreeCursor, tree_cursor_allocate);
|
78
240
|
|
79
241
|
/* Class methods */
|
80
|
-
rb_define_method(cTreeCursor, "
|
81
|
-
rb_define_method(cTreeCursor, "
|
82
|
-
rb_define_method(cTreeCursor, "
|
83
|
-
|
84
|
-
tree_cursor_current_field_name, 0);
|
242
|
+
rb_define_method(cTreeCursor, "copy", tree_cursor_copy, 0);
|
243
|
+
rb_define_method(cTreeCursor, "current_depth", tree_cursor_current_depth, 0);
|
244
|
+
rb_define_method(cTreeCursor, "current_descendant_index",
|
245
|
+
tree_cursor_current_descendant_index, 0);
|
85
246
|
rb_define_method(cTreeCursor, "current_field_id",
|
86
247
|
tree_cursor_current_field_id, 0);
|
87
|
-
rb_define_method(cTreeCursor, "
|
88
|
-
|
89
|
-
|
248
|
+
rb_define_method(cTreeCursor, "current_field_name",
|
249
|
+
tree_cursor_current_field_name, 0);
|
250
|
+
rb_define_method(cTreeCursor, "current_node", tree_cursor_current_node, 0);
|
251
|
+
rb_define_method(cTreeCursor, "goto_descendant", tree_cursor_goto_descendant,
|
252
|
+
1);
|
90
253
|
rb_define_method(cTreeCursor, "goto_first_child",
|
91
254
|
tree_cursor_goto_first_child, 0);
|
92
255
|
rb_define_method(cTreeCursor, "goto_first_child_for_byte",
|
93
256
|
tree_cursor_goto_first_child_for_byte, 1);
|
94
257
|
rb_define_method(cTreeCursor, "goto_first_child_for_point",
|
95
258
|
tree_cursor_goto_first_child_for_point, 1);
|
96
|
-
rb_define_method(cTreeCursor, "
|
259
|
+
rb_define_method(cTreeCursor, "goto_last_child", tree_cursor_goto_last_child,
|
260
|
+
0);
|
261
|
+
rb_define_method(cTreeCursor, "goto_next_sibling",
|
262
|
+
tree_cursor_goto_next_sibling, 0);
|
263
|
+
rb_define_method(cTreeCursor, "goto_parent", tree_cursor_goto_parent, 0);
|
264
|
+
rb_define_method(cTreeCursor, "goto_previous_sibling",
|
265
|
+
tree_cursor_goto_previous_sibling, 0);
|
266
|
+
rb_define_method(cTreeCursor, "initialize", tree_cursor_initialize, 1);
|
267
|
+
rb_define_method(cTreeCursor, "reset", tree_cursor_reset, 1);
|
268
|
+
rb_define_method(cTreeCursor, "reset_to", tree_cursor_reset_to, 1);
|
97
269
|
}
|
@@ -5,8 +5,20 @@ VALUE mTreeSitter;
|
|
5
5
|
void Init_tree_sitter() {
|
6
6
|
mTreeSitter = rb_define_module("TreeSitter");
|
7
7
|
|
8
|
+
/**
|
9
|
+
* The latest ABI version that is supported by the current version of the
|
10
|
+
* library. When Languages are generated by the Tree-sitter CLI, they are
|
11
|
+
* assigned an ABI version number that corresponds to the current CLI version.
|
12
|
+
* The Tree-sitter library is generally backwards-compatible with languages
|
13
|
+
* generated using older CLI versions, but is not forwards-compatible.
|
14
|
+
*/
|
8
15
|
rb_define_const(mTreeSitter, "LANGUAGE_VERSION",
|
9
16
|
INT2NUM(TREE_SITTER_LANGUAGE_VERSION));
|
17
|
+
|
18
|
+
/**
|
19
|
+
* The earliest ABI version that is supported by the current version of the
|
20
|
+
* library.
|
21
|
+
*/
|
10
22
|
rb_define_const(mTreeSitter, "MIN_COMPATIBLE_LANGUAGE_VERSION",
|
11
23
|
TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION);
|
12
24
|
|
data/lib/tree_sitter/node.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module TreeSitter
|
4
|
+
# Node is a wrapper around a tree-sitter node.
|
4
5
|
class Node
|
6
|
+
# @return [Array<Symbol>] the node's named fields
|
5
7
|
def fields
|
6
8
|
return @fields if @fields
|
7
9
|
|
@@ -18,6 +20,18 @@ module TreeSitter
|
|
18
20
|
fields.include?(field)
|
19
21
|
end
|
20
22
|
|
23
|
+
# FIXME: These APIs (`[]` and `fetch`) need absolute fixing.
|
24
|
+
# 1. The documentation with the table doesn't work.
|
25
|
+
# 1. The APIs are very confusing! Make them act similarly to Hash's
|
26
|
+
# `fetch` and `[]`.
|
27
|
+
# 1. `[]` should take a single input and return nil if nothing found
|
28
|
+
# (no exceptions).
|
29
|
+
# 1. `fetch` should should accept a single argument, potentially a
|
30
|
+
# default, and raise exception if no default was provided.
|
31
|
+
# Also allow for the `all:` kwarg.
|
32
|
+
# 1. `values_at` takes many arguments.
|
33
|
+
# And I don't think we can move to 1.0 without adressing them.
|
34
|
+
#
|
21
35
|
# Access node's named children.
|
22
36
|
#
|
23
37
|
# It's similar to {#fetch}, but differes in input type, return values, and
|
@@ -49,13 +63,11 @@ module TreeSitter
|
|
49
63
|
when 0 then raise "#{self.class.name}##{__method__} requires a key."
|
50
64
|
when 1
|
51
65
|
case k = keys.first
|
52
|
-
when Numeric
|
66
|
+
when Numeric then named_child(k)
|
53
67
|
when String, Symbol
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
raise "Cannot find field #{k}"
|
58
|
-
end
|
68
|
+
raise "Cannot find field #{k}" unless fields.include?(k.to_sym)
|
69
|
+
|
70
|
+
child_by_field_name(k.to_s)
|
59
71
|
else raise <<~ERR
|
60
72
|
#{self.class.name}##{__method__} accepts Integer and returns named child at given index,
|
61
73
|
or a (String | Symbol) and returns the child by given field name.
|
@@ -66,6 +78,8 @@ module TreeSitter
|
|
66
78
|
end
|
67
79
|
end
|
68
80
|
|
81
|
+
# @!visibility private
|
82
|
+
#
|
69
83
|
# Allows access to child_by_field_name without using [].
|
70
84
|
def method_missing(method_name, *_args, &_block)
|
71
85
|
if fields.include?(method_name)
|
@@ -75,6 +89,8 @@ module TreeSitter
|
|
75
89
|
end
|
76
90
|
end
|
77
91
|
|
92
|
+
# @!visibility private
|
93
|
+
#
|
78
94
|
def respond_to_missing?(*args)
|
79
95
|
args.length == 1 && fields.include?(args[0])
|
80
96
|
end
|
@@ -116,6 +132,7 @@ module TreeSitter
|
|
116
132
|
end
|
117
133
|
end
|
118
134
|
|
135
|
+
# @return [Array<TreeSitter::Node>] all the node's children
|
119
136
|
def to_a
|
120
137
|
each.to_a
|
121
138
|
end
|
@@ -142,7 +159,12 @@ module TreeSitter
|
|
142
159
|
# uses named_child | field_name_for_child
|
143
160
|
# child_by_field_name | via each_node
|
144
161
|
# ------------------------------+----------------------
|
145
|
-
|
162
|
+
# @param all [Boolean] If `true`, return an array of nodes for all the
|
163
|
+
# demanded keys, putting `nil` for missing ones. If `false`, return the
|
164
|
+
# same array after calling `compact`. Defaults to `false`.
|
165
|
+
#
|
166
|
+
# See {#fetch_all}.
|
167
|
+
def fetch(*keys, all: false, **_kwargs)
|
146
168
|
dict = {}
|
147
169
|
keys.each.with_index do |k, i|
|
148
170
|
dict[k.to_s] = i
|
@@ -151,13 +173,25 @@ module TreeSitter
|
|
151
173
|
res = {}
|
152
174
|
each_field do |f, c|
|
153
175
|
if dict.key?(f)
|
154
|
-
res[
|
176
|
+
res[f] = c
|
155
177
|
dict.delete(f)
|
156
178
|
end
|
157
179
|
break if dict.empty?
|
158
180
|
end
|
159
181
|
|
160
|
-
res.
|
182
|
+
res = keys.uniq.map { |k| res[k.to_s] }
|
183
|
+
res = res.compact if !all
|
184
|
+
res
|
185
|
+
end
|
186
|
+
|
187
|
+
# Access all named children of a node, returning `nil` for missing ones.
|
188
|
+
#
|
189
|
+
# Equivalent to `fetch(…, all: true)`.
|
190
|
+
#
|
191
|
+
# See {#fetch}.
|
192
|
+
def fetch_all(*keys, **kwargs)
|
193
|
+
kwargs[:all] = true
|
194
|
+
fetch(*keys, **kwargs)
|
161
195
|
end
|
162
196
|
end
|
163
197
|
end
|
Binary file
|
data/lib/tree_sitter/version.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module TreeSitter
|
4
|
-
|
5
|
-
|
4
|
+
# The version of the tree-sitter library.
|
5
|
+
TREESITTER_VERSION = '0.20.9'
|
6
|
+
# The current version of the gem.
|
7
|
+
VERSION = '1.0.0'
|
6
8
|
end
|
data/lib/tree_sitter.rb
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module TreeStand
|
5
|
+
# An experimental class to modify the AST. It re-runs the query on the
|
6
|
+
# modified document every loop to ensure that the match is still valid.
|
7
|
+
# @see TreeStand::Tree
|
8
|
+
# @api experimental
|
9
|
+
class AstModifier
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
sig { params(tree: TreeStand::Tree).void }
|
13
|
+
def initialize(tree)
|
14
|
+
@tree = tree
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param query [String]
|
18
|
+
# @yieldparam self [self]
|
19
|
+
# @yieldparam match [TreeStand::Match]
|
20
|
+
# @return [void]
|
21
|
+
def on_match(query)
|
22
|
+
matches = @tree.query(query)
|
23
|
+
|
24
|
+
while !matches.empty?
|
25
|
+
yield self, matches.first
|
26
|
+
matches = @tree.query(query)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# typed: true
|
3
|
+
|
4
|
+
module TreeStand
|
5
|
+
# Breadth-first traversal through the tree, calling hooks at each stop.
|
6
|
+
class BreadthFirstVisitor
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { params(node: TreeStand::Node).void }
|
10
|
+
def initialize(node)
|
11
|
+
@node = node
|
12
|
+
end
|
13
|
+
|
14
|
+
# Run the visitor on the document and return self. Allows chaining create and visit.
|
15
|
+
# @example
|
16
|
+
# visitor = CountingVisitor.new(node, :predicate).visit
|
17
|
+
sig { returns(T.self_type) }
|
18
|
+
def visit
|
19
|
+
queue = [@node]
|
20
|
+
visit_node(queue) while queue.any?
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
# @abstract The default implementation does nothing.
|
25
|
+
sig { overridable.params(node: TreeStand::Node).void }
|
26
|
+
def on(node) = nil
|
27
|
+
|
28
|
+
# @abstract The default implementation yields to visit all children.
|
29
|
+
sig { overridable.params(node: TreeStand::Node, block: T.proc.void).void }
|
30
|
+
def around(node, &block) = yield
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def visit_node(queue)
|
35
|
+
node = queue.shift
|
36
|
+
|
37
|
+
if respond_to?("on_#{node.type}")
|
38
|
+
public_send("on_#{node.type}", node)
|
39
|
+
else
|
40
|
+
on(node)
|
41
|
+
end
|
42
|
+
|
43
|
+
if respond_to?("around_#{node.type}")
|
44
|
+
public_send("around_#{node.type}", node) do
|
45
|
+
node.each { |child| queue << child }
|
46
|
+
end
|
47
|
+
else
|
48
|
+
around(node) do
|
49
|
+
node.each { |child| queue << child }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|