tree_haver 1.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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +48 -0
- data/CITATION.cff +20 -0
- data/CODE_OF_CONDUCT.md +134 -0
- data/CONTRIBUTING.md +227 -0
- data/FUNDING.md +74 -0
- data/LICENSE.txt +21 -0
- data/README.md +1260 -0
- data/REEK +0 -0
- data/RUBOCOP.md +71 -0
- data/SECURITY.md +21 -0
- data/lib/tree_haver/backends/ffi.rb +410 -0
- data/lib/tree_haver/backends/java.rb +568 -0
- data/lib/tree_haver/backends/mri.rb +129 -0
- data/lib/tree_haver/backends/rust.rb +175 -0
- data/lib/tree_haver/compat.rb +43 -0
- data/lib/tree_haver/grammar_finder.rb +245 -0
- data/lib/tree_haver/language_registry.rb +139 -0
- data/lib/tree_haver/path_validator.rb +333 -0
- data/lib/tree_haver/version.rb +20 -0
- data/lib/tree_haver.rb +710 -0
- data/sig/tree_haver/backends.rbs +285 -0
- data/sig/tree_haver/grammar_finder.rbs +29 -0
- data/sig/tree_haver/path_validator.rbs +31 -0
- data/sig/tree_haver.rbs +131 -0
- data.tar.gz.sig +0 -0
- metadata +298 -0
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Type definitions for TreeHaver backends
|
|
2
|
+
|
|
3
|
+
module TreeHaver
|
|
4
|
+
module Backends
|
|
5
|
+
# MRI backend using ruby_tree_sitter
|
|
6
|
+
module MRI
|
|
7
|
+
# Check if the MRI backend is available
|
|
8
|
+
def self.available?: () -> bool
|
|
9
|
+
|
|
10
|
+
# Get capabilities supported by this backend
|
|
11
|
+
def self.capabilities: () -> Hash[Symbol, untyped]
|
|
12
|
+
|
|
13
|
+
class Language
|
|
14
|
+
# Load a language from a shared library path
|
|
15
|
+
def self.from_path: (String path) -> untyped
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Parser
|
|
19
|
+
# Create a new parser instance
|
|
20
|
+
def initialize: () -> void
|
|
21
|
+
|
|
22
|
+
# Set the language for this parser
|
|
23
|
+
def language=: (untyped lang) -> untyped
|
|
24
|
+
|
|
25
|
+
# Parse source code
|
|
26
|
+
def parse: (String source) -> untyped
|
|
27
|
+
|
|
28
|
+
# Parse source code with optional incremental parsing
|
|
29
|
+
def parse_string: (untyped? old_tree, String source) -> untyped
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
@parser: untyped
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
class Tree
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class Node
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# FFI-based backend for calling libtree-sitter directly
|
|
44
|
+
module FFI
|
|
45
|
+
# Native FFI bindings to libtree-sitter
|
|
46
|
+
module Native
|
|
47
|
+
# Get list of candidate library names
|
|
48
|
+
def self.lib_candidates: () -> Array[String]
|
|
49
|
+
|
|
50
|
+
# Load the Tree-sitter runtime library
|
|
51
|
+
def self.try_load!: () -> void
|
|
52
|
+
|
|
53
|
+
# Check if the library is loaded
|
|
54
|
+
def self.loaded?: () -> bool
|
|
55
|
+
|
|
56
|
+
# FFI struct representation of TSNode
|
|
57
|
+
class TSNode < ::FFI::Struct
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.ts_parser_new: () -> ::FFI::Pointer
|
|
61
|
+
|
|
62
|
+
def self.ts_parser_delete: (::FFI::Pointer ptr) -> void
|
|
63
|
+
|
|
64
|
+
def self.ts_parser_set_language: (::FFI::Pointer parser, ::FFI::Pointer language) -> bool
|
|
65
|
+
|
|
66
|
+
def self.ts_parser_parse_string: (::FFI::Pointer parser, ::FFI::Pointer old_tree, String source, Integer length) -> ::FFI::Pointer
|
|
67
|
+
|
|
68
|
+
def self.ts_tree_delete: (::FFI::Pointer ptr) -> void
|
|
69
|
+
|
|
70
|
+
def self.ts_tree_root_node: (::FFI::Pointer tree) -> TSNode
|
|
71
|
+
|
|
72
|
+
def self.ts_node_type: (TSNode node) -> String
|
|
73
|
+
|
|
74
|
+
def self.ts_node_child_count: (TSNode node) -> Integer
|
|
75
|
+
|
|
76
|
+
def self.ts_node_child: (TSNode node, Integer index) -> TSNode
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Check if the FFI backend is available
|
|
80
|
+
def self.available?: () -> bool
|
|
81
|
+
|
|
82
|
+
# Get capabilities supported by this backend
|
|
83
|
+
def self.capabilities: () -> Hash[Symbol, untyped]
|
|
84
|
+
|
|
85
|
+
class Language
|
|
86
|
+
# The FFI pointer to the TSLanguage struct
|
|
87
|
+
attr_reader pointer: ::FFI::Pointer
|
|
88
|
+
|
|
89
|
+
# Load a language from a shared library
|
|
90
|
+
def self.from_library: (String path, ?symbol: String?, ?name: String?) -> Language
|
|
91
|
+
|
|
92
|
+
# Alias for from_library
|
|
93
|
+
def self.from_path: (String path, ?symbol: String?, ?name: String?) -> Language
|
|
94
|
+
|
|
95
|
+
# Convert to FFI pointer
|
|
96
|
+
def to_ptr: () -> ::FFI::Pointer
|
|
97
|
+
|
|
98
|
+
private
|
|
99
|
+
|
|
100
|
+
def initialize: (::FFI::Pointer ptr) -> void
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
class Parser
|
|
104
|
+
# Create a new parser instance
|
|
105
|
+
def initialize: () -> void
|
|
106
|
+
|
|
107
|
+
# Get finalizer for cleanup
|
|
108
|
+
def self.finalizer: (::FFI::Pointer ptr) -> Proc
|
|
109
|
+
|
|
110
|
+
# Set the language for this parser
|
|
111
|
+
def language=: (Language lang) -> Language
|
|
112
|
+
|
|
113
|
+
# Parse source code into a syntax tree
|
|
114
|
+
def parse: (String source) -> Tree
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
@parser: ::FFI::Pointer
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
class Tree
|
|
122
|
+
# Get finalizer for cleanup
|
|
123
|
+
def self.finalizer: (::FFI::Pointer ptr) -> Proc
|
|
124
|
+
|
|
125
|
+
# Get the root node of the syntax tree
|
|
126
|
+
def root_node: () -> Node
|
|
127
|
+
|
|
128
|
+
private
|
|
129
|
+
|
|
130
|
+
def initialize: (::FFI::Pointer ptr) -> void
|
|
131
|
+
|
|
132
|
+
@ptr: ::FFI::Pointer
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
class Node
|
|
136
|
+
# Get the type name of this node
|
|
137
|
+
def type: () -> String
|
|
138
|
+
|
|
139
|
+
# Iterate over child nodes
|
|
140
|
+
def each: () { (Node child) -> void } -> nil
|
|
141
|
+
| () -> Enumerator[Node, nil]
|
|
142
|
+
|
|
143
|
+
private
|
|
144
|
+
|
|
145
|
+
def initialize: (Native::TSNode ts_node_value) -> void
|
|
146
|
+
|
|
147
|
+
@val: Native::TSNode
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Java backend for JRuby using java-tree-sitter
|
|
152
|
+
module Java
|
|
153
|
+
JAVA_PACKAGE: String
|
|
154
|
+
|
|
155
|
+
# Attempt to append JARs from ENV to JRuby classpath
|
|
156
|
+
def self.add_jars_from_env!: () -> void
|
|
157
|
+
|
|
158
|
+
# Check if the Java backend is available
|
|
159
|
+
def self.available?: () -> bool
|
|
160
|
+
|
|
161
|
+
# Reset the load state (for testing)
|
|
162
|
+
def self.reset!: () -> void
|
|
163
|
+
|
|
164
|
+
# Get the loaded Java classes
|
|
165
|
+
def self.java_classes: () -> Hash[Symbol, untyped]
|
|
166
|
+
|
|
167
|
+
# Get capabilities supported by this backend
|
|
168
|
+
def self.capabilities: () -> Hash[Symbol, untyped]
|
|
169
|
+
|
|
170
|
+
class Language
|
|
171
|
+
attr_reader impl: untyped
|
|
172
|
+
|
|
173
|
+
def initialize: (untyped impl) -> void
|
|
174
|
+
|
|
175
|
+
def self.from_library: (String path, ?symbol: String?, ?name: String?) -> Language
|
|
176
|
+
|
|
177
|
+
def self.from_path: (String path, ?symbol: String?, ?name: String?) -> Language
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
class Parser
|
|
181
|
+
def initialize: () -> void
|
|
182
|
+
|
|
183
|
+
def language=: (Language | untyped lang) -> void
|
|
184
|
+
|
|
185
|
+
def parse: (String source) -> Tree
|
|
186
|
+
|
|
187
|
+
def parse_string: (Tree? old_tree, String source) -> Tree
|
|
188
|
+
|
|
189
|
+
private
|
|
190
|
+
|
|
191
|
+
@parser: untyped
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
class Tree
|
|
195
|
+
attr_reader impl: untyped
|
|
196
|
+
|
|
197
|
+
def initialize: (untyped impl) -> void
|
|
198
|
+
|
|
199
|
+
def root_node: () -> Node
|
|
200
|
+
|
|
201
|
+
def edit: (
|
|
202
|
+
start_byte: Integer,
|
|
203
|
+
old_end_byte: Integer,
|
|
204
|
+
new_end_byte: Integer,
|
|
205
|
+
start_point: Hash[Symbol, Integer],
|
|
206
|
+
old_end_point: Hash[Symbol, Integer],
|
|
207
|
+
new_end_point: Hash[Symbol, Integer]
|
|
208
|
+
) -> void
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
class Node
|
|
212
|
+
attr_reader impl: untyped
|
|
213
|
+
|
|
214
|
+
def initialize: (untyped impl) -> void
|
|
215
|
+
|
|
216
|
+
def type: () -> String
|
|
217
|
+
|
|
218
|
+
def child_count: () -> Integer
|
|
219
|
+
|
|
220
|
+
def child: (Integer index) -> Node
|
|
221
|
+
|
|
222
|
+
def each: () { (Node) -> void } -> void
|
|
223
|
+
| () -> Enumerator[Node, nil]
|
|
224
|
+
|
|
225
|
+
def start_byte: () -> Integer
|
|
226
|
+
|
|
227
|
+
def end_byte: () -> Integer
|
|
228
|
+
|
|
229
|
+
def start_point: () -> Hash[Symbol, Integer]
|
|
230
|
+
|
|
231
|
+
def end_point: () -> Hash[Symbol, Integer]
|
|
232
|
+
|
|
233
|
+
def has_error?: () -> bool
|
|
234
|
+
|
|
235
|
+
def missing?: () -> bool
|
|
236
|
+
|
|
237
|
+
def text: () -> String
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Rust backend using tree_stump
|
|
242
|
+
module Rust
|
|
243
|
+
# Check if the Rust backend is available
|
|
244
|
+
def self.available?: () -> bool
|
|
245
|
+
|
|
246
|
+
# Reset the load state (for testing)
|
|
247
|
+
def self.reset!: () -> void
|
|
248
|
+
|
|
249
|
+
# Get capabilities supported by this backend
|
|
250
|
+
def self.capabilities: () -> Hash[Symbol, untyped]
|
|
251
|
+
|
|
252
|
+
class Language
|
|
253
|
+
# Load a language from a shared library path
|
|
254
|
+
def self.from_library: (String path, ?symbol: String?, ?name: String?) -> untyped
|
|
255
|
+
|
|
256
|
+
# Alias for from_library
|
|
257
|
+
def self.from_path: (String path) -> untyped
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
class Parser
|
|
261
|
+
# Create a new parser instance
|
|
262
|
+
def initialize: () -> void
|
|
263
|
+
|
|
264
|
+
# Set the language for this parser
|
|
265
|
+
def language=: (untyped lang) -> untyped
|
|
266
|
+
|
|
267
|
+
# Parse source code
|
|
268
|
+
def parse: (String source) -> untyped
|
|
269
|
+
|
|
270
|
+
# Parse source code with optional incremental parsing
|
|
271
|
+
def parse_string: (untyped? old_tree, String source) -> untyped
|
|
272
|
+
|
|
273
|
+
private
|
|
274
|
+
|
|
275
|
+
@parser: untyped
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
class Tree
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
class Node
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Type signatures for TreeHaver::GrammarFinder
|
|
2
|
+
module TreeHaver
|
|
3
|
+
# Generic utility for finding tree-sitter grammar shared libraries
|
|
4
|
+
class GrammarFinder
|
|
5
|
+
BASE_SEARCH_DIRS: Array[String]
|
|
6
|
+
|
|
7
|
+
attr_reader language_name: Symbol
|
|
8
|
+
attr_reader extra_paths: Array[String]
|
|
9
|
+
|
|
10
|
+
def initialize: (Symbol | String language_name, ?extra_paths: Array[String], ?validate: bool) -> void
|
|
11
|
+
|
|
12
|
+
def env_var_name: () -> String
|
|
13
|
+
def symbol_name: () -> String
|
|
14
|
+
def library_filename: () -> String
|
|
15
|
+
def search_paths: () -> Array[String]
|
|
16
|
+
def find_library_path: () -> String?
|
|
17
|
+
def find_library_path_safe: () -> String?
|
|
18
|
+
def available?: () -> bool
|
|
19
|
+
def available_safe?: () -> bool
|
|
20
|
+
def register!: (?raise_on_missing: bool) -> bool
|
|
21
|
+
def search_info: () -> Hash[Symbol, untyped]
|
|
22
|
+
def not_found_message: () -> String
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def platform_extension: () -> String
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Type signatures for TreeHaver::PathValidator
|
|
2
|
+
module TreeHaver
|
|
3
|
+
# Security utilities for validating paths before loading shared libraries
|
|
4
|
+
module PathValidator
|
|
5
|
+
ALLOWED_EXTENSIONS: Array[String]
|
|
6
|
+
DEFAULT_TRUSTED_DIRECTORIES: Array[String]
|
|
7
|
+
TRUSTED_DIRS_ENV_VAR: String
|
|
8
|
+
MAX_PATH_LENGTH: Integer
|
|
9
|
+
VALID_FILENAME_PATTERN: Regexp
|
|
10
|
+
VALID_LANGUAGE_PATTERN: Regexp
|
|
11
|
+
VALID_SYMBOL_PATTERN: Regexp
|
|
12
|
+
|
|
13
|
+
# Trusted directory management
|
|
14
|
+
def self.trusted_directories: () -> Array[String]
|
|
15
|
+
def self.add_trusted_directory: (String directory) -> void
|
|
16
|
+
def self.remove_trusted_directory: (String directory) -> void
|
|
17
|
+
def self.clear_custom_trusted_directories!: () -> void
|
|
18
|
+
def self.custom_trusted_directories: () -> Array[String]
|
|
19
|
+
|
|
20
|
+
# Validation methods
|
|
21
|
+
def self.safe_library_path?: (String? path, ?require_trusted_dir: bool) -> bool
|
|
22
|
+
def self.in_trusted_directory?: (String? path) -> bool
|
|
23
|
+
def self.safe_language_name?: (String | Symbol | nil name) -> bool
|
|
24
|
+
def self.safe_symbol_name?: (String? symbol) -> bool
|
|
25
|
+
def self.safe_backend_name?: (String | Symbol | nil backend) -> bool
|
|
26
|
+
def self.sanitize_language_name: (String | Symbol | nil name) -> Symbol?
|
|
27
|
+
def self.validation_errors: (String? path) -> Array[String]
|
|
28
|
+
def self.windows_absolute_path?: (String path) -> bool
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
data/sig/tree_haver.rbs
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Type definitions for TreeHaver
|
|
2
|
+
#
|
|
3
|
+
# TreeHaver is a cross-Ruby adapter for the Tree-sitter parsing library
|
|
4
|
+
|
|
5
|
+
module TreeHaver
|
|
6
|
+
VERSION: String
|
|
7
|
+
|
|
8
|
+
# Base error class
|
|
9
|
+
class Error < StandardError
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Raised when a backend or feature is not available
|
|
13
|
+
class NotAvailable < Error
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Module namespace for backend implementations
|
|
17
|
+
module Backends
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Get the current backend selection
|
|
21
|
+
def self.backend: () -> Symbol
|
|
22
|
+
|
|
23
|
+
# Set the backend to use
|
|
24
|
+
def self.backend=: (Symbol | String | nil name) -> (Symbol | nil)
|
|
25
|
+
|
|
26
|
+
# Reset backend selection memoization
|
|
27
|
+
def self.reset_backend!: (?to: Symbol | String | nil) -> void
|
|
28
|
+
|
|
29
|
+
# Determine the concrete backend module to use
|
|
30
|
+
def self.backend_module: () -> Module?
|
|
31
|
+
|
|
32
|
+
# Get capabilities of the current backend
|
|
33
|
+
def self.capabilities: () -> Hash[Symbol, untyped]
|
|
34
|
+
|
|
35
|
+
# Register a language helper by name
|
|
36
|
+
def self.register_language: (Symbol | String name, path: String, ?symbol: String?) -> void
|
|
37
|
+
|
|
38
|
+
# Unregister a previously registered language helper
|
|
39
|
+
def self.unregister_language: (Symbol | String name) -> void
|
|
40
|
+
|
|
41
|
+
# Clear all registered languages
|
|
42
|
+
def self.clear_languages!: () -> void
|
|
43
|
+
|
|
44
|
+
# Fetch a registered language entry
|
|
45
|
+
def self.registered_language: (Symbol | String name) -> Hash[Symbol, String?]?
|
|
46
|
+
|
|
47
|
+
# Represents a Tree-sitter language grammar
|
|
48
|
+
class Language
|
|
49
|
+
# Load a language grammar from a shared library
|
|
50
|
+
def self.from_library: (String path, ?symbol: String?, ?name: String?) -> Language
|
|
51
|
+
|
|
52
|
+
# Alias for from_library
|
|
53
|
+
def self.from_path: (String path, ?symbol: String?, ?name: String?) -> Language
|
|
54
|
+
|
|
55
|
+
def self.method_missing: (Symbol method_name, *untyped args, **untyped kwargs) ?{ () -> untyped } -> Language
|
|
56
|
+
|
|
57
|
+
def self.respond_to_missing?: (Symbol method_name, ?bool include_private) -> bool
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Represents a Tree-sitter parser instance
|
|
61
|
+
class Parser
|
|
62
|
+
# Create a new parser instance
|
|
63
|
+
def initialize: () -> void
|
|
64
|
+
|
|
65
|
+
# Set the language grammar for this parser
|
|
66
|
+
def language=: (Language lang) -> Language
|
|
67
|
+
|
|
68
|
+
# Parse source code into a syntax tree
|
|
69
|
+
def parse: (String source) -> Tree
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
@impl: untyped
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Represents a parsed syntax tree
|
|
77
|
+
class Tree
|
|
78
|
+
# Get the root node of the syntax tree
|
|
79
|
+
def root_node: () -> Node
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
def initialize: (untyped impl) -> void
|
|
84
|
+
|
|
85
|
+
@impl: untyped
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Represents a node in the syntax tree
|
|
89
|
+
class Node
|
|
90
|
+
# Get the type name of this node
|
|
91
|
+
def type: () -> String
|
|
92
|
+
|
|
93
|
+
# Iterate over child nodes
|
|
94
|
+
def each: () { (Node child) -> void } -> nil
|
|
95
|
+
| () -> Enumerator[Node, nil]
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def initialize: (untyped impl) -> void
|
|
100
|
+
|
|
101
|
+
@impl: untyped
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Thread-safe language registrations and cache
|
|
105
|
+
module LanguageRegistry
|
|
106
|
+
# Register a language helper by name
|
|
107
|
+
def self.register: (Symbol | String name, path: String, ?symbol: String?) -> void
|
|
108
|
+
|
|
109
|
+
# Unregister a previously registered language helper
|
|
110
|
+
def self.unregister: (Symbol | String name) -> void
|
|
111
|
+
|
|
112
|
+
# Fetch a registration entry
|
|
113
|
+
def self.registered: (Symbol | String name) -> Hash[Symbol, String?]?
|
|
114
|
+
|
|
115
|
+
# Clear all registrations
|
|
116
|
+
def self.clear_registrations!: () -> void
|
|
117
|
+
|
|
118
|
+
# Fetch a cached language by key or compute and store it
|
|
119
|
+
def self.fetch: [T] (Array[untyped] key) { () -> T } -> T
|
|
120
|
+
|
|
121
|
+
# Clear the language cache
|
|
122
|
+
def self.clear_cache!: () -> void
|
|
123
|
+
|
|
124
|
+
# Clear everything (registrations and cache)
|
|
125
|
+
def self.clear_all!: () -> void
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
module Version
|
|
129
|
+
VERSION: String
|
|
130
|
+
end
|
|
131
|
+
end
|
data.tar.gz.sig
ADDED
|
Binary file
|