ruby_tree_sitter 1.6.0-x86_64-linux-gnu

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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +213 -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 +623 -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 +289 -0
  16. data/ext/tree_sitter/query_capture.c +28 -0
  17. data/ext/tree_sitter/query_cursor.c +231 -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 +128 -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/3.0/tree_sitter.so +0 -0
  29. data/lib/tree_sitter/3.1/tree_sitter.so +0 -0
  30. data/lib/tree_sitter/3.2/tree_sitter.so +0 -0
  31. data/lib/tree_sitter/3.3/tree_sitter.so +0 -0
  32. data/lib/tree_sitter/helpers.rb +23 -0
  33. data/lib/tree_sitter/mixins/language.rb +167 -0
  34. data/lib/tree_sitter/node.rb +167 -0
  35. data/lib/tree_sitter/query.rb +191 -0
  36. data/lib/tree_sitter/query_captures.rb +30 -0
  37. data/lib/tree_sitter/query_cursor.rb +27 -0
  38. data/lib/tree_sitter/query_match.rb +100 -0
  39. data/lib/tree_sitter/query_matches.rb +39 -0
  40. data/lib/tree_sitter/query_predicate.rb +14 -0
  41. data/lib/tree_sitter/text_predicate_capture.rb +37 -0
  42. data/lib/tree_sitter/version.rb +8 -0
  43. data/lib/tree_sitter.rb +34 -0
  44. data/lib/tree_stand/ast_modifier.rb +30 -0
  45. data/lib/tree_stand/breadth_first_visitor.rb +54 -0
  46. data/lib/tree_stand/config.rb +19 -0
  47. data/lib/tree_stand/node.rb +351 -0
  48. data/lib/tree_stand/parser.rb +87 -0
  49. data/lib/tree_stand/range.rb +55 -0
  50. data/lib/tree_stand/tree.rb +123 -0
  51. data/lib/tree_stand/utils/printer.rb +73 -0
  52. data/lib/tree_stand/version.rb +7 -0
  53. data/lib/tree_stand/visitor.rb +127 -0
  54. data/lib/tree_stand/visitors/tree_walker.rb +37 -0
  55. data/lib/tree_stand.rb +48 -0
  56. data/tree_sitter.gemspec +34 -0
  57. metadata +135 -0
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TreeSitter
4
+ # Node is a wrapper around a tree-sitter node.
5
+ class Node
6
+ include Enumerable
7
+
8
+ # @return [Array<Symbol>] the node's named fields
9
+ def fields
10
+ return @fields if @fields
11
+
12
+ @fields = Set.new
13
+ child_count.times do |i|
14
+ name = field_name_for_child(i)
15
+ @fields << name.to_sym if name
16
+ end
17
+
18
+ @fields.to_a
19
+ end
20
+
21
+ # @param field [String, Symbol]
22
+ def field?(field)
23
+ fields.include?(field.to_sym)
24
+ end
25
+
26
+ # Access node's named children.
27
+ #
28
+ # It's similar to {#fetch}, but differs in input type, return values, and
29
+ # the internal implementation.
30
+ #
31
+ # Both of these methods exist for separate use cases, but also because
32
+ # sometime tree-sitter does some monkey business and having both separate
33
+ # implementations can help.
34
+ #
35
+ # Comparison with {#fetch}:
36
+ #
37
+ # [] | fetch
38
+ # ------------------------------+----------------------
39
+ # input types Integer, String, Symbol | Array<String, Symbol>
40
+ # Array<Integer, String, Symbol>|
41
+ # ------------------------------+----------------------
42
+ # returns 1-to-1 correspondance with | unique nodes
43
+ # input |
44
+ # ------------------------------+----------------------
45
+ # uses named_child | field_name_for_child
46
+ # child_by_field_name | via each_node
47
+ # ------------------------------+----------------------
48
+ #
49
+ # @param keys [Integer | String | Symbol | Array<Integer, String, Symbol>, #read]
50
+ #
51
+ # @return [Node | Array<Node>]
52
+ def [](*keys)
53
+ case keys.length
54
+ when 0 then raise ArgumentError, "#{self.class.name}##{__method__} requires a key."
55
+ when 1
56
+ case k = keys.first
57
+ when Integer then named_child(k)
58
+ when String, Symbol
59
+ raise IndexError, "Cannot find field #{k}. Available: #{fields}" unless fields.include?(k.to_sym)
60
+
61
+ child_by_field_name(k.to_s)
62
+ else raise ArgumentError, <<~ERR
63
+ #{self.class.name}##{__method__} accepts Integer and returns named child at given index,
64
+ or a (String | Symbol) and returns the child by given field name.
65
+ ERR
66
+ end
67
+ else
68
+ keys.map { |key| self[key] }
69
+ end
70
+ end
71
+
72
+ # @!visibility private
73
+ #
74
+ # Allows access to child_by_field_name without using [].
75
+ def method_missing(method_name, *_args, &_block)
76
+ if fields.include?(method_name)
77
+ child_by_field_name(method_name.to_s)
78
+ else
79
+ super
80
+ end
81
+ end
82
+
83
+ # @!visibility private
84
+ #
85
+ def respond_to_missing?(*args)
86
+ args.length == 1 && fields.include?(args[0])
87
+ end
88
+
89
+ # Iterate over a node's children.
90
+ #
91
+ # @yieldparam child [Node] the child
92
+ def each(&_block)
93
+ return enum_for __method__ if !block_given?
94
+
95
+ (0...child_count).each do |i|
96
+ yield child(i)
97
+ end
98
+ end
99
+
100
+ # Iterate over a node's children assigned to a field.
101
+ #
102
+ # @yieldparam name [NilClass | String] field name.
103
+ # @yieldparam child [Node] the child.
104
+ def each_field
105
+ return enum_for __method__ if !block_given?
106
+
107
+ each.with_index do |c, i|
108
+ f = field_name_for_child(i)
109
+ next if f.nil? || f.empty?
110
+
111
+ yield f, c
112
+ end
113
+ end
114
+
115
+ # Iterate over a node's named children
116
+ #
117
+ # @yieldparam child [Node] the child
118
+ def each_named
119
+ return enum_for __method__ if !block_given?
120
+
121
+ (0...(named_child_count)).each do |i|
122
+ yield named_child(i)
123
+ end
124
+ end
125
+
126
+ # @return [Array<TreeSitter::Node>] all the node's children
127
+ def to_a
128
+ each.to_a
129
+ end
130
+
131
+ # Access node's named children.
132
+ #
133
+ # It's similar to {#[]}, but differs in input type, return values, and
134
+ # the internal implementation.
135
+ #
136
+ # Both of these methods exist for separate use cases, but also because
137
+ # sometime tree-sitter does some monkey business and having both separate
138
+ # implementations can help.
139
+ #
140
+ # Comparison with {#fetch}:
141
+ #
142
+ # [] | fetch
143
+ # ------------------------------+----------------------
144
+ # input types Integer, String, Symbol | String, Symbol
145
+ # Array<Integer, String, Symbol>| Array<String, Symbol>
146
+ # ------------------------------+----------------------
147
+ # returns 1-to-1 correspondance with | unique nodes
148
+ # input |
149
+ # ------------------------------+----------------------
150
+ # uses named_child | field_name_for_child
151
+ # child_by_field_name | via each_node
152
+ # ------------------------------+----------------------
153
+ #
154
+ # See {#[]}.
155
+ def fetch(*keys)
156
+ keys = keys.map(&:to_s)
157
+ key_set = keys.to_set
158
+ fields = {}
159
+ each_field do |f, _c|
160
+ fields[f] = self[f] if key_set.delete(f)
161
+
162
+ break if key_set.empty?
163
+ end
164
+ fields.values_at(*keys)
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helpers'
4
+
5
+ module TreeSitter
6
+ # Query is a wrapper around a tree-sitter query.
7
+ class Query
8
+ attr_reader :capture_names
9
+ attr_reader :capture_quantifiers
10
+ attr_reader :text_predicates
11
+ attr_reader :property_predicates
12
+ attr_reader :property_settings
13
+ attr_reader :general_predicates
14
+
15
+ private
16
+
17
+ # Called from query.c on initialize.
18
+ #
19
+ # Prepares all the predicates so we could process them in places like
20
+ # {QueryMatch#satisfies_text_predicate?}.
21
+ #
22
+ # This is translation from the [rust bindings](https://github.com/tree-sitter/tree-sitter/blob/e553578696fe86071846ed612ee476d0167369c1/lib/binding_rust/lib.rs#L1860)
23
+ # Because it's a direct translation, it's way too long and we need to shut up rubocop.
24
+ # TODO: refactor + simplify when stable.
25
+ def process(source) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
26
+ string_count = self.string_count
27
+ capture_count = self.capture_count
28
+ pattern_count = self.pattern_count
29
+
30
+ # Build a vector of strings to store the capture names.
31
+ capture_names = capture_count.times.map { |i| capture_name_for_id(i) }
32
+
33
+ # Build a vector to store capture qunatifiers.
34
+ capture_quantifiers =
35
+ pattern_count.times.map do |i|
36
+ capture_count.times.map do |j|
37
+ capture_quantifier_for_id(i, j)
38
+ end
39
+ end
40
+
41
+ # Build a vector of strings to represent literal values used in predicates.
42
+ string_values = string_count.times.map { |i| string_value_for_id(i) }
43
+
44
+ # Build a vector of predicates for each pattern.
45
+ pattern_count.times do |i| # rubocop:disable Metrics/BlockLength
46
+ predicate_steps = predicates_for_pattern(i)
47
+ byte_offset = start_byte_for_pattern(i)
48
+ row =
49
+ source.chars.map.with_index
50
+ .take_while { |_, i| i < byte_offset } # rubocop:disable Lint/ShadowingOuterLocalVariable
51
+ .filter { |c, _| c == "\n" }
52
+ .size
53
+ text_predicates = []
54
+ property_predicates = []
55
+ property_settings = []
56
+ general_predicates = []
57
+
58
+ array_split_like_rust(predicate_steps) { |s| s.type == QueryPredicateStep::DONE } # rubocop:disable Metrics/BlockLength
59
+ .each do |p|
60
+ next if p.empty?
61
+
62
+ if p[0] == QueryPredicateStep::STRING
63
+ cap = capture_names[p[0].value_id]
64
+ raise ArgumentError, <<~MSG.chomp
65
+ L#{row}: Expected predicate to start with a function name. Got @#{cap}.
66
+ MSG
67
+ end
68
+
69
+ # Build a predicate for each of the known predicate function names.
70
+ operator_name = string_values[p[0].value_id]
71
+
72
+ case operator_name
73
+ in 'any-eq?' | 'any-not-eq?' | 'eq?' | 'not-eq?'
74
+ if p.size != 3
75
+ raise ArgumentError, <<~MSG.chomp
76
+ L#{row}: Wrong number of arguments to ##{operator_name} predicate. Expected 2, got #{p.size - 1}.
77
+ MSG
78
+ end
79
+
80
+ if p[1].type != QueryPredicateStep::CAPTURE
81
+ lit = string_values[p[1].value_id]
82
+ raise ArgumentError, <<~MSG.chomp
83
+ L#{row}: First argument to ##{operator_name} predicate must be a capture name. Got literal "#{lit}".
84
+ MSG
85
+ end
86
+
87
+ is_positive = %w[eq? any-eq?].include?(operator_name)
88
+ match_all = %w[eq? not-eq?].include?(operator_name)
89
+ # NOTE: in the rust impl, match_all can hit an unreachable! but I am simplifying
90
+ # for readability. Same applies for the other `in` branches.
91
+ text_predicates <<
92
+ if p[2].type == QueryPredicateStep::CAPTURE
93
+ TextPredicateCapture.eq_capture(p[1].value_id, p[2].value_id, is_positive, match_all)
94
+ else
95
+ TextPredicateCapture.eq_string(p[1].value_id, string_values[p[2].value_id], is_positive, match_all)
96
+ end
97
+
98
+ in 'match?' | 'not-match?' | 'any-match?' | 'any-not-match?'
99
+ if p.size != 3
100
+ raise ArgumentError, <<~MSG.chomp
101
+ L#{row}: Wrong number of arguments to ##{operator_name} predicate. Expected 2, got #{p.size - 1}.
102
+ MSG
103
+ end
104
+
105
+ if p[1].type != QueryPredicateStep::CAPTURE
106
+ lit = string_values[p[1].value_id]
107
+ raise ArgumentError, <<~MSG.chomp
108
+ L#{row}: First argument to ##{operator_name} predicate must be a capture name. Got literal "#{lit}".
109
+ MSG
110
+ end
111
+
112
+ if p[2].type == QueryPredicateStep::CAPTURE
113
+ cap = capture_names[p[2].value_id]
114
+ raise ArgumentError, <<~MSG.chomp
115
+ L#{row}: First argument to ##{operator_name} predicate must be a literal. Got capture @#{cap}".
116
+ MSG
117
+ end
118
+
119
+ is_positive = %w[match? any-match?].include?(operator_name)
120
+ match_all = %w[match? not-match?].include?(operator_name)
121
+ regex = /#{string_values[p[2].value_id]}/
122
+
123
+ text_predicates << TextPredicateCapture.match_string(p[1].value_id, regex, is_positive, match_all)
124
+
125
+ in 'set!'
126
+ property_settings << 'todo!'
127
+
128
+ in 'is?' | 'is-not?'
129
+ property_predicates << 'todo!'
130
+
131
+ in 'any-of?' | 'not-any-of?'
132
+ if p.size < 2
133
+ raise ArgumentError, <<~MSG.chomp
134
+ L#{row}: Wrong number of arguments to ##{operator_name} predicate. Expected at least 1, got #{p.size - 1}.
135
+ MSG
136
+ end
137
+
138
+ if p[1].type != QueryPredicateStep::CAPTURE
139
+ lit = string_values[p[1].value_id]
140
+ raise ArgumentError, <<~MSG.chomp
141
+ L#{row}: First argument to ##{operator_name} predicate must be a capture name. Got literal "#{lit}".
142
+ MSG
143
+ end
144
+
145
+ is_positive = operator_name == 'any_of'
146
+ values = []
147
+
148
+ p[2..].each do |arg|
149
+ if arg.type == QueryPredicateStep::CAPTURE
150
+ lit = string_values[arg.value_id]
151
+ raise ArgumentError, <<~MSG.chomp
152
+ L#{row}: First argument to ##{operator_name} predicate must be a capture name. Got literal "#{lit}".
153
+ MSG
154
+ end
155
+ values << string_values[arg.value_id]
156
+ end
157
+
158
+ # TODO: is the map to to_s necessary in ruby?
159
+ text_predicates <<
160
+ TextPredicateCapture.any_string(p[1].value_id, values.map(&:to_s), is_positive, match_all)
161
+ else
162
+ general_predicates <<
163
+ QueryPredicate.new(
164
+ operator_name,
165
+ p[1..].map do |a|
166
+ if a.type == QueryPredicateStep::CAPTURE
167
+ { capture: a.value_id }
168
+ else
169
+ { string: string_values[a.value_id] }
170
+ end
171
+ end,
172
+ )
173
+ end
174
+
175
+ @text_predicates << text_predicates
176
+ @property_predicates << property_predicates
177
+ @property_settings << property_settings
178
+ @general_predicates << general_predicates
179
+ end
180
+
181
+ @capture_names = capture_names
182
+ @capture_quantifiers = capture_quantifiers
183
+ end
184
+ end
185
+
186
+ # TODO
187
+ def parse_property
188
+ # todo
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TreeSitter
4
+ # A sequence of {TreeSitter::QueryCapture} associated with a given {TreeSitter::QueryCursor}.
5
+ class QueryCaptures
6
+ include Enumerable
7
+
8
+ def initialize(cursor, query, src)
9
+ @cursor = cursor
10
+ @query = query
11
+ @src = src
12
+ end
13
+
14
+ # Iterator over captures.
15
+ #
16
+ # @yieldparam match [TreeSitter::QueryMatch]
17
+ # @yieldparam capture_index [Integer]
18
+ def each(&_block)
19
+ return enum_for __method__ if !block_given?
20
+
21
+ while (capture_index, match = @cursor.next_capture)
22
+ next if !match.is_a?(TreeSitter::QueryMatch)
23
+
24
+ if match.satisfies_text_predicate?(@query, @src)
25
+ yield [match, capture_index]
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TreeSitter
4
+ # A Cursor for {Query}.
5
+ class QueryCursor
6
+ # Iterate over all of the matches in the order that they were found.
7
+ #
8
+ # Each match contains the index of the pattern that matched, and a list of
9
+ # captures. Because multiple patterns can match the same set of nodes,
10
+ # one match may contain captures that appear *before* some of the
11
+ # captures from a previous match.
12
+ def matches(query, node, src)
13
+ self.exec(query, node)
14
+ QueryMatches.new(self, query, src)
15
+ end
16
+
17
+ # Iterate over all of the individual captures in the order that they
18
+ # appear.
19
+ #
20
+ # This is useful if you don't care about which pattern matched, and just
21
+ # want a single, ordered sequence of captures.
22
+ def captures(query, node, src)
23
+ self.exec(query, node)
24
+ QueryCaptures.new(self, query, src)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'query_captures'
4
+
5
+ module TreeSitter
6
+ # A match for a {Query}.
7
+ class QueryMatch
8
+ # All nodes at a given capture index.
9
+ #
10
+ # @param index [Integer]
11
+ #
12
+ # @return [TreeSitter::Node]
13
+ def nodes_for_capture_index(index) = captures.filter_map { |capture| capture.node if capture.index == index }
14
+
15
+ # Whether the {QueryMatch} satisfies the text predicates in the query.
16
+ #
17
+ # This is a translation from the [rust bindings](https://github.com/tree-sitter/tree-sitter/blob/e553578696fe86071846ed612ee476d0167369c1/lib/binding_rust/lib.rs#L2502).
18
+ # Because it's a direct translation, it's way too long and we need to shut up rubocop.
19
+ # TODO: refactor + simplify when satable.
20
+ def satisfies_text_predicate?(query, src) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
21
+ return true if query.text_predicates[pattern_index].nil?
22
+
23
+ query # rubocop:disable Metrics/BlockLength
24
+ .text_predicates[pattern_index]
25
+ .all? do |predicate|
26
+ case predicate.type
27
+ in TextPredicateCapture::EQ_CAPTURE
28
+ fst_nodes = nodes_for_capture_index(predicate.fst)
29
+ snd_nodes = nodes_for_capture_index(predicate.snd)
30
+ res = nil
31
+ consumed = 0
32
+ fst_nodes.zip(snd_nodes).each do |node1, node2|
33
+ text1 = node_text(node1, src)
34
+ text2 = node_text(node2, src)
35
+ if (text1 == text2) != predicate.positive? && predicate.match_all?
36
+ res = false
37
+ break
38
+ end
39
+ if (text1 == text2) == predicate.positive? && !predicate.match_all?
40
+ res = true
41
+ break
42
+ end
43
+ consumed += 1
44
+ end
45
+ (res.nil? && consumed == fst_nodes.length && consumed == snd_nodes.length) \
46
+ || res
47
+
48
+ in TextPredicateCapture::EQ_STRING
49
+ nodes = nodes_for_capture_index(predicate.fst)
50
+ res = true
51
+ nodes.each do |node|
52
+ text = node_text(node, src)
53
+ if (predicate.snd == text) != predicate.positive? && predicate.match_all?
54
+ res = false
55
+ break
56
+ end
57
+ if (predicate.snd == text) == predicate.positive? && !predicate.match_all?
58
+ res = true
59
+ break
60
+ end
61
+ end
62
+ res
63
+
64
+ in TextPredicateCapture::MATCH_STRING
65
+ nodes = nodes_for_capture_index(predicate.fst)
66
+ res = true
67
+ nodes.each do |node|
68
+ text = node_text(node, src)
69
+ if predicate.snd.match?(text) != predicate.positive? && predicate.match_all?
70
+ res = false
71
+ break
72
+ end
73
+ if predicate.snd.match?(text) == predicate.positive? && !predicate.match_all?
74
+ res = true
75
+ break
76
+ end
77
+ end
78
+ res
79
+
80
+ in TextPredicateCapture::ANY_STRING
81
+ nodes = nodes_for_capture_index(predicate.fst)
82
+ res = true
83
+ nodes.each do |node|
84
+ text = node_text(node, src)
85
+ if predicate.snd.any? { |v| v == text } != predicate.positive?
86
+ res = false
87
+ break
88
+ end
89
+ end
90
+ res
91
+
92
+ end
93
+ end
94
+ end
95
+
96
+ private
97
+
98
+ def node_text(node, text) = text.byteslice(node.start_byte...node.end_byte)
99
+ end
100
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TreeSitter
4
+ # A sequence of {QueryMatch} associated with a given {QueryCursor}.
5
+ class QueryMatches
6
+ include Enumerable
7
+
8
+ def initialize(cursor, query, src)
9
+ @cursor = cursor
10
+ @query = query
11
+ @src = src
12
+ end
13
+
14
+ # Iterator over matches.
15
+ #
16
+ # @yieldparam match [TreeSitter::QueryMatch]
17
+ def each(&_block)
18
+ return enum_for __method__ if !block_given?
19
+
20
+ while match = @cursor.next_match
21
+ if match.satisfies_text_predicate?(@query, @src)
22
+ yield match
23
+ end
24
+ end
25
+ end
26
+
27
+ # Iterate over all the results presented as hashes of `capture name => node`.
28
+ #
29
+ # @yieldparam match [Hash<String, TreeSitter::Node>]
30
+ def each_capture_hash(&_block)
31
+ # TODO: should we return [Array<Hash<Symbol, TreeSitter::Node]>>] instead?
32
+ return enum_for __method__ if !block_given?
33
+
34
+ each do |match|
35
+ yield match.captures.to_h { |cap| [@query.capture_name_for_id(cap.index), cap.node] }
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TreeSitter
4
+ # A {Query} predicate generic representation.
5
+ class QueryPredicate
6
+ attr_accessor :operator
7
+ attr_accessor :args
8
+
9
+ def initialize(operator, args)
10
+ @operator = operator
11
+ @args = args
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TreeSitter
4
+ # A representation for text predicates.
5
+ class TextPredicateCapture
6
+ EQ_CAPTURE = 0 # Equality Capture
7
+ EQ_STRING = 1 # Equality String
8
+ MATCH_STRING = 2 # Match String
9
+ ANY_STRING = 3 # Any String
10
+
11
+ attr_reader :fst
12
+ attr_reader :snd
13
+ attr_reader :type
14
+
15
+ # Create a TextPredicateCapture for {EQ_CAPTURE}.
16
+ def self.eq_capture(...) = new(EQ_CAPTURE, ...)
17
+ # Create a TextPredicateCapture for {EQ_STRING}.
18
+ def self.eq_string(...) = new(EQ_STRING, ...)
19
+ # Create a TextPredicateCapture for {MATCH_STRING}.
20
+ def self.match_string(...) = new(MATCH_STRING, ...)
21
+ # Create a TextPredicateCapture for {ANY_STRING}.
22
+ def self.any_string(...) = new(ANY_STRING, ...)
23
+
24
+ def initialize(type, fst, snd, positive, match_all)
25
+ @type = type
26
+ @fst = fst
27
+ @snd = snd
28
+ @positive = positive
29
+ @match_all = match_all
30
+ end
31
+
32
+ # `#eq` is positive, `#not-eq` is not.
33
+ def positive? = @positive
34
+ # `#any-` means don't match all.
35
+ def match_all? = @match_all
36
+ end
37
+ end
@@ -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.6.0'
8
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ begin
6
+ RUBY_VERSION =~ /(\d+\.\d+)/
7
+ require "tree_sitter/#{Regexp.last_match(1)}/tree_sitter"
8
+ rescue LoadError
9
+ require 'tree_sitter/tree_sitter'
10
+ end
11
+
12
+ require 'tree_sitter/version'
13
+
14
+ require 'tree_sitter/mixins/language'
15
+
16
+ require 'tree_sitter/node'
17
+ require 'tree_sitter/query'
18
+ require 'tree_sitter/query_captures'
19
+ require 'tree_sitter/query_cursor'
20
+ require 'tree_sitter/query_match'
21
+ require 'tree_sitter/query_matches'
22
+ require 'tree_sitter/query_predicate'
23
+ require 'tree_sitter/text_predicate_capture'
24
+
25
+ # TreeSitter is a Ruby interface to the tree-sitter parsing library.
26
+ module TreeSitter
27
+ extend Mixins::Language
28
+
29
+ class << self
30
+ alias_method :lang, :language
31
+ end
32
+ end
33
+
34
+ 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