tree_stump 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rspec +1 -0
- data/Cargo.lock +368 -0
- data/Cargo.toml +7 -0
- data/README.md +61 -0
- data/Rakefile +18 -0
- data/ext/tree_stump/Cargo.toml +14 -0
- data/ext/tree_stump/extconf.rb +6 -0
- data/ext/tree_stump/src/data.rs +103 -0
- data/ext/tree_stump/src/language.rs +74 -0
- data/ext/tree_stump/src/lib.rs +311 -0
- data/ext/tree_stump/src/parser.rs +67 -0
- data/ext/tree_stump/src/query.rs +209 -0
- data/ext/tree_stump/src/tree.rs +531 -0
- data/ext/tree_stump/src/util.rs +19 -0
- data/lib/tree_stump/version.rb +5 -0
- data/lib/tree_stump.rb +15 -0
- data/sig/tree_house.rbs +4 -0
- metadata +76 -0
@@ -0,0 +1,311 @@
|
|
1
|
+
use magnus::{function, method, prelude::*, typed_data, value::Lazy, Error, RClass, Ruby};
|
2
|
+
|
3
|
+
use libloading::Library;
|
4
|
+
use tree_sitter::ffi::TSLanguage;
|
5
|
+
|
6
|
+
use std::collections::HashMap;
|
7
|
+
use std::sync::Mutex;
|
8
|
+
use std::sync::OnceLock;
|
9
|
+
|
10
|
+
mod data;
|
11
|
+
mod language;
|
12
|
+
mod parser;
|
13
|
+
mod query;
|
14
|
+
mod tree;
|
15
|
+
mod util;
|
16
|
+
|
17
|
+
use crate::language::{LanguageRef, LookaheadIterator};
|
18
|
+
use crate::parser::Parser;
|
19
|
+
use crate::query::{Query, QueryCursor, QueryMatch};
|
20
|
+
use crate::tree::{Node, Tree, TreeCursor};
|
21
|
+
|
22
|
+
pub static LANG_LIBRARIES: OnceLock<Mutex<HashMap<String, Library>>> = OnceLock::new();
|
23
|
+
pub static LANG_LANGUAGES: OnceLock<Mutex<HashMap<String, tree_sitter::Language>>> =
|
24
|
+
OnceLock::new();
|
25
|
+
|
26
|
+
pub static QUERY_CAPTURE_CLASS: Lazy<RClass> =
|
27
|
+
Lazy::new(|ruby| ruby.define_struct(None, ("node", "index")).unwrap());
|
28
|
+
|
29
|
+
fn register_lang(lang: String, path: String) -> () {
|
30
|
+
let func_name = String::from("tree_sitter_") + ⟨
|
31
|
+
let language;
|
32
|
+
|
33
|
+
let libraries = LANG_LIBRARIES.get_or_init(|| Mutex::new(HashMap::new()));
|
34
|
+
let languages = LANG_LANGUAGES.get_or_init(|| Mutex::new(HashMap::new()));
|
35
|
+
|
36
|
+
unsafe {
|
37
|
+
let mut libraries = libraries.lock().unwrap();
|
38
|
+
let lib = libraries.entry(lang.clone()).or_insert_with(|| {
|
39
|
+
let loaded = Library::new(path).expect("Failed to load library");
|
40
|
+
loaded
|
41
|
+
});
|
42
|
+
|
43
|
+
let func: libloading::Symbol<unsafe extern "C" fn() -> *const TSLanguage> =
|
44
|
+
lib.get(func_name.as_bytes()).unwrap();
|
45
|
+
|
46
|
+
language = tree_sitter::Language::from_raw(func());
|
47
|
+
|
48
|
+
let mut languages = languages.lock().unwrap();
|
49
|
+
languages.insert(lang.to_string(), language);
|
50
|
+
};
|
51
|
+
}
|
52
|
+
|
53
|
+
fn available_langs() -> Vec<String> {
|
54
|
+
let languages = LANG_LANGUAGES.get_or_init(|| Mutex::new(HashMap::new()));
|
55
|
+
let languages = languages.lock().unwrap();
|
56
|
+
languages.keys().cloned().collect()
|
57
|
+
}
|
58
|
+
|
59
|
+
#[magnus::init]
|
60
|
+
fn init(ruby: &Ruby) -> Result<(), Error> {
|
61
|
+
let namespace = ruby.define_module("TreeStump")?;
|
62
|
+
namespace.define_singleton_method("register_lang", function!(register_lang, 2))?;
|
63
|
+
namespace.define_singleton_method("available_langs", function!(available_langs, 0))?;
|
64
|
+
|
65
|
+
let parser_class = namespace.define_class("Parser", ruby.class_object())?;
|
66
|
+
parser_class.define_singleton_method("new", function!(Parser::new, 0))?;
|
67
|
+
parser_class.define_method("set_language", method!(Parser::set_language, 1))?;
|
68
|
+
parser_class.define_method("parse", method!(Parser::parse, 1))?;
|
69
|
+
parser_class.define_method("reset", method!(Parser::reset, 0))?;
|
70
|
+
parser_class.define_method("timeout_micros", method!(Parser::timeout_micros, 0))?;
|
71
|
+
parser_class.define_method("set_timeout_micros", method!(Parser::set_timeout_micros, 1))?;
|
72
|
+
parser_class.define_method("build_query", method!(Parser::build_query, 1))?;
|
73
|
+
|
74
|
+
let tree_class = namespace.define_class("Tree", ruby.class_object())?;
|
75
|
+
tree_class.define_method("root_node", method!(Tree::root_node, 0))?;
|
76
|
+
tree_class.define_method("language", method!(Tree::language, 0))?;
|
77
|
+
tree_class.define_method("walk", method!(Tree::walk, 0))?;
|
78
|
+
tree_class.define_method("print_dot_graph", method!(Tree::print_dot_graph, 1))?;
|
79
|
+
tree_class.define_method("inspect", method!(Tree::inspect, 0))?;
|
80
|
+
|
81
|
+
let tree_cursor_class = namespace.define_class("TreeCursor", ruby.class_object())?;
|
82
|
+
tree_cursor_class.define_method("node", method!(TreeCursor::node, 0))?;
|
83
|
+
tree_cursor_class.define_method("field_id", method!(TreeCursor::field_id, 0))?;
|
84
|
+
tree_cursor_class
|
85
|
+
.define_method("goto_first_child", method!(TreeCursor::goto_first_child, 0))?;
|
86
|
+
tree_cursor_class.define_method("goto_last_child", method!(TreeCursor::goto_last_child, 0))?;
|
87
|
+
tree_cursor_class.define_method("goto_parent", method!(TreeCursor::goto_parent, 0))?;
|
88
|
+
tree_cursor_class.define_method(
|
89
|
+
"goto_next_sibling",
|
90
|
+
method!(TreeCursor::goto_next_sibling, 0),
|
91
|
+
)?;
|
92
|
+
tree_cursor_class.define_method("goto_descendant", method!(TreeCursor::goto_descendant, 1))?;
|
93
|
+
tree_cursor_class.define_method(
|
94
|
+
"goto_previous_sibling",
|
95
|
+
method!(TreeCursor::goto_previous_sibling, 0),
|
96
|
+
)?;
|
97
|
+
tree_cursor_class.define_method(
|
98
|
+
"goto_first_child_for_byte",
|
99
|
+
method!(TreeCursor::goto_first_child_for_byte, 1),
|
100
|
+
)?;
|
101
|
+
tree_cursor_class.define_method("reset", method!(TreeCursor::reset, 1))?;
|
102
|
+
tree_cursor_class.define_method("reset_to", method!(TreeCursor::reset_to, 1))?;
|
103
|
+
|
104
|
+
let node_class = namespace.define_class("Node", ruby.class_object())?;
|
105
|
+
node_class.define_method("hash", method!(<Node as typed_data::Hash>::hash, 0))?;
|
106
|
+
node_class.define_method("==", method!(<Node as typed_data::IsEql>::is_eql, 1))?;
|
107
|
+
node_class.define_method("eql?", method!(<Node as typed_data::IsEql>::is_eql, 1))?;
|
108
|
+
node_class.define_method("id", method!(Node::id, 0))?;
|
109
|
+
node_class.define_method("kind", method!(Node::kind, 0))?;
|
110
|
+
node_class.define_method("kind_id", method!(Node::kind_id, 0))?;
|
111
|
+
node_class.define_method("grammar_id", method!(Node::grammar_id, 0))?;
|
112
|
+
node_class.define_method("grammar_name", method!(Node::grammar_name, 0))?;
|
113
|
+
node_class.define_method("language", method!(Node::language, 0))?;
|
114
|
+
node_class.define_method("is_named?", method!(Node::is_named, 0))?;
|
115
|
+
node_class.define_method("is_extra?", method!(Node::is_extra, 0))?;
|
116
|
+
node_class.define_method("has_changes?", method!(Node::has_changes, 0))?;
|
117
|
+
node_class.define_method("has_error?", method!(Node::has_error, 0))?;
|
118
|
+
node_class.define_method("is_error?", method!(Node::is_error, 0))?;
|
119
|
+
node_class.define_method("parse_state", method!(Node::parse_state, 0))?;
|
120
|
+
node_class.define_method("next_parse_state", method!(Node::next_parse_state, 0))?;
|
121
|
+
node_class.define_method("start_byte", method!(Node::start_byte, 0))?;
|
122
|
+
node_class.define_method("end_byte", method!(Node::end_byte, 0))?;
|
123
|
+
node_class.define_method("byte_range", method!(Node::byte_range, 0))?;
|
124
|
+
node_class.define_method("range", method!(Node::range, 0))?;
|
125
|
+
node_class.define_method("start_position", method!(Node::start_position, 0))?;
|
126
|
+
node_class.define_method("end_position", method!(Node::end_position, 0))?;
|
127
|
+
node_class.define_method("child", method!(Node::child, 1))?;
|
128
|
+
node_class.define_method("child_count", method!(Node::child_count, 0))?;
|
129
|
+
node_class.define_method("named_child", method!(Node::named_child, 1))?;
|
130
|
+
node_class.define_method("named_child_count", method!(Node::named_child_count, 0))?;
|
131
|
+
node_class.define_method("child_by_field_name", method!(Node::child_by_field_name, 1))?;
|
132
|
+
node_class.define_method("child_by_field_id", method!(Node::child_by_field_id, 1))?;
|
133
|
+
node_class.define_method(
|
134
|
+
"field_name_for_child",
|
135
|
+
method!(Node::field_name_for_child, 1),
|
136
|
+
)?;
|
137
|
+
node_class.define_method("parent", method!(Node::parent, 0))?;
|
138
|
+
node_class.define_method("children", method!(Node::children, 0))?;
|
139
|
+
node_class.define_method(
|
140
|
+
"children_with_cursor",
|
141
|
+
method!(Node::children_with_cursor, 1),
|
142
|
+
)?;
|
143
|
+
node_class.define_method(
|
144
|
+
"named_children_with_cursor",
|
145
|
+
method!(Node::named_children_with_cursor, 1),
|
146
|
+
)?;
|
147
|
+
node_class.define_method(
|
148
|
+
"children_by_field_name_with_cursor",
|
149
|
+
method!(Node::children_by_field_name_with_cursor, 2),
|
150
|
+
)?;
|
151
|
+
node_class.define_method(
|
152
|
+
"children_by_field_id_with_cursor",
|
153
|
+
method!(Node::children_by_field_id_with_cursor, 2),
|
154
|
+
)?;
|
155
|
+
node_class.define_method(
|
156
|
+
"child_containing_descendant",
|
157
|
+
method!(Node::child_containing_descendant, 1),
|
158
|
+
)?;
|
159
|
+
node_class.define_method("next_sibling", method!(Node::next_sibling, 0))?;
|
160
|
+
node_class.define_method("prev_sibling", method!(Node::prev_sibling, 0))?;
|
161
|
+
node_class.define_method("next_named_sibling", method!(Node::next_named_sibling, 0))?;
|
162
|
+
node_class.define_method("prev_named_sibling", method!(Node::prev_named_sibling, 0))?;
|
163
|
+
node_class.define_method("descendant_count", method!(Node::descendant_count, 0))?;
|
164
|
+
node_class.define_method(
|
165
|
+
"descendant_for_byte_range",
|
166
|
+
method!(Node::descendant_for_byte_range, 2),
|
167
|
+
)?;
|
168
|
+
node_class.define_method(
|
169
|
+
"named_descendant_for_byte_range",
|
170
|
+
method!(Node::named_descendant_for_byte_range, 2),
|
171
|
+
)?;
|
172
|
+
node_class.define_method(
|
173
|
+
"descendant_for_point_range",
|
174
|
+
method!(Node::descendant_for_point_range, 2),
|
175
|
+
)?;
|
176
|
+
node_class.define_method(
|
177
|
+
"named_descendant_for_point_range",
|
178
|
+
method!(Node::named_descendant_for_point_range, 2),
|
179
|
+
)?;
|
180
|
+
|
181
|
+
node_class.define_method("to_sexp", method!(Node::to_sexp, 0))?;
|
182
|
+
node_class.define_method("utf8_text", method!(Node::utf8_text, 1))?;
|
183
|
+
node_class.define_method("walk", method!(Node::walk, 0))?;
|
184
|
+
|
185
|
+
node_class.define_method("inspect", method!(Node::inspect, 0))?;
|
186
|
+
node_class.define_method("to_s", method!(Node::to_s, 0))?;
|
187
|
+
|
188
|
+
let point_class = namespace.define_class("Point", ruby.class_object())?;
|
189
|
+
point_class.define_singleton_method("new", function!(data::Point::new, 2))?;
|
190
|
+
point_class.define_method("hash", method!(<data::Point as typed_data::Hash>::hash, 0))?;
|
191
|
+
point_class.define_method("==", method!(<data::Point as typed_data::IsEql>::is_eql, 1))?;
|
192
|
+
point_class.define_method(
|
193
|
+
"eql?",
|
194
|
+
method!(<data::Point as typed_data::IsEql>::is_eql, 1),
|
195
|
+
)?;
|
196
|
+
point_class.define_method("row", method!(data::Point::get_row, 0))?;
|
197
|
+
point_class.define_method("column", method!(data::Point::get_column, 0))?;
|
198
|
+
point_class.define_method("inspect", method!(data::Point::inspect, 0))?;
|
199
|
+
point_class.define_method("to_s", method!(data::Point::to_s, 0))?;
|
200
|
+
|
201
|
+
let range_class = namespace.define_class("Range", ruby.class_object())?;
|
202
|
+
range_class.define_singleton_method("new", function!(data::Range::new, 4))?;
|
203
|
+
range_class.define_method("hash", method!(<data::Range as typed_data::Hash>::hash, 0))?;
|
204
|
+
range_class.define_method("==", method!(<data::Range as typed_data::IsEql>::is_eql, 1))?;
|
205
|
+
range_class.define_method(
|
206
|
+
"eql?",
|
207
|
+
method!(<data::Range as typed_data::IsEql>::is_eql, 1),
|
208
|
+
)?;
|
209
|
+
range_class.define_method("start_byte", method!(data::Range::get_start_byte, 0))?;
|
210
|
+
range_class.define_method("end_byte", method!(data::Range::get_end_byte, 0))?;
|
211
|
+
range_class.define_method("start_point", method!(data::Range::get_start_point, 0))?;
|
212
|
+
range_class.define_method("end_point", method!(data::Range::get_end_point, 0))?;
|
213
|
+
range_class.define_method("inspect", method!(data::Range::inspect, 0))?;
|
214
|
+
range_class.define_method("to_s", method!(data::Range::to_s, 0))?;
|
215
|
+
|
216
|
+
let language_class = namespace.define_class("LanguageRef", ruby.class_object())?;
|
217
|
+
language_class.define_method("version", method!(LanguageRef::version, 0))?;
|
218
|
+
language_class.define_method("node_kind_count", method!(LanguageRef::node_kind_count, 0))?;
|
219
|
+
language_class.define_method(
|
220
|
+
"parse_state_count",
|
221
|
+
method!(LanguageRef::parse_state_count, 0),
|
222
|
+
)?;
|
223
|
+
language_class.define_method(
|
224
|
+
"node_kind_for_id",
|
225
|
+
method!(LanguageRef::node_kind_for_id, 1),
|
226
|
+
)?;
|
227
|
+
language_class.define_method(
|
228
|
+
"id_for_node_kind",
|
229
|
+
method!(LanguageRef::id_for_node_kind, 2),
|
230
|
+
)?;
|
231
|
+
language_class.define_method(
|
232
|
+
"node_kind_is_named",
|
233
|
+
method!(LanguageRef::node_kind_is_named, 1),
|
234
|
+
)?;
|
235
|
+
language_class.define_method(
|
236
|
+
"node_kind_is_visible",
|
237
|
+
method!(LanguageRef::node_kind_is_visible, 1),
|
238
|
+
)?;
|
239
|
+
language_class.define_method(
|
240
|
+
"field_name_for_id",
|
241
|
+
method!(LanguageRef::field_name_for_id, 1),
|
242
|
+
)?;
|
243
|
+
language_class.define_method(
|
244
|
+
"field_id_for_name",
|
245
|
+
method!(LanguageRef::field_id_for_name, 1),
|
246
|
+
)?;
|
247
|
+
language_class.define_method("next_state", method!(LanguageRef::next_state, 2))?;
|
248
|
+
language_class.define_method(
|
249
|
+
"lookahead_iterator",
|
250
|
+
method!(LanguageRef::lookahead_iterator, 1),
|
251
|
+
)?;
|
252
|
+
|
253
|
+
let lookahead_iterator_class =
|
254
|
+
namespace.define_class("LookaheadIterator", ruby.class_object())?;
|
255
|
+
lookahead_iterator_class.define_method("next", method!(LookaheadIterator::next, 0))?;
|
256
|
+
lookahead_iterator_class.define_method(
|
257
|
+
"current_symbol_name",
|
258
|
+
method!(LookaheadIterator::current_symbol_name, 0),
|
259
|
+
)?;
|
260
|
+
|
261
|
+
let query_class = namespace.define_class("Query", ruby.class_object())?;
|
262
|
+
query_class.define_method(
|
263
|
+
"start_byte_for_pattern",
|
264
|
+
method!(Query::start_byte_for_pattern, 1),
|
265
|
+
)?;
|
266
|
+
query_class.define_method("pattern_count", method!(Query::pattern_count, 0))?;
|
267
|
+
query_class.define_method("capture_names", method!(Query::capture_names, 0))?;
|
268
|
+
query_class.define_method(
|
269
|
+
"capture_quantifiers",
|
270
|
+
method!(Query::capture_quantifiers, 1),
|
271
|
+
)?;
|
272
|
+
query_class.define_method(
|
273
|
+
"capture_index_for_name",
|
274
|
+
method!(Query::capture_index_for_name, 1),
|
275
|
+
)?;
|
276
|
+
query_class.define_method("disable_capture", method!(Query::disable_capture, 1))?;
|
277
|
+
query_class.define_method("disable_pattern", method!(Query::disable_pattern, 1))?;
|
278
|
+
query_class.define_method("is_pattern_rooted", method!(Query::is_pattern_rooted, 1))?;
|
279
|
+
query_class.define_method(
|
280
|
+
"is_pattern_guaranteed_at_step",
|
281
|
+
method!(Query::is_pattern_guaranteed_at_step, 1),
|
282
|
+
)?;
|
283
|
+
|
284
|
+
Lazy::force(&QUERY_CAPTURE_CLASS, ruby);
|
285
|
+
let struct_class = Lazy::try_get_inner(&QUERY_CAPTURE_CLASS).unwrap();
|
286
|
+
namespace.const_set("QueryCapture", struct_class)?;
|
287
|
+
|
288
|
+
let query_match_class = namespace.define_class("QueryMatch", ruby.class_object())?;
|
289
|
+
query_match_class.define_method("pattern_index", method!(QueryMatch::pattern_index, 0))?;
|
290
|
+
query_match_class.define_method("captures", method!(QueryMatch::captures, 0))?;
|
291
|
+
|
292
|
+
let query_cursor_class = namespace.define_class("QueryCursor", ruby.class_object())?;
|
293
|
+
query_cursor_class.define_singleton_method("new", function!(QueryCursor::new, 0))?;
|
294
|
+
query_cursor_class.define_method("match_limit", method!(QueryCursor::match_limit, 0))?;
|
295
|
+
query_cursor_class
|
296
|
+
.define_method("set_match_limit", method!(QueryCursor::set_match_limit, 1))?;
|
297
|
+
query_cursor_class.define_method(
|
298
|
+
"did_exceed_match_limit",
|
299
|
+
method!(QueryCursor::did_exceed_match_limit, 0),
|
300
|
+
)?;
|
301
|
+
query_cursor_class.define_method("matches", method!(QueryCursor::matches, 3))?;
|
302
|
+
query_cursor_class.define_method("set_byte_range", method!(QueryCursor::set_byte_range, 1))?;
|
303
|
+
query_cursor_class
|
304
|
+
.define_method("set_point_range", method!(QueryCursor::set_point_range, 1))?;
|
305
|
+
query_cursor_class.define_method(
|
306
|
+
"set_max_start_depth",
|
307
|
+
method!(QueryCursor::set_max_start_depth, 1),
|
308
|
+
)?;
|
309
|
+
|
310
|
+
Ok(())
|
311
|
+
}
|
@@ -0,0 +1,67 @@
|
|
1
|
+
use crate::query::Query;
|
2
|
+
use crate::tree::Tree;
|
3
|
+
use crate::util::build_error;
|
4
|
+
use crate::LANG_LANGUAGES;
|
5
|
+
|
6
|
+
use std::cell::RefCell;
|
7
|
+
use std::collections::HashMap;
|
8
|
+
use std::sync::{Arc, Mutex};
|
9
|
+
|
10
|
+
#[magnus::wrap(class = "TreeStump::Parser")]
|
11
|
+
pub struct Parser {
|
12
|
+
raw_parser: RefCell<tree_sitter::Parser>,
|
13
|
+
}
|
14
|
+
|
15
|
+
impl Parser {
|
16
|
+
pub fn new() -> Self {
|
17
|
+
Self {
|
18
|
+
raw_parser: RefCell::new(tree_sitter::Parser::new()),
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
pub fn set_language(&self, lang: String) -> Result<bool, magnus::Error> {
|
23
|
+
let languages = LANG_LANGUAGES.get_or_init(|| Mutex::new(HashMap::new()));
|
24
|
+
let languages = languages.lock().unwrap();
|
25
|
+
let language = languages.get(&lang);
|
26
|
+
match language {
|
27
|
+
Some(language) => {
|
28
|
+
let result = self.raw_parser.borrow_mut().set_language(language);
|
29
|
+
result.map_or_else(|e| Err(build_error(e.to_string())), |_| Ok(true))
|
30
|
+
}
|
31
|
+
None => Err(build_error(format!("Language {} is not registered", lang))),
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
pub fn parse(&self, source: String) -> Result<Tree, magnus::Error> {
|
36
|
+
let tree = self.raw_parser.borrow_mut().parse(source, None);
|
37
|
+
|
38
|
+
match tree {
|
39
|
+
Some(tree) => Ok(Tree::from(Arc::new(tree))),
|
40
|
+
None => Err(build_error("Failed to parse")),
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
pub fn reset(&self) {
|
45
|
+
self.raw_parser.borrow_mut().reset();
|
46
|
+
}
|
47
|
+
|
48
|
+
pub fn timeout_micros(&self) -> u64 {
|
49
|
+
self.raw_parser.borrow().timeout_micros()
|
50
|
+
}
|
51
|
+
|
52
|
+
pub fn set_timeout_micros(&self, timeout: u64) {
|
53
|
+
self.raw_parser.borrow_mut().set_timeout_micros(timeout);
|
54
|
+
}
|
55
|
+
|
56
|
+
fn language(&self) -> Option<tree_sitter::Language> {
|
57
|
+
self.raw_parser.borrow().language()
|
58
|
+
}
|
59
|
+
|
60
|
+
pub fn build_query(&self, source: String) -> Result<Query, magnus::Error> {
|
61
|
+
let lang = self.language();
|
62
|
+
lang.map_or_else(
|
63
|
+
|| Err(build_error("Failed to get language from parser")),
|
64
|
+
|lang| Query::new(&lang, source),
|
65
|
+
)
|
66
|
+
}
|
67
|
+
}
|
@@ -0,0 +1,209 @@
|
|
1
|
+
use std::{cell::RefCell, sync::Arc};
|
2
|
+
|
3
|
+
use magnus::{
|
4
|
+
block::Yield,
|
5
|
+
symbol::IntoSymbol,
|
6
|
+
typed_data,
|
7
|
+
value::{InnerRef, Opaque, ReprValue},
|
8
|
+
Class, Error, IntoValue, RArray, RStruct, RTypedData, Ruby, Value,
|
9
|
+
};
|
10
|
+
|
11
|
+
use crate::{data::Point, tree::Node, util::build_error, QUERY_CAPTURE_CLASS};
|
12
|
+
|
13
|
+
#[magnus::wrap(class = "TreeStump::Query", free_immediately)]
|
14
|
+
#[derive(Debug)]
|
15
|
+
pub struct Query {
|
16
|
+
pub raw_query: RefCell<tree_sitter::Query>,
|
17
|
+
}
|
18
|
+
|
19
|
+
impl Query {
|
20
|
+
pub fn new(language: &tree_sitter::Language, source: String) -> Result<Self, magnus::Error> {
|
21
|
+
let raw_query = tree_sitter::Query::new(language, source.as_str());
|
22
|
+
let raw_query = raw_query.map_err(|e| build_error(e.to_string()));
|
23
|
+
raw_query.map(|q| Self {
|
24
|
+
raw_query: RefCell::new(q),
|
25
|
+
})
|
26
|
+
}
|
27
|
+
|
28
|
+
pub fn start_byte_for_pattern(&self, pattern_index: usize) -> usize {
|
29
|
+
self.raw_query
|
30
|
+
.borrow()
|
31
|
+
.start_byte_for_pattern(pattern_index)
|
32
|
+
}
|
33
|
+
|
34
|
+
pub fn pattern_count(&self) -> usize {
|
35
|
+
self.raw_query.borrow().pattern_count()
|
36
|
+
}
|
37
|
+
|
38
|
+
pub fn capture_names(&self) -> Vec<String> {
|
39
|
+
self.raw_query
|
40
|
+
.borrow()
|
41
|
+
.capture_names()
|
42
|
+
.iter()
|
43
|
+
.map(|s| s.to_string())
|
44
|
+
.collect()
|
45
|
+
}
|
46
|
+
|
47
|
+
pub fn capture_quantifiers(&self, index: usize) -> Result<RArray, Error> {
|
48
|
+
let raw_query = self.raw_query.borrow();
|
49
|
+
let quantifiers = raw_query
|
50
|
+
.capture_quantifiers(index)
|
51
|
+
.iter()
|
52
|
+
.map(|q| format!("{:?}", q).into_symbol());
|
53
|
+
let ruby = Ruby::get().expect("Ruby is not initialized");
|
54
|
+
let array = ruby.ary_from_iter(quantifiers);
|
55
|
+
Ok(array)
|
56
|
+
}
|
57
|
+
|
58
|
+
pub fn capture_index_for_name(&self, name: String) -> Option<u32> {
|
59
|
+
self.raw_query
|
60
|
+
.borrow()
|
61
|
+
.capture_index_for_name(name.as_str())
|
62
|
+
}
|
63
|
+
|
64
|
+
pub fn disable_capture(&self, name: String) {
|
65
|
+
self.raw_query.borrow_mut().disable_capture(&name);
|
66
|
+
}
|
67
|
+
|
68
|
+
pub fn disable_pattern(&self, index: usize) {
|
69
|
+
self.raw_query.borrow_mut().disable_pattern(index);
|
70
|
+
}
|
71
|
+
|
72
|
+
pub fn is_pattern_rooted(&self, index: usize) -> bool {
|
73
|
+
self.raw_query.borrow().is_pattern_rooted(index)
|
74
|
+
}
|
75
|
+
|
76
|
+
pub fn is_pattern_guaranteed_at_step(&self, index: usize) -> bool {
|
77
|
+
self.raw_query.borrow().is_pattern_guaranteed_at_step(index)
|
78
|
+
}
|
79
|
+
}
|
80
|
+
|
81
|
+
#[magnus::wrap(class = "TreeStump::QueryMatch", free_immediately)]
|
82
|
+
pub struct QueryMatch {
|
83
|
+
pattern_index: usize,
|
84
|
+
captures: Opaque<RArray>,
|
85
|
+
}
|
86
|
+
|
87
|
+
impl QueryMatch {
|
88
|
+
pub fn pattern_index(&self) -> usize {
|
89
|
+
self.pattern_index
|
90
|
+
}
|
91
|
+
|
92
|
+
pub fn captures(ruby: &Ruby, rb_self: typed_data::Obj<Self>) -> Result<RArray, Error> {
|
93
|
+
Ok(ruby.get_inner(rb_self.captures))
|
94
|
+
}
|
95
|
+
}
|
96
|
+
|
97
|
+
#[magnus::wrap(class = "TreeStump::QueryCursor", free_immediately)]
|
98
|
+
pub struct QueryCursor {
|
99
|
+
raw_cursor: RefCell<tree_sitter::QueryCursor>,
|
100
|
+
}
|
101
|
+
|
102
|
+
impl QueryCursor {
|
103
|
+
pub fn new() -> Self {
|
104
|
+
Self {
|
105
|
+
raw_cursor: RefCell::new(tree_sitter::QueryCursor::new()),
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
pub fn match_limit(&self) -> u32 {
|
110
|
+
self.raw_cursor.borrow().match_limit()
|
111
|
+
}
|
112
|
+
|
113
|
+
pub fn set_match_limit(&self, limit: u32) {
|
114
|
+
self.raw_cursor.borrow_mut().set_match_limit(limit);
|
115
|
+
}
|
116
|
+
|
117
|
+
pub fn did_exceed_match_limit(&self) -> bool {
|
118
|
+
self.raw_cursor.borrow().did_exceed_match_limit()
|
119
|
+
}
|
120
|
+
|
121
|
+
pub fn matches<'tree>(
|
122
|
+
ruby: &Ruby,
|
123
|
+
rb_self: typed_data::Obj<Self>,
|
124
|
+
query: typed_data::Obj<Query>,
|
125
|
+
node: typed_data::Obj<Node<'tree>>,
|
126
|
+
source: String,
|
127
|
+
) -> Result<Yield<impl Iterator<Item = Value>>, Error> {
|
128
|
+
let mut cursor = rb_self.raw_cursor.borrow_mut();
|
129
|
+
let raw_query = query.raw_query.borrow();
|
130
|
+
|
131
|
+
let matches = cursor.matches(&raw_query, node.get_raw_node(), source.as_bytes());
|
132
|
+
let struct_class = QUERY_CAPTURE_CLASS.get_inner_ref_with(ruby);
|
133
|
+
let array = ruby.ary_new();
|
134
|
+
|
135
|
+
for m in matches {
|
136
|
+
let captures = ruby.ary_new();
|
137
|
+
for c in m.captures {
|
138
|
+
let r_struct = RStruct::from_value(
|
139
|
+
struct_class
|
140
|
+
.new_instance((Node::new(Arc::clone(&node.raw_tree), c.node), c.index))?,
|
141
|
+
);
|
142
|
+
captures.push(r_struct)?
|
143
|
+
}
|
144
|
+
let query_match = QueryMatch {
|
145
|
+
pattern_index: m.pattern_index,
|
146
|
+
captures: Opaque::from(captures),
|
147
|
+
};
|
148
|
+
array.push(query_match)?
|
149
|
+
}
|
150
|
+
|
151
|
+
if ruby.block_given() {
|
152
|
+
Ok(Yield::Iter(array.into_iter()))
|
153
|
+
} else {
|
154
|
+
Ok(Yield::Enumerator(rb_self.enumeratorize(
|
155
|
+
"matches",
|
156
|
+
(query, node, source.into_value()),
|
157
|
+
)))
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
pub fn set_byte_range(
|
162
|
+
_ruby: &Ruby,
|
163
|
+
rb_self: typed_data::Obj<Self>,
|
164
|
+
range: magnus::Range,
|
165
|
+
) -> Result<typed_data::Obj<Self>, Error> {
|
166
|
+
let mut cursor = rb_self.raw_cursor.borrow_mut();
|
167
|
+
let len = range.funcall("size", ())?;
|
168
|
+
let std_range = range.to_range_with_len(len)?;
|
169
|
+
cursor.set_byte_range(std_range);
|
170
|
+
Ok(rb_self)
|
171
|
+
}
|
172
|
+
|
173
|
+
pub fn set_point_range(
|
174
|
+
_ruby: &Ruby,
|
175
|
+
rb_self: typed_data::Obj<Self>,
|
176
|
+
range: magnus::Range,
|
177
|
+
) -> Result<typed_data::Obj<Self>, Error> {
|
178
|
+
let excl = range.excl();
|
179
|
+
|
180
|
+
if excl {
|
181
|
+
return Err(build_error("Point range must be inclusive"));
|
182
|
+
}
|
183
|
+
|
184
|
+
let start: Value = range.beg()?;
|
185
|
+
let end: Value = range.end()?;
|
186
|
+
|
187
|
+
let start_typed_data = RTypedData::from_value(start).expect("Expected typed data");
|
188
|
+
let start = start_typed_data.get::<Point>()?;
|
189
|
+
|
190
|
+
let end_typed_data = RTypedData::from_value(end).expect("Expected typed data");
|
191
|
+
let end = end_typed_data.get::<Point>()?;
|
192
|
+
|
193
|
+
let point_range = start.into_raw()..end.into_raw();
|
194
|
+
|
195
|
+
let mut cursor = rb_self.raw_cursor.borrow_mut();
|
196
|
+
cursor.set_point_range(point_range);
|
197
|
+
Ok(rb_self)
|
198
|
+
}
|
199
|
+
|
200
|
+
pub fn set_max_start_depth(
|
201
|
+
_ruby: &Ruby,
|
202
|
+
rb_self: typed_data::Obj<Self>,
|
203
|
+
depth: Option<u32>,
|
204
|
+
) -> Result<typed_data::Obj<Self>, Error> {
|
205
|
+
let mut cursor = rb_self.raw_cursor.borrow_mut();
|
206
|
+
cursor.set_max_start_depth(depth);
|
207
|
+
Ok(rb_self)
|
208
|
+
}
|
209
|
+
}
|