tree_haver 7.0.0 → 7.1.0

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 (44) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/LICENSE.md +13 -0
  4. data/README.md +1959 -0
  5. data/lib/tree_haver/backend_api.rb +392 -0
  6. data/lib/tree_haver/backend_registry.rb +153 -3
  7. data/lib/tree_haver/backends/citrus.rb +489 -0
  8. data/lib/tree_haver/backends/ffi.rb +1013 -0
  9. data/lib/tree_haver/backends/java.rb +909 -0
  10. data/lib/tree_haver/backends/mri.rb +367 -0
  11. data/lib/tree_haver/backends/parslet.rb +565 -0
  12. data/lib/tree_haver/backends/prism.rb +568 -0
  13. data/lib/tree_haver/backends/psych.rb +379 -0
  14. data/lib/tree_haver/backends/rust.rb +243 -0
  15. data/lib/tree_haver/backends/tslp.rb +274 -0
  16. data/lib/tree_haver/base/comment.rb +320 -0
  17. data/lib/tree_haver/base/language.rb +98 -0
  18. data/lib/tree_haver/base/node.rb +330 -0
  19. data/lib/tree_haver/base/parser.rb +28 -0
  20. data/lib/tree_haver/base/point.rb +48 -0
  21. data/lib/tree_haver/base/tree.rb +128 -0
  22. data/lib/tree_haver/citrus_grammar_finder.rb +213 -0
  23. data/lib/tree_haver/contracts.rb +661 -96
  24. data/lib/tree_haver/grammar_finder.rb +429 -0
  25. data/lib/tree_haver/kaitai_backend.rb +2 -2
  26. data/lib/tree_haver/language.rb +294 -0
  27. data/lib/tree_haver/language_pack.rb +17 -166
  28. data/lib/tree_haver/language_registry.rb +221 -0
  29. data/lib/tree_haver/library_path_utils.rb +80 -0
  30. data/lib/tree_haver/node.rb +588 -0
  31. data/lib/tree_haver/parser.rb +445 -0
  32. data/lib/tree_haver/parslet_grammar_finder.rb +217 -0
  33. data/lib/tree_haver/path_validator.rb +356 -0
  34. data/lib/tree_haver/peg_backends.rb +7 -7
  35. data/lib/tree_haver/point.rb +27 -0
  36. data/lib/tree_haver/rspec/dependency_tags.rb +52 -0
  37. data/lib/tree_haver/rspec.rb +3 -0
  38. data/lib/tree_haver/tree.rb +267 -0
  39. data/lib/tree_haver/version.rb +5 -3
  40. data/lib/tree_haver.rb +613 -8
  41. data/sig/tree_haver.rbs +6 -0
  42. data.tar.gz.sig +0 -0
  43. metadata +314 -13
  44. metadata.gz.sig +0 -0
