tree_haver 5.0.5 → 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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/LICENSE.md +13 -0
- data/README.md +299 -660
- data/lib/tree_haver/backend_api.rb +78 -35
- data/lib/tree_haver/backend_context.rb +28 -0
- data/lib/tree_haver/backend_registry.rb +119 -382
- data/lib/tree_haver/backends/citrus.rb +55 -53
- data/lib/tree_haver/backends/ffi.rb +105 -101
- data/lib/tree_haver/backends/java.rb +92 -76
- data/lib/tree_haver/backends/mri.rb +44 -39
- data/lib/tree_haver/backends/parslet.rb +53 -48
- data/lib/tree_haver/backends/prism.rb +140 -43
- data/lib/tree_haver/backends/psych.rb +30 -26
- data/lib/tree_haver/backends/rust.rb +21 -17
- data/lib/tree_haver/backends/tslp.rb +274 -0
- data/lib/tree_haver/base/comment.rb +320 -0
- data/lib/tree_haver/base/language.rb +2 -2
- data/lib/tree_haver/base/node.rb +39 -31
- data/lib/tree_haver/base/parser.rb +4 -0
- data/lib/tree_haver/base/point.rb +3 -3
- data/lib/tree_haver/base/tree.rb +1 -1
- data/lib/tree_haver/citrus_grammar_finder.rb +21 -26
- data/lib/tree_haver/contracts.rb +1025 -0
- data/lib/tree_haver/grammar_finder.rb +125 -70
- data/lib/tree_haver/kaitai_backend.rb +30 -0
- data/lib/tree_haver/language.rb +36 -37
- data/lib/tree_haver/language_pack.rb +41 -0
- data/lib/tree_haver/language_registry.rb +34 -3
- data/lib/tree_haver/library_path_utils.rb +7 -7
- data/lib/tree_haver/node.rb +40 -31
- data/lib/tree_haver/parser.rb +44 -37
- data/lib/tree_haver/parslet_grammar_finder.rb +19 -26
- data/lib/tree_haver/path_validator.rb +39 -36
- data/lib/tree_haver/peg_backends.rb +76 -0
- data/lib/tree_haver/rspec/dependency_tags.rb +23 -1363
- data/lib/tree_haver/rspec.rb +1 -31
- data/lib/tree_haver/tree.rb +16 -7
- data/lib/tree_haver/version.rb +5 -14
- data/lib/tree_haver.rb +536 -1240
- data/sig/tree_haver.rbs +1 -229
- data.tar.gz.sig +0 -0
- metadata +154 -70
- metadata.gz.sig +0 -0
- data/CHANGELOG.md +0 -1393
- data/CITATION.cff +0 -20
- data/CODE_OF_CONDUCT.md +0 -134
- data/CONTRIBUTING.md +0 -359
- data/FUNDING.md +0 -74
- data/LICENSE.txt +0 -21
- data/REEK +0 -0
- data/RUBOCOP.md +0 -71
- data/SECURITY.md +0 -21
- data/lib/tree_haver/base.rb +0 -12
- data/lib/tree_haver/compat.rb +0 -43
- data/lib/tree_haver/rspec/testable_node.rb +0 -217
- data/sig/tree_haver/backends.rbs +0 -352
- data/sig/tree_haver/grammar_finder.rbs +0 -29
- data/sig/tree_haver/path_validator.rbs +0 -32
|
@@ -28,6 +28,19 @@ module TreeHaver
|
|
|
28
28
|
# TreeHaver::BackendAPI.validate_node!(node_instance)
|
|
29
29
|
#
|
|
30
30
|
module BackendAPI
|
|
31
|
+
# Descriptive levels for backend comment support.
|
|
32
|
+
#
|
|
33
|
+
# - :full - backend can expose comment nodes plus strong attachment hints
|
|
34
|
+
# - :partial - backend can expose comments, but ownership/attachment is incomplete
|
|
35
|
+
# - :nodes_only - backend can surface comment nodes/tokens only
|
|
36
|
+
# - :none - backend does not expose comments through TreeHaver
|
|
37
|
+
COMMENT_SUPPORT_LEVELS = %i[
|
|
38
|
+
full
|
|
39
|
+
partial
|
|
40
|
+
nodes_only
|
|
41
|
+
none
|
|
42
|
+
].freeze
|
|
43
|
+
|
|
31
44
|
# Required methods for Language class/instances
|
|
32
45
|
#
|
|
33
46
|
# All backends MUST implement `from_library` for API consistency.
|
|
@@ -120,7 +133,7 @@ module TreeHaver
|
|
|
120
133
|
has_error?: %i[has_error],
|
|
121
134
|
missing?: %i[is_missing? is_missing],
|
|
122
135
|
next_sibling: %i[next_named_sibling],
|
|
123
|
-
prev_sibling: %i[previous_sibling previous_named_sibling prev_named_sibling]
|
|
136
|
+
prev_sibling: %i[previous_sibling previous_named_sibling prev_named_sibling]
|
|
124
137
|
}.freeze
|
|
125
138
|
|
|
126
139
|
class << self
|
|
@@ -134,7 +147,7 @@ module TreeHaver
|
|
|
134
147
|
valid: true,
|
|
135
148
|
errors: [],
|
|
136
149
|
warnings: [],
|
|
137
|
-
capabilities: {}
|
|
150
|
+
capabilities: {}
|
|
138
151
|
}
|
|
139
152
|
|
|
140
153
|
# Check module-level methods
|
|
@@ -144,7 +157,7 @@ module TreeHaver
|
|
|
144
157
|
if backend_module.const_defined?(:Language)
|
|
145
158
|
validate_language(backend_module::Language, results)
|
|
146
159
|
else
|
|
147
|
-
results[:errors] <<
|
|
160
|
+
results[:errors] << 'Missing Language class'
|
|
148
161
|
results[:valid] = false
|
|
149
162
|
end
|
|
150
163
|
|
|
@@ -152,7 +165,7 @@ module TreeHaver
|
|
|
152
165
|
if backend_module.const_defined?(:Parser)
|
|
153
166
|
validate_parser(backend_module::Parser, results)
|
|
154
167
|
else
|
|
155
|
-
results[:errors] <<
|
|
168
|
+
results[:errors] << 'Missing Parser class'
|
|
156
169
|
results[:valid] = false
|
|
157
170
|
end
|
|
158
171
|
|
|
@@ -160,20 +173,18 @@ module TreeHaver
|
|
|
160
173
|
if backend_module.const_defined?(:Tree)
|
|
161
174
|
validate_tree(backend_module::Tree, results)
|
|
162
175
|
else
|
|
163
|
-
results[:warnings] <<
|
|
176
|
+
results[:warnings] << 'No Tree class (backend returns raw trees)'
|
|
164
177
|
end
|
|
165
178
|
|
|
166
179
|
# Check Node class if present (wrapper backends)
|
|
167
180
|
if backend_module.const_defined?(:Node)
|
|
168
181
|
validate_node_class(backend_module::Node, results, strict: strict)
|
|
169
182
|
else
|
|
170
|
-
results[:warnings] <<
|
|
183
|
+
results[:warnings] << 'No Node class (backend returns raw nodes, TreeHaver::Node will wrap)'
|
|
171
184
|
end
|
|
172
185
|
|
|
173
186
|
# Fail on warnings in strict mode
|
|
174
|
-
if strict && results[:warnings].any?
|
|
175
|
-
results[:valid] = false
|
|
176
|
-
end
|
|
187
|
+
results[:valid] = false if strict && results[:warnings].any?
|
|
177
188
|
|
|
178
189
|
results
|
|
179
190
|
end
|
|
@@ -188,9 +199,9 @@ module TreeHaver
|
|
|
188
199
|
results = validate(backend_module, strict: strict)
|
|
189
200
|
unless results[:valid]
|
|
190
201
|
raise TreeHaver::Error,
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
202
|
+
"Backend #{backend_module.name} API validation failed:\n " \
|
|
203
|
+
"Errors: #{results[:errors].join(', ')}\n " \
|
|
204
|
+
"Warnings: #{results[:warnings].join(', ')}"
|
|
194
205
|
end
|
|
195
206
|
results
|
|
196
207
|
end
|
|
@@ -205,7 +216,7 @@ module TreeHaver
|
|
|
205
216
|
errors: [],
|
|
206
217
|
warnings: [],
|
|
207
218
|
supported_methods: [],
|
|
208
|
-
unsupported_methods: []
|
|
219
|
+
unsupported_methods: []
|
|
209
220
|
}
|
|
210
221
|
|
|
211
222
|
# Check required methods
|
|
@@ -235,13 +246,51 @@ module TreeHaver
|
|
|
235
246
|
|
|
236
247
|
def validate_module_methods(mod, results)
|
|
237
248
|
unless mod.singleton_class.method_defined?(:available?)
|
|
238
|
-
results[:errors] <<
|
|
249
|
+
results[:errors] << 'Missing module method: available?'
|
|
239
250
|
results[:valid] = false
|
|
240
251
|
end
|
|
241
252
|
|
|
242
253
|
unless mod.singleton_class.method_defined?(:capabilities)
|
|
243
|
-
results[:warnings] <<
|
|
254
|
+
results[:warnings] << 'Missing module method: capabilities'
|
|
255
|
+
return
|
|
244
256
|
end
|
|
257
|
+
|
|
258
|
+
validate_capabilities_hash(mod.capabilities, results)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def validate_capabilities_hash(capabilities, results)
|
|
262
|
+
return if capabilities.nil? || capabilities.empty?
|
|
263
|
+
|
|
264
|
+
unless capabilities.is_a?(Hash)
|
|
265
|
+
results[:errors] << 'Backend capabilities must return a Hash'
|
|
266
|
+
results[:valid] = false
|
|
267
|
+
return
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
comment_support = capabilities[:comment_support]
|
|
271
|
+
if comment_support.nil?
|
|
272
|
+
results[:warnings] << 'Capabilities missing :comment_support'
|
|
273
|
+
return
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
unless COMMENT_SUPPORT_LEVELS.include?(comment_support)
|
|
277
|
+
results[:errors] << "Invalid :comment_support #{comment_support.inspect}; expected one of #{COMMENT_SUPPORT_LEVELS.inspect}"
|
|
278
|
+
results[:valid] = false
|
|
279
|
+
return
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
results[:capabilities][:comment_support] = comment_support
|
|
283
|
+
|
|
284
|
+
return unless capabilities.key?(:comment_attachment_hints)
|
|
285
|
+
|
|
286
|
+
attachment_hints = capabilities[:comment_attachment_hints]
|
|
287
|
+
unless [true, false].include?(attachment_hints)
|
|
288
|
+
results[:errors] << "Invalid :comment_attachment_hints #{attachment_hints.inspect}; expected true or false"
|
|
289
|
+
results[:valid] = false
|
|
290
|
+
return
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
results[:capabilities][:comment_attachment_hints] = attachment_hints
|
|
245
294
|
end
|
|
246
295
|
|
|
247
296
|
def validate_language(klass, results)
|
|
@@ -249,19 +298,17 @@ module TreeHaver
|
|
|
249
298
|
# Language-specific backends should implement it to ignore path/symbol
|
|
250
299
|
# and return their single language (for API consistency)
|
|
251
300
|
unless klass.singleton_class.method_defined?(:from_library)
|
|
252
|
-
results[:errors] <<
|
|
301
|
+
results[:errors] << 'Language missing required class method: from_library'
|
|
253
302
|
results[:valid] = false
|
|
254
303
|
end
|
|
255
304
|
|
|
256
305
|
# Check for optional convenience methods
|
|
257
306
|
optional_methods = LANGUAGE_OPTIONAL_CLASS_METHODS.select { |m| klass.singleton_class.method_defined?(m) }
|
|
258
|
-
if optional_methods.any?
|
|
259
|
-
results[:capabilities][:language_shortcuts] = optional_methods
|
|
260
|
-
end
|
|
307
|
+
results[:capabilities][:language_shortcuts] = optional_methods if optional_methods.any?
|
|
261
308
|
|
|
262
309
|
results[:capabilities][:language] = {
|
|
263
310
|
class_methods: LANGUAGE_CLASS_METHODS.select { |m| klass.singleton_class.method_defined?(m) } +
|
|
264
|
-
|
|
311
|
+
optional_methods
|
|
265
312
|
}
|
|
266
313
|
end
|
|
267
314
|
|
|
@@ -282,9 +329,7 @@ module TreeHaver
|
|
|
282
329
|
end
|
|
283
330
|
|
|
284
331
|
PARSER_OPTIONAL_METHODS.each do |method|
|
|
285
|
-
unless klass.method_defined?(method)
|
|
286
|
-
results[:warnings] << "Parser missing optional method: #{method}"
|
|
287
|
-
end
|
|
332
|
+
results[:warnings] << "Parser missing optional method: #{method}" unless klass.method_defined?(method)
|
|
288
333
|
end
|
|
289
334
|
end
|
|
290
335
|
|
|
@@ -297,9 +342,7 @@ module TreeHaver
|
|
|
297
342
|
end
|
|
298
343
|
|
|
299
344
|
TREE_OPTIONAL_METHODS.each do |method|
|
|
300
|
-
unless klass.method_defined?(method)
|
|
301
|
-
results[:warnings] << "Tree missing optional method: #{method}"
|
|
302
|
-
end
|
|
345
|
+
results[:warnings] << "Tree missing optional method: #{method}" unless klass.method_defined?(method)
|
|
303
346
|
end
|
|
304
347
|
end
|
|
305
348
|
|
|
@@ -312,20 +355,20 @@ module TreeHaver
|
|
|
312
355
|
end
|
|
313
356
|
|
|
314
357
|
NODE_OPTIONAL_METHODS.each do |method|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
358
|
+
next if has_method_or_alias?(klass, method)
|
|
359
|
+
|
|
360
|
+
msg = "Node missing optional method: #{method}"
|
|
361
|
+
if strict
|
|
362
|
+
results[:errors] << msg
|
|
363
|
+
results[:valid] = false
|
|
364
|
+
else
|
|
365
|
+
results[:warnings] << msg
|
|
323
366
|
end
|
|
324
367
|
end
|
|
325
368
|
|
|
326
369
|
results[:capabilities][:node] = {
|
|
327
370
|
required: NODE_INSTANCE_METHODS.select { |m| has_method_or_alias?(klass, m) },
|
|
328
|
-
optional: NODE_OPTIONAL_METHODS.select { |m| has_method_or_alias?(klass, m) }
|
|
371
|
+
optional: NODE_OPTIONAL_METHODS.select { |m| has_method_or_alias?(klass, m) }
|
|
329
372
|
}
|
|
330
373
|
end
|
|
331
374
|
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TreeHaver
|
|
4
|
+
BACKEND_CONTEXT_KEY = :structured_merge_tree_haver_backend
|
|
5
|
+
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def current_backend_id
|
|
9
|
+
Thread.current[BACKEND_CONTEXT_KEY]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def with_backend(backend_id)
|
|
13
|
+
validate_backend_id!(backend_id)
|
|
14
|
+
|
|
15
|
+
previous_backend = Thread.current[BACKEND_CONTEXT_KEY]
|
|
16
|
+
Thread.current[BACKEND_CONTEXT_KEY] = backend_id.to_s
|
|
17
|
+
yield
|
|
18
|
+
ensure
|
|
19
|
+
Thread.current[BACKEND_CONTEXT_KEY] = previous_backend
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def validate_backend_id!(backend_id)
|
|
23
|
+
return if BackendRegistry.fetch(backend_id.to_s)
|
|
24
|
+
|
|
25
|
+
raise ArgumentError, "Unknown tree_haver backend #{backend_id}."
|
|
26
|
+
end
|
|
27
|
+
private_class_method :validate_backend_id!
|
|
28
|
+
end
|