tree_haver 5.0.5 → 7.0.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 (57) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/tree_haver/backend_context.rb +28 -0
  4. data/lib/tree_haver/backend_registry.rb +19 -432
  5. data/lib/tree_haver/contracts.rb +460 -0
  6. data/lib/tree_haver/kaitai_backend.rb +30 -0
  7. data/lib/tree_haver/language_pack.rb +190 -0
  8. data/lib/tree_haver/peg_backends.rb +76 -0
  9. data/lib/tree_haver/version.rb +1 -12
  10. data/lib/tree_haver.rb +7 -1316
  11. data.tar.gz.sig +0 -0
  12. metadata +34 -251
  13. metadata.gz.sig +0 -0
  14. data/CHANGELOG.md +0 -1393
  15. data/CITATION.cff +0 -20
  16. data/CODE_OF_CONDUCT.md +0 -134
  17. data/CONTRIBUTING.md +0 -359
  18. data/FUNDING.md +0 -74
  19. data/LICENSE.txt +0 -21
  20. data/README.md +0 -2320
  21. data/REEK +0 -0
  22. data/RUBOCOP.md +0 -71
  23. data/SECURITY.md +0 -21
  24. data/lib/tree_haver/backend_api.rb +0 -349
  25. data/lib/tree_haver/backends/citrus.rb +0 -487
  26. data/lib/tree_haver/backends/ffi.rb +0 -1009
  27. data/lib/tree_haver/backends/java.rb +0 -893
  28. data/lib/tree_haver/backends/mri.rb +0 -362
  29. data/lib/tree_haver/backends/parslet.rb +0 -560
  30. data/lib/tree_haver/backends/prism.rb +0 -471
  31. data/lib/tree_haver/backends/psych.rb +0 -375
  32. data/lib/tree_haver/backends/rust.rb +0 -239
  33. data/lib/tree_haver/base/language.rb +0 -98
  34. data/lib/tree_haver/base/node.rb +0 -322
  35. data/lib/tree_haver/base/parser.rb +0 -24
  36. data/lib/tree_haver/base/point.rb +0 -48
  37. data/lib/tree_haver/base/tree.rb +0 -128
  38. data/lib/tree_haver/base.rb +0 -12
  39. data/lib/tree_haver/citrus_grammar_finder.rb +0 -218
  40. data/lib/tree_haver/compat.rb +0 -43
  41. data/lib/tree_haver/grammar_finder.rb +0 -374
  42. data/lib/tree_haver/language.rb +0 -295
  43. data/lib/tree_haver/language_registry.rb +0 -190
  44. data/lib/tree_haver/library_path_utils.rb +0 -80
  45. data/lib/tree_haver/node.rb +0 -579
  46. data/lib/tree_haver/parser.rb +0 -438
  47. data/lib/tree_haver/parslet_grammar_finder.rb +0 -224
  48. data/lib/tree_haver/path_validator.rb +0 -353
  49. data/lib/tree_haver/point.rb +0 -27
  50. data/lib/tree_haver/rspec/dependency_tags.rb +0 -1392
  51. data/lib/tree_haver/rspec/testable_node.rb +0 -217
  52. data/lib/tree_haver/rspec.rb +0 -33
  53. data/lib/tree_haver/tree.rb +0 -258
  54. data/sig/tree_haver/backends.rbs +0 -352
  55. data/sig/tree_haver/grammar_finder.rbs +0 -29
  56. data/sig/tree_haver/path_validator.rbs +0 -32
  57. data/sig/tree_haver.rbs +0 -234