@@ -0,0 +1,379 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TreeHaver
4
+ module Backends
5
+ # Psych backend using Ruby's built-in YAML parser
6
+ #
7
+ # This backend wraps Psych, Ruby's standard library YAML parser.
8
+ # Psych provides AST access via Psych.parse_stream which returns
9
+ # Psych::Nodes::* objects (Stream, Document, Mapping, Sequence, Scalar, Alias).
10
+ #
11
+ # @note This backend only parses YAML source code
12
+ # @see https://ruby-doc.org/stdlib/libdoc/psych/rdoc/Psych.html Psych documentation
13
+ #
14
+ # @example Basic usage
15
+ # parser = TreeHaver::Parser.new
16
+ # parser.language = TreeHaver::Backends::Psych::Language.yaml
17
+ # tree = parser.parse(yaml_source)
18
+ # root = tree.root_node
19
+ # puts root.type # => "stream"
20
+ module Psych
21
+ @load_attempted = false
22
+ @loaded = false
23
+
24
+ # Check if the Psych backend is available
25
+ #
26
+ # Psych is part of Ruby stdlib, so it should always be available.
27
+ #
28
+ # @return [Boolean] true if psych is available
29
+ class << self
30
+ def available?
31
+ return @loaded if @load_attempted
32
+
33
+ @load_attempted = true
34
+ begin
35
+ require 'psych'
36
+ @loaded = true
37
+ rescue LoadError
38
+ @loaded = false
39
+ rescue StandardError
40
+ # simplecov:disable defensive code - StandardError during require is extremely rare
41
+ @loaded = false
42
+ # simplecov:enable
43
+ end
44
+ @loaded
45
+ end
46
+
47
+ # Reset the load state (primarily for testing)
48
+ #
49
+ # @return [void]
50
+ # @api private
51
+ def reset!
52
+ @load_attempted = false
53
+ @loaded = false
54
+ end
55
+
56
+ # Get capabilities supported by this backend
57
+ #
58
+ # @return [Hash{Symbol => Object}] capability map
59
+ def capabilities
60
+ return {} unless available?
61
+
62
+ {
63
+ backend: :psych,
64
+ query: false, # Psych doesn't have tree-sitter-style queries
65
+ bytes_field: false, # Psych uses line/column, not byte offsets
66
+ incremental: false, # Psych doesn't support incremental parsing
67
+ pure_ruby: false, # Psych has native libyaml C extension
68
+ yaml_only: true, # Psych only parses YAML
69
+ error_tolerant: false, # Psych raises on syntax errors
70
+ comment_support: :none
71
+ }
72
+ end
73
+ end
74
+
75
+ # Psych language wrapper
76
+ #
77
+ # Unlike tree-sitter which supports many languages via grammar files,
78
+ # Psych only parses YAML. This class exists for API compatibility with
79
+ # other tree_haver backends.
80
+ #
81
+ # @example
82
+ # language = TreeHaver::Backends::Psych::Language.yaml
83
+ # parser.language = language
84
+ class Language < TreeHaver::Base::Language
85
+ # Create a new Psych language instance
86
+ #
87
+ # @param name [Symbol] Language name (should be :yaml)
88
+ def initialize(name = :yaml)
89
+ super(name, backend: :psych, options: {})
90
+ end
91
+
92
+ class << self
93
+ # Create a YAML language instance
94
+ #
95
+ # @return [Language] YAML language
96
+ def yaml
97
+ new(:yaml)
98
+ end
99
+
100
+ # Load language from library path (API compatibility)
101
+ #
102
+ # Psych only supports YAML, so path and symbol parameters are ignored.
103
+ #
104
+ # @param _path [String] Ignored - Psych doesn't load external grammars
105
+ # @param symbol [String, nil] Ignored - Psych only supports YAML
106
+ # @param name [String, nil] Language name hint (defaults to :yaml)
107
+ # @return [Language] YAML language
108
+ # @raise [TreeHaver::NotAvailable] if requested language is not YAML
109
+ def from_library(_path = nil, symbol: nil, name: nil) # rubocop:disable Lint/UnusedMethodArgument
110
+ lang_name = name || :yaml
111
+
112
+ unless lang_name == :yaml
113
+ raise TreeHaver::NotAvailable,
114
+ "Psych backend only supports YAML, not #{lang_name}. " \
115
+ "Use a tree-sitter backend for #{lang_name} support."
116
+ end
117
+
118
+ yaml
119
+ end
120
+ end
121
+ end
122
+
123
+ # Psych parser wrapper
124
+ #
125
+ # Wraps Psych.parse_stream to provide TreeHaver-compatible parsing.
126
+ #
127
+ # @example
128
+ # parser = TreeHaver::Backends::Psych::Parser.new
129
+ # parser.language = Language.yaml
130
+ # tree = parser.parse(yaml_source)
131
+ class Parser < TreeHaver::Base::Parser
132
+ # Parse YAML source code
133
+ #
134
+ # @param source [String] YAML source to parse
135
+ # @return [Tree] Parsed tree
136
+ # @raise [::Psych::SyntaxError] on syntax errors
137
+ def parse(source)
138
+ raise 'Language not set' unless language
139
+
140
+ Psych.available? or raise 'Psych not available'
141
+
142
+ ast = ::Psych.parse_stream(source)
143
+ Tree.new(ast, source)
144
+ end
145
+
146
+ # Alias for compatibility with tree-sitter API
147
+ #
148
+ # @param _old_tree [nil] Ignored (Psych doesn't support incremental parsing)
149
+ # @param source [String] YAML source to parse
150
+ # @return [Tree] Parsed tree
151
+ def parse_string(_old_tree, source)
152
+ parse(source)
153
+ end
154
+ end
155
+
156
+ # Psych tree wrapper
157
+ #
158
+ # Wraps a Psych::Nodes::Stream to provide TreeHaver-compatible tree interface.
159
+ class Tree < TreeHaver::Base::Tree
160
+ # @return [::Psych::Nodes::Stream] The underlying Psych stream
161
+ attr_reader :inner_tree
162
+
163
+ # Create a new tree wrapper
164
+ #
165
+ # @param stream [::Psych::Nodes::Stream] Psych stream node
166
+ # @param source [String] Original source
167
+ def initialize(stream, source)
168
+ super(stream, source: source)
169
+ end
170
+
171
+ # Get the root node
172
+ #
173
+ # @return [Node] Root node
174
+ def root_node
175
+ Node.new(inner_tree, source: source, lines: lines)
176
+ end
177
+
178
+ # Human-readable representation
179
+ def inspect
180
+ "#<TreeHaver::Backends::Psych::Tree documents=#{inner_tree.children&.size || 0}>"
181
+ end
182
+ end
183
+
184
+ # Psych node wrapper
185
+ #
186
+ # Wraps Psych::Nodes::* classes to provide TreeHaver::Node-compatible interface.
187
+ #
188
+ # Psych node types:
189
+ # - Stream: Root container
190
+ # - Document: YAML document (multiple per stream possible)
191
+ # - Mapping: Hash/object
192
+ # - Sequence: Array/list
193
+ # - Scalar: Primitive value (string, number, boolean, null)
194
+ # - Psych::Nodes::Alias: YAML anchor reference
195
+ class Node < TreeHaver::Base::Node
196
+ # Get the node type as a string
197
+ #
198
+ # @return [String] Node type
199
+ def type
200
+ inner_node.class.name.split('::').last.downcase
201
+ end
202
+
203
+ # Alias for type (API compatibility)
204
+ # @return [String] node type
205
+ def kind
206
+ type
207
+ end
208
+
209
+ # Get the text content of this node
210
+ #
211
+ # @return [String] Node text
212
+ def text
213
+ case inner_node
214
+ when ::Psych::Nodes::Scalar
215
+ inner_node.value.to_s
216
+ when ::Psych::Nodes::Alias
217
+ "*#{inner_node.anchor}"
218
+ else
219
+ # For container nodes, extract from source using location
220
+ extract_text_from_location
221
+ end
222
+ end
223
+
224
+ # Get child nodes
225
+ #
226
+ # @return [Array<Node>] Child nodes
227
+ def children
228
+ return [] unless inner_node.respond_to?(:children) && inner_node.children
229
+
230
+ inner_node.children.map { |child| Node.new(child, source: source, lines: lines) }
231
+ end
232
+
233
+ # Get start byte offset
234
+ #
235
+ # @return [Integer] Start byte offset
236
+ def start_byte
237
+ return 0 unless inner_node.respond_to?(:start_line)
238
+
239
+ line = inner_node.start_line || 0
240
+ col = inner_node.start_column || 0
241
+ calculate_byte_offset(line, col)
242
+ end
243
+
244
+ # Get end byte offset
245
+ #
246
+ # @return [Integer] End byte offset
247
+ def end_byte
248
+ return start_byte + text.bytesize unless inner_node.respond_to?(:end_line)
249
+
250
+ line = inner_node.end_line || 0
251
+ col = inner_node.end_column || 0
252
+ calculate_byte_offset(line, col)
253
+ end
254
+
255
+ # Get start point (row, column) - 0-based
256
+ #
257
+ # @return [TreeHaver::Base::Point] Start position
258
+ def start_point
259
+ row = (inner_node.respond_to?(:start_line) ? inner_node.start_line : 0) || 0
260
+ col = (inner_node.respond_to?(:start_column) ? inner_node.start_column : 0) || 0
261
+ TreeHaver::Base::Point.new(row, col)
262
+ end
263
+
264
+ # Get end point (row, column) - 0-based
265
+ #
266
+ # @return [TreeHaver::Base::Point] End position
267
+ def end_point
268
+ row = (inner_node.respond_to?(:end_line) ? inner_node.end_line : 0) || 0
269
+ col = (inner_node.respond_to?(:end_column) ? inner_node.end_column : 0) || 0
270
+ TreeHaver::Base::Point.new(row, col)
271
+ end
272
+
273
+ # Psych-specific: Get the anchor name for Alias/anchored nodes
274
+ #
275
+ # @return [String, nil] Anchor name
276
+ def anchor
277
+ inner_node.anchor if inner_node.respond_to?(:anchor)
278
+ end
279
+
280
+ # Psych-specific: Get the tag for tagged nodes
281
+ #
282
+ # @return [String, nil] Tag
283
+ def tag
284
+ inner_node.tag if inner_node.respond_to?(:tag)
285
+ end
286
+
287
+ # Psych-specific: Get the scalar value
288
+ #
289
+ # @return [String, nil] Value for scalar nodes
290
+ def value
291
+ inner_node.value if inner_node.respond_to?(:value)
292
+ end
293
+
294
+ # Psych-specific: Check if this is a mapping (hash)
295
+ #
296
+ # @return [Boolean]
297
+ def mapping?
298
+ inner_node.is_a?(::Psych::Nodes::Mapping)
299
+ end
300
+
301
+ # Psych-specific: Check if this is a sequence (array)
302
+ #
303
+ # @return [Boolean]
304
+ def sequence?
305
+ inner_node.is_a?(::Psych::Nodes::Sequence)
306
+ end
307
+
308
+ # Psych-specific: Check if this is a scalar (primitive)
309
+ #
310
+ # @return [Boolean]
311
+ def scalar?
312
+ inner_node.is_a?(::Psych::Nodes::Scalar)
313
+ end
314
+
315
+ # Psych-specific: Check if this is an alias
316
+ #
317
+ # @return [Boolean]
318
+ def alias?
319
+ inner_node.is_a?(::Psych::Nodes::Alias)
320
+ end
321
+
322
+ # Psych-specific: Get mapping entries as key-value pairs
323
+ #
324
+ # For Mapping nodes, children alternate key, value, key, value...
325
+ #
326
+ # @return [Array<Array(Node, Node)>] Key-value pairs
327
+ def mapping_entries
328
+ return [] unless mapping?
329
+
330
+ pairs = []
331
+ children.each_slice(2) do |key, val|
332
+ pairs << [key, val] if key && val
333
+ end
334
+ pairs
335
+ end
336
+
337
+ private
338
+
339
+ # Extract text from source using location
340
+ #
341
+ # @return [String] Extracted text
342
+ def extract_text_from_location
343
+ return '' unless inner_node.respond_to?(:start_line) && inner_node.respond_to?(:end_line)
344
+
345
+ start_ln = inner_node.start_line || 0
346
+ end_ln = inner_node.end_line || start_ln
347
+ start_col = inner_node.start_column || 0
348
+ end_col = inner_node.end_column || 0
349
+
350
+ if start_ln == end_ln
351
+ line = lines[start_ln] || ''
352
+ line[start_col...end_col] || ''
353
+ else
354
+ result = []
355
+ (start_ln..end_ln).each do |ln|
356
+ line = lines[ln] || ''
357
+ result << if ln == start_ln
358
+ line[start_col..]
359
+ elsif ln == end_ln
360
+ line[0...end_col]
361
+ else
362
+ line
363
+ end
364
+ end
365
+ result.compact.join
366
+ end
367
+ end
368
+ end
369
+
370
+ # Alias Point to the base class for compatibility
371
+ Point = TreeHaver::Base::Point
372
+
373
+ # Register the availability checker for RSpec dependency tags
374
+ TreeHaver::BackendRegistry.register_availability_checker(:psych) do
375
+ available?
376
+ end
377
+ end
378
+ end
379
+ end
@@ -0,0 +1,243 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TreeHaver
4
+ module Backends
5
+ # Rust backend using the tree_stump gem
6
+ #
7
+ # This backend wraps the tree_stump gem, which provides Ruby bindings to
8
+ # tree-sitter written in Rust. It offers native performance with Rust's
9
+ # safety guarantees and includes precompiled binaries for common platforms.
10
+ #
11
+ # tree_stump supports incremental parsing and the Query API, making it
12
+ # suitable for editor/IDE use cases where performance is critical.
13
+ #
14
+ # == Tree/Node Architecture
15
+ #
16
+ # This backend (like all tree-sitter backends: MRI, Rust, FFI, Java) does NOT
17
+ # define its own Tree or Node classes. Instead:
18
+ #
19
+ # - Parser#parse returns raw `::TreeStump::Tree` objects
20
+ # - These are wrapped by `TreeHaver::Tree` (inherits from `Base::Tree`)
21
+ # - `TreeHaver::Tree#root_node` wraps raw nodes in `TreeHaver::Node`
22
+ #
23
+ # This differs from pure-Ruby backends (Citrus, Prism, Psych) which define
24
+ # their own `Backend::X::Tree` and `Backend::X::Node` classes.
25
+ #
26
+ # @see TreeHaver::Tree The wrapper class for tree-sitter Tree objects
27
+ # @see TreeHaver::Node The wrapper class for tree-sitter Node objects
28
+ # @see TreeHaver::Base::Tree Base class documenting the Tree API contract
29
+ # @see TreeHaver::Base::Node Base class documenting the Node API contract
30
+ #
31
+ # == Platform Compatibility
32
+ #
33
+ # - MRI Ruby: ✓ Full support
34
+ # - JRuby: ✗ Cannot load native extensions (runs on JVM)
35
+ # - TruffleRuby: ✗ magnus/rb-sys incompatible with TruffleRuby's C API emulation
36
+ #
37
+ # @see https://github.com/joker1007/tree_stump tree_stump
38
+ module Rust
39
+ @load_attempted = false
40
+ @loaded = false
41
+
42
+ # Check if the Rust backend is available
43
+ #
44
+ # Attempts to require tree_stump on first call and caches the result.
45
+ #
46
+ # @return [Boolean] true if tree_stump is available
47
+ # @example
48
+ # if TreeHaver::Backends::Rust.available?
49
+ # puts "Rust backend is ready"
50
+ # end
51
+ class << self
52
+ def available?
53
+ return @loaded if @load_attempted
54
+
55
+ @load_attempted = true
56
+ begin
57
+ # tree_stump uses magnus which requires MRI's C API
58
+ # It doesn't work on JRuby or TruffleRuby
59
+ if RUBY_ENGINE == 'ruby'
60
+ require 'tree_stump'
61
+ @loaded = true
62
+ else
63
+ @loaded = false
64
+ end
65
+ rescue LoadError
66
+ @loaded = false
67
+ rescue StandardError
68
+ @loaded = false
69
+ end
70
+ @loaded
71
+ end
72
+
73
+ # Reset the load state (primarily for testing)
74
+ #
75
+ # @return [void]
76
+ # @api private
77
+ def reset!
78
+ @load_attempted = false
79
+ @loaded = false
80
+ end
81
+
82
+ # Get capabilities supported by this backend
83
+ #
84
+ # @return [Hash{Symbol => Object}] capability map
85
+ # @example
86
+ # TreeHaver::Backends::Rust.capabilities
87
+ # # => { backend: :rust, query: true, bytes_field: true, incremental: false, comment_support: :nodes_only }
88
+ def capabilities
89
+ return {} unless available?
90
+
91
+ {
92
+ backend: :rust,
93
+ query: true,
94
+ bytes_field: true,
95
+ incremental: false, # TreeStump doesn't currently expose incremental parsing to Ruby
96
+ comment_support: :nodes_only
97
+ }
98
+ end
99
+ end
100
+
101
+ # Wrapper for tree_stump Language
102
+ #
103
+ # Provides TreeHaver-compatible interface to tree_stump's language loading.
104
+ # tree_stump uses a registration-based API where languages are registered
105
+ # by name, then referenced by that name when setting parser language.
106
+ class Language
107
+ include Comparable
108
+
109
+ # The registered language name
110
+ # @return [String]
111
+ attr_reader :name
112
+
113
+ # The backend this language is for
114
+ # @return [Symbol]
115
+ attr_reader :backend
116
+
117
+ # The path this language was loaded from (if known)
118
+ # @return [String, nil]
119
+ attr_reader :path
120
+
121
+ # @api private
122
+ # @param name [String] the registered language name
123
+ # @param path [String, nil] path language was loaded from
124
+ def initialize(name, path: nil)
125
+ @name = name
126
+ @backend = :rust
127
+ @path = path
128
+ end
129
+
130
+ # Compare languages for equality
131
+ #
132
+ # Rust languages are equal if they have the same backend and name.
133
+ # Name uniquely identifies a registered language in TreeStump.
134
+ #
135
+ # @param other [Object] object to compare with
136
+ # @return [Integer, nil] -1, 0, 1, or nil if not comparable
137
+ def <=>(other)
138
+ return unless other.is_a?(Language)
139
+ return unless other.backend == @backend
140
+
141
+ @name <=> other.name
142
+ end
143
+
144
+ # Hash value for this language (for use in Sets/Hashes)
145
+ # @return [Integer]
146
+ def hash
147
+ [@backend, @name].hash
148
+ end
149
+
150
+ # Alias eql? to ==
151
+ alias eql? ==
152
+
153
+ # Load a language from a shared library path
154
+ #
155
+ # @param path [String] absolute path to the language shared library
156
+ # @param symbol [String, nil] the symbol name (accepted for API consistency, but tree_stump derives it from name)
157
+ # @param name [String, nil] logical name for the language (optional, derived from path if not provided)
158
+ # @return [Language] a wrapper holding the registered language name
159
+ # @raise [TreeHaver::NotAvailable] if tree_stump is not available
160
+ # @example
161
+ # lang = TreeHaver::Backends::Rust::Language.from_library("/usr/local/lib/libtree-sitter-toml.so")
162
+ class << self
163
+ def from_library(path, symbol: nil, name: nil) # rubocop:disable Lint/UnusedMethodArgument
164
+ raise TreeHaver::NotAvailable, 'tree_stump not available' unless Rust.available?
165
+
166
+ # Validate the path exists before calling register_lang to provide a clear error
167
+ raise TreeHaver::NotAvailable, "Language library not found: #{path}" unless File.exist?(path)
168
+
169
+ # tree_stump uses TreeStump.register_lang(name, path) to register languages
170
+ # The name is used to derive the symbol automatically (tree_sitter_<name>)
171
+ # Use shared utility for consistent path parsing across backends
172
+ lang_name = name || LibraryPathUtils.derive_language_name_from_path(path)
173
+ ::TreeStump.register_lang(lang_name, path)
174
+ new(lang_name, path: path)
175
+ rescue RuntimeError => e
176
+ raise TreeHaver::NotAvailable, "Failed to load language from #{path}: #{e.message}"
177
+ end
178
+
179
+ # Backward-compatible alias for from_library
180
+ alias from_path from_library
181
+ end
182
+ end
183
+
184
+ # Wrapper for tree_stump Parser
185
+ #
186
+ # Provides TreeHaver-compatible interface to tree_stump's parser.
187
+ class Parser
188
+ # Create a new parser instance
189
+ #
190
+ # @raise [TreeHaver::NotAvailable] if tree_stump is not available
191
+ def initialize
192
+ raise TreeHaver::NotAvailable, 'tree_stump not available' unless Rust.available?
193
+
194
+ @parser = ::TreeStump::Parser.new
195
+ end
196
+
197
+ # Set the language for this parser
198
+ #
199
+ # Note: TreeHaver::Parser unwraps language objects before calling this method.
200
+ # When called from TreeHaver::Parser, receives String (language name).
201
+ # For backward compatibility and backend tests, also handles Language wrapper.
202
+ #
203
+ # @param lang [Language, String] the language wrapper or name string
204
+ # @return [Language, String] the language that was set
205
+ def language=(lang)
206
+ # Extract language name (handle both wrapper and raw string)
207
+ lang_name = lang.respond_to?(:name) ? lang.name : lang.to_s
208
+ # tree_stump uses set_language with a string name
209
+ @parser.set_language(lang_name)
210
+ lang # rubocop:disable Lint/Void (intentional return value)
211
+ end
212
+
213
+ # Parse source code
214
+ #
215
+ # @param source [String] the source code to parse
216
+ # @return [TreeStump::Tree] raw backend tree (wrapping happens in TreeHaver::Parser)
217
+ def parse(source)
218
+ # Return raw tree_stump tree - TreeHaver::Parser will wrap it
219
+ @parser.parse(source)
220
+ end
221
+
222
+ # Parse source code with optional incremental parsing
223
+ #
224
+ # Note: TreeStump does not currently expose incremental parsing to Ruby.
225
+ # The parse method always does a full parse, ignoring old_tree.
226
+ #
227
+ # @param old_tree [TreeHaver::Tree, nil] previous tree for incremental parsing (ignored)
228
+ # @param source [String] the source code to parse
229
+ # @return [TreeStump::Tree] raw backend tree (wrapping happens in TreeHaver::Parser)
230
+ def parse_string(old_tree, source) # rubocop:disable Lint/UnusedMethodArgument
231
+ # TreeStump's parse method only accepts source as a single argument
232
+ # and internally always passes None for the old tree (no incremental parsing support)
233
+ @parser.parse(source)
234
+ end
235
+ end
236
+
237
+ # Register the availability checker for RSpec dependency tags
238
+ TreeHaver::BackendRegistry.register_availability_checker(:rust) do
239
+ available?
240
+ end
241
+ end
242
+ end
243
+ end