ruby_tree_sitter 1.1.0-arm64-darwin-22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +199 -0
  4. data/ext/tree_sitter/encoding.c +29 -0
  5. data/ext/tree_sitter/extconf.rb +149 -0
  6. data/ext/tree_sitter/input.c +127 -0
  7. data/ext/tree_sitter/input_edit.c +42 -0
  8. data/ext/tree_sitter/language.c +219 -0
  9. data/ext/tree_sitter/logger.c +228 -0
  10. data/ext/tree_sitter/macros.h +163 -0
  11. data/ext/tree_sitter/node.c +618 -0
  12. data/ext/tree_sitter/parser.c +398 -0
  13. data/ext/tree_sitter/point.c +26 -0
  14. data/ext/tree_sitter/quantifier.c +43 -0
  15. data/ext/tree_sitter/query.c +282 -0
  16. data/ext/tree_sitter/query_capture.c +28 -0
  17. data/ext/tree_sitter/query_cursor.c +215 -0
  18. data/ext/tree_sitter/query_error.c +41 -0
  19. data/ext/tree_sitter/query_match.c +44 -0
  20. data/ext/tree_sitter/query_predicate_step.c +83 -0
  21. data/ext/tree_sitter/range.c +35 -0
  22. data/ext/tree_sitter/repo.rb +121 -0
  23. data/ext/tree_sitter/symbol_type.c +46 -0
  24. data/ext/tree_sitter/tree.c +234 -0
  25. data/ext/tree_sitter/tree_cursor.c +269 -0
  26. data/ext/tree_sitter/tree_sitter.c +44 -0
  27. data/ext/tree_sitter/tree_sitter.h +107 -0
  28. data/lib/tree_sitter/node.rb +197 -0
  29. data/lib/tree_sitter/tree_sitter.bundle +0 -0
  30. data/lib/tree_sitter/version.rb +8 -0
  31. data/lib/tree_sitter.rb +14 -0
  32. data/lib/tree_stand/ast_modifier.rb +30 -0
  33. data/lib/tree_stand/breadth_first_visitor.rb +54 -0
  34. data/lib/tree_stand/config.rb +13 -0
  35. data/lib/tree_stand/node.rb +224 -0
  36. data/lib/tree_stand/parser.rb +67 -0
  37. data/lib/tree_stand/range.rb +55 -0
  38. data/lib/tree_stand/tree.rb +123 -0
  39. data/lib/tree_stand/utils/printer.rb +73 -0
  40. data/lib/tree_stand/version.rb +7 -0
  41. data/lib/tree_stand/visitor.rb +127 -0
  42. data/lib/tree_stand/visitors/tree_walker.rb +37 -0
  43. data/lib/tree_stand.rb +48 -0
  44. data/tree_sitter.gemspec +35 -0
  45. metadata +124 -0