@@ -1,471 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module TreeHaver
4
- module Backends
5
- # Prism backend using Ruby's built-in Prism parser
6
- #
7
- # This backend wraps Prism, Ruby's official parser (stdlib in Ruby 3.4+,
8
- # available as a gem for 3.2+). Unlike tree-sitter backends which are
9
- # language-agnostic runtime parsers, Prism is specifically designed for
10
- # parsing Ruby source code.
11
- #
12
- # Prism provides excellent error recovery, detailed location information,
13
- # and is the future of Ruby parsing (used by CRuby, JRuby, TruffleRuby).
14
- #
15
- # @note This backend only parses Ruby source code
16
- # @see https://github.com/ruby/prism Prism parser
17
- #
18
- # @example Basic usage
19
- # parser = TreeHaver::Parser.new
20
- # parser.language = TreeHaver::Backends::Prism::Language.ruby
21
- # tree = parser.parse(ruby_source)
22
- # root = tree.root_node
23
- # puts root.type # => "program_node"
24
- module Prism
25
- @load_attempted = false
26
- @loaded = false
27
-
28
- # Check if the Prism backend is available
29
- #
30
- # Attempts to require prism on first call and caches the result.
31
- # On Ruby 3.4+, Prism is in stdlib. On 3.2-3.3, it's a gem.
32
- #
33
- # @return [Boolean] true if prism is available
34
- # @example
35
- # if TreeHaver::Backends::Prism.available?
36
- # puts "Prism backend is ready"
37
- # end
38
- class << self
39
- def available?
40
- return @loaded if @load_attempted # rubocop:disable ThreadSafety/ClassInstanceVariable
41
- @load_attempted = true # rubocop:disable ThreadSafety/ClassInstanceVariable
42
- begin
43
- require "prism"
44
- @loaded = true # rubocop:disable ThreadSafety/ClassInstanceVariable
45
- rescue LoadError
46
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
47
- rescue StandardError
48
- # :nocov: defensive code - StandardError during require is extremely rare
49
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
50
- # :nocov:
51
- end
52
- @loaded # rubocop:disable ThreadSafety/ClassInstanceVariable
53
- end
54
-
55
- # Reset the load state (primarily for testing)
56
- #
57
- # @return [void]
58
- # @api private
59
- def reset!
60
- @load_attempted = false # rubocop:disable ThreadSafety/ClassInstanceVariable
61
- @loaded = false # rubocop:disable ThreadSafety/ClassInstanceVariable
62
- end
63
-
64
- # Get capabilities supported by this backend
65
- #
66
- # @return [Hash{Symbol => Object}] capability map
67
- # @example
68
- # TreeHaver::Backends::Prism.capabilities
69
- # # => { backend: :prism, query: false, bytes_field: true, incremental: false, ruby_only: true }
70
- def capabilities
71
- return {} unless available?
72
- {
73
- backend: :prism,
74
- query: false, # Prism doesn't have tree-sitter-style queries (has pattern matching)
75
- bytes_field: true, # Prism provides byte offsets via Location
76
- incremental: false, # Prism doesn't support incremental parsing (yet)
77
- pure_ruby: false, # Prism has native C extension (but also pure Ruby mode)
78
- ruby_only: true, # Prism only parses Ruby source code
79
- error_tolerant: true, # Prism has excellent error recovery
80
- }
81
- end
82
- end
83
-
84
- # Prism language wrapper
85
- #
86
- # Unlike tree-sitter which supports many languages via grammar files,
87
- # Prism only parses Ruby. This class exists for API compatibility with
88
- # other tree_haver backends.
89
- #
90
- # @example
91
- # language = TreeHaver::Backends::Prism::Language.ruby
92
- # parser.language = language
93
- class Language < TreeHaver::Base::Language
94
- # @param name [Symbol] language name (should be :ruby)
95
- # @param options [Hash] Prism parsing options (e.g., frozen_string_literal, version)
96
- def initialize(name = :ruby, options: {})
97
- super(name, backend: :prism, options: options)
98
-
99
- unless self.name == :ruby
100
- raise TreeHaver::NotAvailable,
101
- "Prism only supports Ruby parsing. " \
102
- "Got language: #{name.inspect}"
103
- end
104
- end
105
-
106
- # Compare languages for equality by options (since name is always :ruby)
107
- #
108
- # @param other [Object] object to compare with
109
- # @return [Integer, nil] -1, 0, 1, or nil if not comparable
110
- def <=>(other)
111
- return unless other.is_a?(TreeHaver::Base::Language)
112
- return unless other.backend == backend
113
-
114
- options.to_a.sort <=> other.options.to_a.sort
115
- end
116
-
117
- class << self
118
- # Create a Ruby language instance (convenience method)
119
- #
120
- # @param options [Hash] Prism parsing options
121
- # @option options [Boolean] :frozen_string_literal frozen string literal pragma
122
- # @option options [String] :version Ruby version to parse as (e.g., "3.3.0")
123
- # @option options [Symbol] :command_line command line option (-e, -n, etc.)
124
- # @return [Language]
125
- # @example
126
- # lang = TreeHaver::Backends::Prism::Language.ruby
127
- # lang = TreeHaver::Backends::Prism::Language.ruby(frozen_string_literal: true)
128
- def ruby(options = {})
129
- new(:ruby, options: options)
130
- end
131
-
132
- # Load language from library path (API compatibility)
133
- #
134
- # Prism only supports Ruby, so path and symbol parameters are ignored.
135
- #
136
- # @param _path [String] Ignored - Prism doesn't load external grammars
137
- # @param symbol [String, nil] Ignored - Prism only supports Ruby
138
- # @param name [String, nil] Language name hint (defaults to :ruby)
139
- # @return [Language] Ruby language
140
- # @raise [TreeHaver::NotAvailable] if requested language is not Ruby
141
- def from_library(_path = nil, symbol: nil, name: nil)
142
- lang_name = name || :ruby
143
-
144
- unless lang_name == :ruby
145
- raise TreeHaver::NotAvailable,
146
- "Prism backend only supports Ruby, not #{lang_name}. " \
147
- "Use a tree-sitter backend for #{lang_name} support."
148
- end
149
-
150
- ruby
151
- end
152
-
153
- alias_method :from_path, :from_library
154
- end
155
- end
156
-
157
- # Prism parser wrapper
158
- #
159
- # Wraps Prism to provide a tree-sitter-like API for parsing Ruby code.
160
- class Parser < TreeHaver::Base::Parser
161
- # Create a new Prism parser instance
162
- #
163
- # @raise [TreeHaver::NotAvailable] if prism is not available
164
- def initialize
165
- super
166
- raise TreeHaver::NotAvailable, "prism not available" unless Prism.available?
167
- @options = {}
168
- end
169
-
170
- # Set the language for this parser
171
- #
172
- # Note: TreeHaver::Parser unwraps language objects before calling this method.
173
- # This backend receives the Language wrapper (since Prism::Language stores options).
174
- #
175
- # @param lang [Language, Symbol] Prism language (should be :ruby or Language instance)
176
- # @return [void]
177
- def language=(lang)
178
- case lang
179
- when Language
180
- @language = lang
181
- @options = lang.options
182
- when Symbol, String
183
- if lang.to_sym == :ruby
184
- @language = Language.ruby
185
- @options = {}
186
- else
187
- raise ArgumentError,
188
- "Prism only supports Ruby parsing. Got: #{lang.inspect}"
189
- end
190
- else
191
- raise ArgumentError,
192
- "Expected Prism::Language or :ruby, got #{lang.class}"
193
- end
194
- end
195
-
196
- # Parse source code
197
- #
198
- # @param source [String] the Ruby source code to parse
199
- # @return [Tree] raw backend tree (wrapping happens in TreeHaver::Parser)
200
- # @raise [TreeHaver::NotAvailable] if no language is set
201
- def parse(source)
202
- raise TreeHaver::NotAvailable, "No language loaded (use parser.language = :ruby)" unless @language
203
-
204
- # Use Prism.parse with options
205
- prism_result = ::Prism.parse(source, **@options)
206
- Tree.new(prism_result, source)
207
- end
208
-
209
- # Parse source code (compatibility with tree-sitter API)
210
- #
211
- # Prism doesn't support incremental parsing, so old_tree is ignored.
212
- #
213
- # @param old_tree [TreeHaver::Tree, nil] ignored (no incremental parsing support)
214
- # @param source [String] the Ruby source code to parse
215
- # @return [Tree] raw backend tree (wrapping happens in TreeHaver::Parser)
216
- def parse_string(old_tree, source) # rubocop:disable Lint/UnusedMethodArgument
217
- parse(source) # Prism doesn't support incremental parsing
218
- end
219
- end
220
-
221
- # Prism tree wrapper
222
- #
223
- # Wraps a Prism::ParseResult to provide tree-sitter-compatible API.
224
- #
225
- # @api private
226
- class Tree < TreeHaver::Base::Tree
227
- # @return [::Prism::ParseResult] the underlying Prism parse result
228
- attr_reader :parse_result
229
-
230
- def initialize(parse_result, source)
231
- super(parse_result, source: source)
232
- @parse_result = parse_result
233
- end
234
-
235
- # Get the root node of the parse tree
236
- #
237
- # @return [Node] wrapped root node
238
- def root_node
239
- Node.new(@parse_result.value, source)
240
- end
241
-
242
- # Check if the parse had errors
243
- #
244
- # @return [Boolean]
245
- def has_errors?
246
- @parse_result.failure?
247
- end
248
-
249
- # Get parse errors
250
- #
251
- # @return [Array<::Prism::ParseError>]
252
- def errors
253
- @parse_result.errors
254
- end
255
-
256
- # Get parse warnings
257
- #
258
- # @return [Array<::Prism::ParseWarning>]
259
- def warnings
260
- @parse_result.warnings
261
- end
262
-
263
- # Get comments from the parse
264
- #
265
- # @return [Array<::Prism::Comment>]
266
- def comments
267
- @parse_result.comments
268
- end
269
-
270
- # Get magic comments (e.g., frozen_string_literal)
271
- #
272
- # @return [Array<::Prism::MagicComment>]
273
- def magic_comments
274
- @parse_result.magic_comments
275
- end
276
-
277
- # Get data locations (__END__ section)
278
- #
279
- # @return [::Prism::Location, nil]
280
- def data_loc
281
- @parse_result.data_loc
282
- end
283
- end
284
-
285
- # Prism node wrapper
286
- #
287
- # Wraps Prism::Node objects to provide tree-sitter-compatible node API.
288
- #
289
- # Prism nodes provide:
290
- # - type: class name without "Node" suffix (e.g., ProgramNode → "program")
291
- # - location: ::Prism::Location with start/end offsets and line/column
292
- # - child_nodes: array of child nodes
293
- # - Various node-specific accessors
294
- #
295
- # @api private
296
- class Node < TreeHaver::Base::Node
297
- def initialize(node, source)
298
- super(node, source: source)
299
- end
300
-
301
- # Get node type from Prism class name
302
- #
303
- # Converts PrismClassName to tree-sitter-style type string.
304
- # Example: CallNode → "call_node", ProgramNode → "program_node"
305
- #
306
- # @return [String] node type in snake_case
307
- def type
308
- return "nil" if inner_node.nil?
309
-
310
- # Convert class name to snake_case type
311
- class_name = inner_node.class.name.split("::").last
312
- class_name.gsub(/([A-Z])/, '_\1').downcase.sub(/^_/, "")
313
- end
314
-
315
- # Alias for type (API compatibility)
316
- # @return [String] node type
317
- def kind
318
- type
319
- end
320
-
321
- # Get byte offset where the node starts
322
- #
323
- # @return [Integer]
324
- def start_byte
325
- return 0 if inner_node.nil? || !inner_node.respond_to?(:location)
326
- loc = inner_node.location
327
- loc&.start_offset || 0
328
- end
329
-
330
- # Get byte offset where the node ends
331
- #
332
- # @return [Integer]
333
- def end_byte
334
- return 0 if inner_node.nil? || !inner_node.respond_to?(:location)
335
- loc = inner_node.location
336
- loc&.end_offset || 0
337
- end
338
-
339
- # Get the start position as row/column (0-based)
340
- #
341
- # @return [Hash{Symbol => Integer}]
342
- def start_point
343
- return {row: 0, column: 0} if inner_node.nil? || !inner_node.respond_to?(:location)
344
- loc = inner_node.location
345
- return {row: 0, column: 0} unless loc
346
-
347
- {row: (loc.start_line - 1), column: loc.start_column}
348
- end
349
-
350
- # Get the end position as row/column (0-based)
351
- #
352
- # @return [Hash{Symbol => Integer}]
353
- def end_point
354
- return {row: 0, column: 0} if inner_node.nil? || !inner_node.respond_to?(:location)
355
- loc = inner_node.location
356
- return {row: 0, column: 0} unless loc
357
-
358
- {row: (loc.end_line - 1), column: loc.end_column}
359
- end
360
-
361
- # Get all child nodes
362
- #
363
- # @return [Array<Node>] array of wrapped child nodes
364
- def children
365
- return [] if inner_node.nil?
366
- return [] unless inner_node.respond_to?(:child_nodes)
367
-
368
- inner_node.child_nodes.compact.map { |n| Node.new(n, source) }
369
- end
370
-
371
- # Get the text content of this node
372
- #
373
- # @return [String]
374
- def text
375
- return "" if inner_node.nil?
376
-
377
- if inner_node.respond_to?(:slice)
378
- inner_node.slice
379
- else
380
- super
381
- end
382
- end
383
-
384
- # Alias for Prism compatibility
385
- alias_method :slice, :text
386
-
387
- # Check if this node has errors
388
- #
389
- # @return [Boolean]
390
- def has_error?
391
- return false if inner_node.nil?
392
-
393
- # Check if this is an error node type
394
- return true if type.include?("missing") || type.include?("error")
395
-
396
- # Check children recursively (Prism error nodes are usually children)
397
- return false unless inner_node.respond_to?(:child_nodes)
398
- inner_node.child_nodes.compact.any? { |n| n.class.name.to_s.include?("Missing") }
399
- end
400
-
401
- # Check if this node is a "missing" node (error recovery)
402
- #
403
- # @return [Boolean]
404
- def missing?
405
- return false if inner_node.nil?
406
- type.include?("missing")
407
- end
408
-
409
- # Get a child by field name (Prism node accessor)
410
- #
411
- # Prism nodes have specific accessors for their children.
412
- #
413
- # @param name [String, Symbol] field/accessor name
414
- # @return [Node, nil] wrapped child node
415
- def child_by_field_name(name)
416
- return if inner_node.nil?
417
- return unless inner_node.respond_to?(name)
418
-
419
- result = inner_node.public_send(name)
420
- return if result.nil?
421
-
422
- # Wrap if it's a node
423
- result.is_a?(::Prism::Node) ? Node.new(result, source) : nil
424
- end
425
-
426
- alias_method :field, :child_by_field_name
427
-
428
- # String representation
429
- #
430
- # @return [String]
431
- def to_s
432
- text
433
- end
434
-
435
- # Check if node responds to a method (includes delegation to inner_node)
436
- #
437
- # @param method_name [Symbol] method to check
438
- # @param include_private [Boolean] include private methods
439
- # @return [Boolean]
440
- def respond_to_missing?(method_name, include_private = false)
441
- return false if inner_node.nil?
442
- inner_node.respond_to?(method_name, include_private) || super
443
- end
444
-
445
- # Delegate unknown methods to the underlying Prism node
446
- #
447
- # This provides passthrough access for Prism-specific node methods
448
- # like `receiver`, `message`, `arguments`, etc.
449
- #
450
- # @param method_name [Symbol] method to call
451
- # @param args [Array] arguments to pass
452
- # @param kwargs [Hash] keyword arguments
453
- # @param block [Proc] block to pass
454
- # @return [Object] result from the underlying node
455
- def method_missing(method_name, *args, **kwargs, &block)
456
- if inner_node&.respond_to?(method_name)
457
- inner_node.public_send(method_name, *args, **kwargs, &block)
458
- else
459
- super
460
- end
461
- end
462
- end
463
-
464
- # Register the availability checker for RSpec dependency tags
465
- TreeHaver::BackendRegistry.register_availability_checker(:prism) do
466
- available?
467
- end
468
- end
469
- end
470
- end
471
-