@@ -0,0 +1,44 @@
1
+ #include "tree_sitter.h"
2
+
3
+ VALUE mTreeSitter;
4
+
5
+ void Init_tree_sitter() {
6
+ mTreeSitter = rb_define_module("TreeSitter");
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
+ */
15
+ rb_define_const(mTreeSitter, "LANGUAGE_VERSION",
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
+ */
22
+ rb_define_const(mTreeSitter, "MIN_COMPATIBLE_LANGUAGE_VERSION",
23
+ TREE_SITTER_MIN_COMPATIBLE_LANGUAGE_VERSION);
24
+
25
+ init_encoding();
26
+ init_input();
27
+ init_input_edit();
28
+ init_language();
29
+ init_logger();
30
+ init_node();
31
+ init_parser();
32
+ init_point();
33
+ init_quantifier();
34
+ init_query();
35
+ init_query_capture();
36
+ init_query_cursor();
37
+ init_query_error();
38
+ init_query_match();
39
+ init_query_predicate_step();
40
+ init_range();
41
+ init_symbol_type();
42
+ init_tree();
43
+ init_tree_cursor();
44
+ }
@@ -0,0 +1,107 @@
1
+ #ifndef _RB_TREE_SITTER_H
2
+ #define _RB_TREE_SITTER_H
3
+
4
+ #include "macros.h"
5
+ #include <dlfcn.h>
6
+ #include <fcntl.h>
7
+ #include <ruby.h>
8
+ #include <stdio.h>
9
+ #include <string.h>
10
+ #include <tree_sitter/api.h>
11
+
12
+ static inline VALUE safe_str(const char *str) {
13
+ if (str == NULL) {
14
+ return Qnil;
15
+ } else {
16
+ return rb_utf8_str_new_cstr(str);
17
+ }
18
+ }
19
+
20
+ static inline VALUE safe_str2(const char *str, uint32_t len) {
21
+ if (str == NULL) {
22
+ return Qnil;
23
+ } else if (len == 0) {
24
+ return rb_utf8_str_new_cstr("");
25
+ } else {
26
+ return rb_utf8_str_new(str, len);
27
+ }
28
+ }
29
+
30
+ static inline VALUE safe_symbol(const char *str) {
31
+ if (str == NULL) {
32
+ return Qnil;
33
+ } else {
34
+ return ID2SYM(rb_intern(str));
35
+ }
36
+ }
37
+
38
+ // VALUE to TS* converters
39
+
40
+ TSInput value_to_input(VALUE);
41
+ TSInputEdit value_to_input_edit(VALUE);
42
+ TSInputEncoding value_to_encoding(VALUE);
43
+ TSLanguage *value_to_language(VALUE);
44
+ TSLogger value_to_logger(VALUE);
45
+ TSNode value_to_node(VALUE);
46
+ TSPoint value_to_point(VALUE);
47
+ TSQuantifier value_to_quantifier(VALUE);
48
+ TSQuery *value_to_query(VALUE);
49
+ TSQueryCursor *value_to_query_cursor(VALUE);
50
+ TSQueryError value_to_query_error(VALUE);
51
+ TSQueryMatch value_to_query_match(VALUE);
52
+ TSQueryPredicateStep value_to_query_predicate_step(VALUE);
53
+ TSQueryPredicateStepType value_to_query_predicate_step_type(VALUE);
54
+ TSRange value_to_range(VALUE);
55
+ TSSymbolType value_to_symbol_type(VALUE);
56
+ TSTree *value_to_tree(VALUE);
57
+ TSTreeCursor value_to_tree_cursor(VALUE);
58
+
59
+ // TS* to VALUE converters
60
+ VALUE new_input(const TSInput *);
61
+ VALUE new_language(const TSLanguage *);
62
+ VALUE new_logger(const TSLogger *);
63
+ VALUE new_logger_by_val(TSLogger);
64
+ VALUE new_node(const TSNode *);
65
+ VALUE new_node_by_val(TSNode);
66
+ VALUE new_point(const TSPoint *);
67
+ VALUE new_point_by_val(TSPoint);
68
+ VALUE new_query_capture(const TSQueryCapture *);
69
+ VALUE new_query_match(const TSQueryMatch *);
70
+ VALUE new_query_predicate_step(const TSQueryPredicateStep *);
71
+ VALUE new_range(const TSRange *);
72
+ VALUE new_symbol_type(TSSymbolType);
73
+ VALUE new_tree(TSTree *);
74
+
75
+ // All init_* functions are called from Init_tree_sitter
76
+ void init_encoding(void);
77
+ void init_input(void);
78
+ void init_input_edit(void);
79
+ void init_language(void);
80
+ void init_logger(void);
81
+ void init_node(void);
82
+ void init_parser(void);
83
+ void init_point(void);
84
+ void init_quantifier(void);
85
+ void init_query(void);
86
+ void init_query_capture(void);
87
+ void init_query_cursor(void);
88
+ void init_query_error(void);
89
+ void init_query_match(void);
90
+ void init_query_predicate_step(void);
91
+ void init_range(void);
92
+ void init_symbol_type(void);
93
+ void init_tree(void);
94
+ void init_tree_cursor(void);
95
+
96
+ // Other helpers
97
+ const char *quantifier_str(TSQuantifier);
98
+ const char *query_error_str(TSQueryError);
99
+
100
+ // TSTree reference counting
101
+ int tree_rc_free(const TSTree *);
102
+ void tree_rc_new(const TSTree *);
103
+
104
+ // This is a special entry-point for the extension
105
+ void Init_tree_sitter(void);
106
+
107
+ #endif
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TreeSitter
4
+ # Node is a wrapper around a tree-sitter node.
5
+ class Node
6
+ # @return [Array<Symbol>] the node's named fields
7
+ def fields
8
+ return @fields if @fields
9
+
10
+ @fields = Set.new
11
+ child_count.times do |i|
12
+ name = field_name_for_child(i)
13
+ @fields << name.to_sym if name
14
+ end
15
+
16
+ @fields
17
+ end
18
+
19
+ def field?(field)
20
+ fields.include?(field)
21
+ end
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
+ #
35
+ # Access node's named children.
36
+ #
37
+ # It's similar to {#fetch}, but differes in input type, return values, and
38
+ # the internal implementation.
39
+ #
40
+ # Both of these methods exist for separate use cases, but also because
41
+ # sometime tree-sitter does some monkey business and having both separate
42
+ # implementations can help.
43
+ #
44
+ # Comparison with {#fetch}:
45
+ #
46
+ # [] | fetch
47
+ # ------------------------------+----------------------
48
+ # input types Integer, String, Symbol | Array<String, Symbol>
49
+ # Array<Integer, String, Symbol>|
50
+ # ------------------------------+----------------------
51
+ # returns 1-to-1 correspondance with | unique nodes
52
+ # input |
53
+ # ------------------------------+----------------------
54
+ # uses named_child | field_name_for_child
55
+ # child_by_field_name | via each_node
56
+ # ------------------------------+----------------------
57
+ #
58
+ # @param keys [Integer | String | Symbol | Array<Integer, String, Symbol>, #read]
59
+ #
60
+ # @return [Node | Array<Node>]
61
+ def [](*keys)
62
+ case keys.length
63
+ when 0 then raise "#{self.class.name}##{__method__} requires a key."
64
+ when 1
65
+ case k = keys.first
66
+ when Numeric then named_child(k)
67
+ when String, Symbol
68
+ raise "Cannot find field #{k}" unless fields.include?(k.to_sym)
69
+
70
+ child_by_field_name(k.to_s)
71
+ else raise <<~ERR
72
+ #{self.class.name}##{__method__} accepts Integer and returns named child at given index,
73
+ or a (String | Symbol) and returns the child by given field name.
74
+ ERR
75
+ end
76
+ else
77
+ keys.map { |key| self[key] }
78
+ end
79
+ end
80
+
81
+ # @!visibility private
82
+ #
83
+ # Allows access to child_by_field_name without using [].
84
+ def method_missing(method_name, *_args, &_block)
85
+ if fields.include?(method_name)
86
+ child_by_field_name(method_name.to_s)
87
+ else
88
+ super
89
+ end
90
+ end
91
+
92
+ # @!visibility private
93
+ #
94
+ def respond_to_missing?(*args)
95
+ args.length == 1 && fields.include?(args[0])
96
+ end
97
+
98
+ # Iterate over a node's children.
99
+ #
100
+ # @yieldparam child [Node] the child
101
+ def each
102
+ return enum_for __method__ if !block_given?
103
+
104
+ (0...(child_count)).each do |i|
105
+ yield child(i)
106
+ end
107
+ end
108
+
109
+ # Iterate over a node's children assigned to a field.
110
+ #
111
+ # @yieldparam name [NilClass | String] field name.
112
+ # @yieldparam child [Node] the child.
113
+ def each_field
114
+ return enum_for __method__ if !block_given?
115
+
116
+ each.with_index do |c, i|
117
+ f = field_name_for_child(i)
118
+ next if f.nil? || f.empty?
119
+
120
+ yield f, c
121
+ end
122
+ end
123
+
124
+ # Iterate over a node's named children
125
+ #
126
+ # @yieldparam child [Node] the child
127
+ def each_named
128
+ return enum_for __method__ if !block_given?
129
+
130
+ (0...(named_child_count)).each do |i|
131
+ yield named_child(i)
132
+ end
133
+ end
134
+
135
+ # @return [Array<TreeSitter::Node>] all the node's children
136
+ def to_a
137
+ each.to_a
138
+ end
139
+
140
+ # Access node's named children.
141
+ #
142
+ # It's similar to {#fetch}, but differes in input type, return values, and
143
+ # the internal implementation.
144
+ #
145
+ # Both of these methods exist for separate use cases, but also because
146
+ # sometime tree-sitter does some monkey business and having both separate
147
+ # implementations can help.
148
+ #
149
+ # Comparison with {#fetch}:
150
+ #
151
+ # [] | fetch
152
+ # ------------------------------+----------------------
153
+ # input types Integer, String, Symbol | String, Symbol
154
+ # Array<Integer, String, Symbol>| Array<String, Symbol>
155
+ # ------------------------------+----------------------
156
+ # returns 1-to-1 correspondance with | unique nodes
157
+ # input |
158
+ # ------------------------------+----------------------
159
+ # uses named_child | field_name_for_child
160
+ # child_by_field_name | via each_node
161
+ # ------------------------------+----------------------
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)
168
+ dict = {}
169
+ keys.each.with_index do |k, i|
170
+ dict[k.to_s] = i
171
+ end
172
+
173
+ res = {}
174
+ each_field do |f, c|
175
+ if dict.key?(f)
176
+ res[f] = c
177
+ dict.delete(f)
178
+ end
179
+ break if dict.empty?
180
+ end
181
+
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)
195
+ end
196
+ end
197
+ end
Binary file
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TreeSitter
4
+ # The version of the tree-sitter library.
5
+ TREESITTER_VERSION = '0.22.6'
6
+ # The current version of the gem.
7
+ VERSION = '1.1.0'
8
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # TreeSitter is a Ruby interface to the tree-sitter parsing library.
4
+ module TreeSitter
5
+ end
6
+
7
+ require 'set'
8
+
9
+ require 'tree_sitter/version'
10
+
11
+ require 'tree_sitter/tree_sitter'
12
+ require 'tree_sitter/node'
13
+
14
+ ObjectSpace.define_finalizer(TreeSitter::Tree.class, proc { TreeSitter::Tree.finalizer })
@@ -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
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module TreeStand
5
+ # Global configuration for the gem.
6
+ # @api private
7
+ class Config
8
+ extend T::Sig
9
+
10
+ sig { returns(String) }
11
+ attr_accessor :parser_path
12
+ end
13
+ end
@@ -0,0 +1,224 @@
1
+ # frozen_string_literal: true
2
+ # typed: true
3
+
4
+ module TreeStand
5
+ # Wrapper around a TreeSitter node and provides convient
6
+ # methods that are missing on the original node. This class
7
+ # overrides the `method_missing` method to delegate to a nodes
8
+ # named children.
9
+ class Node
10
+ extend T::Sig
11
+ extend Forwardable
12
+ include Enumerable
13
+
14
+ # @!method type
15
+ # @return [Symbol] the type of the node in the tree-sitter grammar.
16
+ # @!method error?
17
+ # @return [bool] true if the node is an error node.
18
+ def_delegators(
19
+ :@ts_node,
20
+ :type,
21
+ :error?,
22
+ )
23
+
24
+ sig { returns(TreeStand::Tree) }
25
+ attr_reader :tree
26
+
27
+ sig { returns(TreeSitter::Node) }
28
+ attr_reader :ts_node
29
+
30
+ # @api private
31
+ sig { params(tree: TreeStand::Tree, ts_node: TreeSitter::Node).void }
32
+ def initialize(tree, ts_node)
33
+ @tree = tree
34
+ @ts_node = ts_node
35
+ @fields = @ts_node.each_field.to_a.map(&:first)
36
+ end
37
+
38
+ # TreeSitter uses a `TreeSitter::Cursor` to iterate over matches by calling
39
+ # `curser#next_match` repeatedly until it returns `nil`.
40
+ #
41
+ # This method does all of that for you and collects all of the matches into
42
+ # an array and each corresponding capture into a hash.
43
+ #
44
+ # @example
45
+ # # This will return a match for each identifier nodes in the tree.
46
+ # tree_matches = tree.query(<<~QUERY)
47
+ # (identifier) @identifier
48
+ # QUERY
49
+ #
50
+ # # It is equivalent to:
51
+ # tree.root_node.query(<<~QUERY)
52
+ # (identifier) @identifier
53
+ # QUERY
54
+ sig { params(query_string: String).returns(T::Array[T::Hash[String, TreeStand::Node]]) }
55
+ def query(query_string)
56
+ ts_query = TreeSitter::Query.new(@tree.parser.ts_language, query_string)
57
+ ts_cursor = TreeSitter::QueryCursor.exec(ts_query, ts_node)
58
+ matches = []
59
+ while ts_match = ts_cursor.next_match
60
+ captures = {}
61
+
62
+ ts_match.captures.each do |ts_capture|
63
+ capture_name = ts_query.capture_name_for_id(ts_capture.index)
64
+ captures[capture_name] = TreeStand::Node.new(@tree, ts_capture.node)
65
+ end
66
+
67
+ matches << captures
68
+ end
69
+ matches
70
+ end
71
+
72
+ # Returns the first captured node that matches the query string or nil if
73
+ # there was no captured node.
74
+ #
75
+ # @example Find the first identifier node.
76
+ # identifier_node = tree.root_node.find_node("(identifier) @identifier")
77
+ #
78
+ # @see #find_node!
79
+ # @see #query
80
+ sig { params(query_string: String).returns(T.nilable(TreeStand::Node)) }
81
+ def find_node(query_string)
82
+ query(query_string).first&.values&.first
83
+ end
84
+
85
+ # Like {#find_node}, except that if no node is found, raises an
86
+ # {TreeStand::NodeNotFound} error.
87
+ #
88
+ # @see #find_node
89
+ # @raise [TreeStand::NodeNotFound]
90
+ sig { params(query_string: String).returns(TreeStand::Node) }
91
+ def find_node!(query_string)
92
+ find_node(query_string) || raise(TreeStand::NodeNotFound)
93
+ end
94
+
95
+ sig { returns(TreeStand::Range) }
96
+ def range
97
+ TreeStand::Range.new(
98
+ start_byte: @ts_node.start_byte,
99
+ end_byte: @ts_node.end_byte,
100
+ start_point: @ts_node.start_point,
101
+ end_point: @ts_node.end_point,
102
+ )
103
+ end
104
+
105
+ # Node includes enumerable so that you can iterate over the child nodes.
106
+ # @example
107
+ # node.text # => "3 * 4"
108
+ #
109
+ # @example Iterate over the child nodes
110
+ # node.each do |child|
111
+ # print child.text
112
+ # end
113
+ # # prints: 3*4
114
+ #
115
+ # @example Enumerable methods
116
+ # node.map(&:text) # => ["3", "*", "4"]
117
+ #
118
+ # @yieldparam child [TreeStand::Node]
119
+ sig do
120
+ override
121
+ .params(block: T.nilable(T.proc.params(node: TreeStand::Node).returns(BasicObject)))
122
+ .returns(T::Enumerator[TreeStand::Node])
123
+ end
124
+ def each(&block)
125
+ enumerator = Enumerator.new do |yielder|
126
+ @ts_node.each do |child|
127
+ yielder << TreeStand::Node.new(@tree, child)
128
+ end
129
+ end
130
+ enumerator.each(&block) if block_given?
131
+ enumerator
132
+ end
133
+
134
+ # (see TreeStand::Visitors::TreeWalker)
135
+ # Backed by {TreeStand::Visitors::TreeWalker}.
136
+ #
137
+ # @example Check the subtree for error nodes
138
+ # node.walk.any? { |node| node.type == :error }
139
+ #
140
+ # @see TreeStand::Visitors::TreeWalker
141
+ #
142
+ # @yieldparam node [TreeStand::Node]
143
+ sig do
144
+ params(block: T.nilable(T.proc.params(node: TreeStand::Node).returns(BasicObject)))
145
+ .returns(T::Enumerator[TreeStand::Node])
146
+ end
147
+ def walk(&block)
148
+ enumerator = Enumerator.new do |yielder|
149
+ Visitors::TreeWalker.new(self) do |child|
150
+ yielder << child
151
+ end.visit
152
+ end
153
+ enumerator.each(&block) if block_given?
154
+ enumerator
155
+ end
156
+
157
+ # @example
158
+ # node.text # => "4"
159
+ # node.parent.text # => "3 * 4"
160
+ # node.parent.parent.text # => "1 + 3 * 4"
161
+ sig { returns(TreeStand::Node) }
162
+ def parent
163
+ TreeStand::Node.new(@tree, @ts_node.parent)
164
+ end
165
+
166
+ # @example
167
+ # node.text # => "3 * 4"
168
+ # node.to_a.map(&:text) # => ["3", "*", "4"]
169
+ # node.children.map(&:text) # => ["3", "*", "4"]
170
+ sig { returns(T::Array[TreeStand::Node]) }
171
+ def children = to_a
172
+
173
+ # A convenience method for getting the text of the node. Each {TreeStand::Node}
174
+ # wraps the parent {TreeStand::Tree #tree} and has access to the source document.
175
+ sig { returns(String) }
176
+ def text
177
+ T.must(@tree.document[@ts_node.start_byte...@ts_node.end_byte])
178
+ end
179
+
180
+ # This class overrides the `method_missing` method to delegate to the
181
+ # node's named children.
182
+ # @example
183
+ # node.text # => "3 * 4"
184
+ #
185
+ # node.left.text # => "3"
186
+ # node.operator.text # => "*"
187
+ # node.right.text # => "4"
188
+ # node.operand # => NoMethodError
189
+ # @overload method_missing(field_name)
190
+ # @param name [Symbol, String]
191
+ # @return [TreeStand::Node] child node for the given field name
192
+ # @raise [NoMethodError] Raised if the node does not have child with name `field_name`
193
+ #
194
+ # @overload method_missing(method_name, *args, &block)
195
+ # @raise [NoMethodError]
196
+ def method_missing(method, *args, &block)
197
+ return super unless @fields.include?(method.to_s)
198
+
199
+ TreeStand::Node.new(@tree, T.unsafe(@ts_node).public_send(method, *args, &block))
200
+ end
201
+
202
+ sig { params(other: Object).returns(T::Boolean) }
203
+ def ==(other)
204
+ return false unless other.is_a?(TreeStand::Node)
205
+
206
+ T.must(range == other.range && type == other.type && text == other.text)
207
+ end
208
+
209
+ # (see TreeStand::Utils::Printer)
210
+ # Backed by {TreeStand::Utils::Printer}.
211
+ #
212
+ # @see TreeStand::Utils::Printer
213
+ sig { params(pp: PP).void }
214
+ def pretty_print(pp)
215
+ Utils::Printer.new(ralign: 80).print(self, io: pp.output)
216
+ end
217
+
218
+ private
219
+
220
+ def respond_to_missing?(method, *)
221
+ @fields.include?(method.to_s) || super
222
+ end
223
+ end
224
+ end