OR-1 dataflow CPU sketch
1#include "./query.h"
2#include "./language.h"
3#include "./node.h"
4
5#include <napi.h>
6#include <string>
7#include <vector>
8
9using std::vector;
10using namespace Napi;
11
12namespace node_tree_sitter {
13
14/*
15 tstag() {
16 b2sum -l64 <(printf tree-sitter) <(printf "$1") | \
17 awk '{printf "0x" toupper($1) (NR == 1 ? ", " : "\n")}'
18 }
19 tstag query # => 0x8AF2E5212AD58ABF, 0x7B1FAB666CBD6803
20*/
21const napi_type_tag QUERY_TYPE_TAG = {
22 0x8AF2E5212AD58ABF, 0x7B1FAB666CBD6803
23};
24
25const char *query_error_names[] = {
26 "TSQueryErrorNone",
27 "TSQueryErrorSyntax",
28 "TSQueryErrorNodeType",
29 "TSQueryErrorField",
30 "TSQueryErrorCapture",
31 "TSQueryErrorStructure",
32};
33
34void Query::Init(Napi::Env env, Napi::Object exports) {
35 auto *data = env.GetInstanceData<AddonData>();
36 data->ts_query_cursor = ts_query_cursor_new();
37
38 Function ctor = DefineClass(env, "Query", {
39 InstanceAccessor("matchLimit", &Query::MatchLimit, nullptr, napi_default_method),
40
41 InstanceMethod("_matches", &Query::Matches, napi_default_method),
42 InstanceMethod("_captures", &Query::Captures, napi_default_method),
43 InstanceMethod("_getPredicates", &Query::GetPredicates, napi_default_method),
44 InstanceMethod("disableCapture", &Query::DisableCapture, napi_default_method),
45 InstanceMethod("disablePattern", &Query::DisablePattern, napi_default_method),
46 InstanceMethod("isPatternGuaranteedAtStep", &Query::IsPatternGuaranteedAtStep, napi_default_method),
47 InstanceMethod("isPatternRooted", &Query::IsPatternRooted, napi_default_method),
48 InstanceMethod("isPatternNonLocal", &Query::IsPatternNonLocal, napi_default_method),
49 InstanceMethod("startIndexForPattern", &Query::StartIndexForPattern, napi_default_method),
50 InstanceMethod("endIndexForPattern", &Query::EndIndexForPattern, napi_default_method),
51 InstanceMethod("didExceedMatchLimit", &Query::DidExceedMatchLimit, napi_default_method),
52 });
53
54 data->query_constructor = Napi::Persistent(ctor);
55 exports["Query"] = ctor;
56}
57
58Query::Query(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Query>(info) , query_(nullptr) {
59 Napi::Env env = info.Env();
60
61 Value().TypeTag(&QUERY_TYPE_TAG);
62
63 const TSLanguage *language = language_methods::UnwrapLanguage(info[0]);
64 const char *source;
65 uint32_t source_len;
66 uint32_t error_offset = 0;
67 TSQueryError error_type = TSQueryErrorNone;
68
69 if (language == nullptr) {
70 throw Error::New(env, "Missing language argument");
71 }
72
73 if (info[1].IsString()) {
74 auto string = info[1].As<String>();
75 std::string utf8_string = string.Utf8Value();
76 source = utf8_string.data();
77 source_len = utf8_string.length();
78 query_ = ts_query_new(language, source, source_len, &error_offset, &error_type);
79 } else if (info[1].IsBuffer()) {
80 auto buf = info[1].As<Buffer<char>>();
81 source = buf.Data();
82 source_len = buf.Length();
83 query_ = ts_query_new(language, source, source_len, &error_offset, &error_type);
84 }
85 else {
86 throw Error::New(env, "Missing source argument");
87 }
88
89 if (error_offset > 0) {
90 const char *error_name = query_error_names[error_type];
91 std::string message = "Query error of type ";
92 message += error_name;
93 message += " at position ";
94 message += std::to_string(error_offset);
95 throw Error::New(env, message.c_str());
96 }
97
98 info.This().As<Napi::Object>().Get("_init").As<Napi::Function>().Call(info.This(), {});
99}
100
101Query::~Query() {
102 ts_query_delete(query_);
103}
104
105Query *Query::UnwrapQuery(const Napi::Value &value) {
106 if (!value.IsObject()) {
107 return nullptr;
108 }
109 auto js_query = value.As<Object>();
110 if (!js_query.CheckTypeTag(&QUERY_TYPE_TAG)) {
111 return nullptr;
112 }
113 return Query::Unwrap(js_query);
114}
115
116Napi::Value Query::GetPredicates(const Napi::CallbackInfo &info) {
117 Napi::Env env = info.Env();
118 Query *query = Query::UnwrapQuery(info.This());
119 auto *ts_query = query->query_;
120
121 auto pattern_len = ts_query_pattern_count(ts_query);
122
123 Array js_predicates = Array::New(env);
124
125 for (size_t pattern_index = 0; pattern_index < pattern_len; pattern_index++) {
126 uint32_t predicates_len;
127 const TSQueryPredicateStep *predicates = ts_query_predicates_for_pattern(
128 ts_query, pattern_index, &predicates_len);
129
130 Array js_pattern_predicates = Array::New(env);
131
132 if (predicates_len > 0) {
133 Array js_predicate = Array::New(env);
134
135 size_t a_index = 0;
136 size_t p_index = 0;
137 for (size_t i = 0; i < predicates_len; i++) {
138 const TSQueryPredicateStep predicate = predicates[i];
139 uint32_t len;
140 switch (predicate.type) {
141 case TSQueryPredicateStepTypeCapture:
142 js_predicate[p_index++] = Number::New(env, TSQueryPredicateStepTypeCapture);
143 js_predicate[p_index++] = String::New(env,
144 ts_query_capture_name_for_id(ts_query, predicate.value_id, &len)
145 );
146 break;
147 case TSQueryPredicateStepTypeString:
148 js_predicate[p_index++] = Number::New(env, TSQueryPredicateStepTypeString);
149 js_predicate[p_index++] = String::New(env,
150 ts_query_string_value_for_id(ts_query, predicate.value_id, &len)
151 );
152 break;
153 case TSQueryPredicateStepTypeDone:
154 js_pattern_predicates[a_index++] = js_predicate;
155 js_predicate = Array::New(env);
156 p_index = 0;
157 break;
158 }
159 }
160 }
161
162 js_predicates[pattern_index] = js_pattern_predicates;
163 }
164
165 return js_predicates;
166}
167
168Napi::Value Query::Matches(const Napi::CallbackInfo &info) {
169 Napi::Env env = info.Env();
170 auto *data = env.GetInstanceData<AddonData>();
171 Query *query = Query::UnwrapQuery(info.This());
172 const Tree *tree = Tree::UnwrapTree(info[0]);
173
174 uint32_t start_row = 0, start_column = 0, end_row = 0, end_column = 0, start_index = 0, end_index = 0,
175 match_limit = UINT32_MAX, max_start_depth = UINT32_MAX, timeout_micros = 0;
176
177 if (info.Length() > 1 && info[1].IsNumber()) {
178 start_row = info[1].As<Number>().Uint32Value();
179 }
180 if (info.Length() > 2 && info[2].IsNumber()) {
181 start_column = info[2].As<Number>().Uint32Value() << 1;
182 }
183 if (info.Length() > 3 && info[3].IsNumber()) {
184 end_row = info[3].As<Number>().Uint32Value();
185 }
186 if (info.Length() > 4 && info[4].IsNumber()) {
187 end_column = info[4].As<Number>().Uint32Value() << 1;
188 }
189 if (info.Length() > 5 && info[5].IsNumber()) {
190 start_index = info[5].As<Number>().Uint32Value();
191 }
192 if (info.Length() > 6 && info[6].IsNumber()) {
193 end_index = info[6].As<Number>().Uint32Value() << 1;
194 }
195 if (info.Length() > 7 && info[7].IsNumber()) {
196 match_limit = info[7].As<Number>().Uint32Value();
197 }
198 if (info.Length() > 8 && info[8].IsNumber()) {
199 max_start_depth = info[8].As<Number>().Uint32Value();
200 }
201 if (info.Length() > 9 && info[9].IsNumber()) {
202 timeout_micros = info[9].As<Number>().Uint32Value();
203 }
204
205 if (query == nullptr) {
206 throw Error::New(env, "Missing argument query");
207 }
208
209 if (tree == nullptr) {
210 throw Error::New(env, "Missing argument tree");
211 }
212
213 TSQuery *ts_query = query->query_;
214 TSNode root_node = node_methods::UnmarshalNode(env, tree);
215 TSPoint start_point = {start_row, start_column};
216 TSPoint end_point = {end_row, end_column};
217 ts_query_cursor_set_point_range(data->ts_query_cursor, start_point, end_point);
218 ts_query_cursor_set_byte_range(data->ts_query_cursor, start_index, end_index);
219 ts_query_cursor_set_match_limit(data->ts_query_cursor, match_limit);
220 ts_query_cursor_set_max_start_depth(data->ts_query_cursor, max_start_depth);
221 ts_query_cursor_set_timeout_micros(data->ts_query_cursor, timeout_micros);
222 ts_query_cursor_exec(data->ts_query_cursor, ts_query, root_node);
223
224 Array js_matches = Array::New(env);
225 unsigned index = 0;
226 vector<TSNode> nodes;
227 TSQueryMatch match;
228
229 while (ts_query_cursor_next_match(data->ts_query_cursor, &match)) {
230 js_matches[index++] = Number::New(env, match.pattern_index);
231
232 for (uint16_t i = 0; i < match.capture_count; i++) {
233 const TSQueryCapture &capture = match.captures[i];
234
235 uint32_t capture_name_len = 0;
236 const char *capture_name = ts_query_capture_name_for_id(
237 ts_query, capture.index, &capture_name_len);
238
239 TSNode node = capture.node;
240 nodes.push_back(node);
241
242 String js_capture = String::New(env, capture_name);;
243 js_matches[index++] = js_capture;
244 }
245 }
246
247 auto js_nodes = node_methods::GetMarshalNodes(info, tree, nodes.data(), nodes.size());
248
249 auto result = Array::New(env);
250 result[0U] = js_matches;
251 result[1] = js_nodes;
252 return result;
253}
254
255Napi::Value Query::Captures(const Napi::CallbackInfo &info) {
256 Napi::Env env = info.Env();
257 auto *data = env.GetInstanceData<AddonData>();
258 Query *query = Query::UnwrapQuery(info.This());
259 const Tree *tree = Tree::UnwrapTree(info[0]);
260
261 uint32_t start_row = 0, start_column = 0, end_row = 0, end_column = 0, start_index = 0, end_index = 0,
262 match_limit = UINT32_MAX, max_start_depth = UINT32_MAX, timeout_micros = 0;
263
264 if (info.Length() > 1 && info[1].IsNumber()) {
265 start_row = info[1].As<Number>().Uint32Value();
266 }
267 if (info.Length() > 2 && info[2].IsNumber()) {
268 start_column = info[2].As<Number>().Uint32Value() << 1;
269 }
270 if (info.Length() > 3 && info[3].IsNumber()) {
271 end_row = info[3].As<Number>().Uint32Value();
272 }
273 if (info.Length() > 4 && info[4].IsNumber()) {
274 end_column = info[4].As<Number>().Uint32Value() << 1;
275 }
276 if (info.Length() > 5 && info[5].IsNumber()) {
277 start_index = info[5].As<Number>().Uint32Value();
278 }
279 if (info.Length() > 6 && info[6].IsNumber()) {
280 end_index = info[6].As<Number>().Uint32Value() << 1;
281 }
282 if (info.Length() > 7 && info[7].IsNumber()) {
283 match_limit = info[7].As<Number>().Uint32Value();
284 }
285 if (info.Length() > 8 && info[8].IsNumber()) {
286 max_start_depth = info[8].As<Number>().Uint32Value();
287 }
288 if (info.Length() > 9 && info[9].IsNumber()) {
289 timeout_micros = info[9].As<Number>().Uint32Value();
290 }
291
292 if (query == nullptr) {
293 throw Error::New(env, "Missing argument query");
294 }
295
296 if (tree == nullptr) {
297 throw Error::New(env, "Missing argument tree");
298 }
299
300 TSQuery *ts_query = query->query_;
301 TSNode root_node = node_methods::UnmarshalNode(env, tree);
302 TSPoint start_point = {start_row, start_column};
303 TSPoint end_point = {end_row, end_column};
304 ts_query_cursor_set_point_range(data->ts_query_cursor, start_point, end_point);
305 ts_query_cursor_set_byte_range(data->ts_query_cursor, start_index, end_index);
306 ts_query_cursor_set_match_limit(data->ts_query_cursor, match_limit);
307 ts_query_cursor_set_max_start_depth(data->ts_query_cursor, max_start_depth);
308 ts_query_cursor_set_timeout_micros(data->ts_query_cursor, timeout_micros);
309 ts_query_cursor_exec(data->ts_query_cursor, ts_query, root_node);
310
311 Array js_matches = Array::New(env);
312 unsigned index = 0;
313 vector<TSNode> nodes;
314 TSQueryMatch match;
315 uint32_t capture_index;
316
317 while (ts_query_cursor_next_capture(
318 data->ts_query_cursor,
319 &match,
320 &capture_index
321 )) {
322
323 js_matches[index++] = Number::New(env, match.pattern_index);
324 js_matches[index++] = Number::New(env, capture_index);
325
326 for (uint16_t i = 0; i < match.capture_count; i++) {
327 const TSQueryCapture &capture = match.captures[i];
328
329 uint32_t capture_name_len = 0;
330 const char *capture_name = ts_query_capture_name_for_id(
331 ts_query, capture.index, &capture_name_len);
332
333 TSNode node = capture.node;
334 nodes.push_back(node);
335
336 String js_capture = String::New(env, capture_name);;
337 js_matches[index++] = js_capture;
338 }
339 }
340
341 auto js_nodes = node_methods::GetMarshalNodes(info, tree, nodes.data(), nodes.size());
342
343 auto result = Array::New(env);
344 result[0U] = js_matches;
345 result[1] = js_nodes;
346 return result;
347}
348
349Napi::Value Query::DisableCapture(const Napi::CallbackInfo &info) {
350 std::string string = info[0].As<String>().Utf8Value();
351 const char *capture_name = string.c_str();
352 ts_query_disable_capture(query_, capture_name, string.length());
353 return info.Env().Undefined();
354}
355
356Napi::Value Query::DisablePattern(const Napi::CallbackInfo &info) {
357 uint32_t pattern_index = info[0].As<Number>().Uint32Value();
358 ts_query_disable_pattern(query_, pattern_index);
359 return info.Env().Undefined();
360}
361
362Napi::Value Query::IsPatternGuaranteedAtStep(const Napi::CallbackInfo &info) {
363 uint32_t byte_offset = info[0].As<Number>().Uint32Value();
364 return Boolean::New(info.Env(), ts_query_is_pattern_guaranteed_at_step(query_, byte_offset));
365}
366
367Napi::Value Query::IsPatternRooted(const Napi::CallbackInfo &info) {
368 uint32_t pattern_index = info[0].As<Number>().Uint32Value();
369 return Boolean::New(info.Env(), ts_query_is_pattern_rooted(query_, pattern_index));
370}
371
372Napi::Value Query::IsPatternNonLocal(const Napi::CallbackInfo &info) {
373 uint32_t pattern_index = info[0].As<Number>().Uint32Value();
374 return Boolean::New(info.Env(), ts_query_is_pattern_non_local(query_, pattern_index));
375}
376
377Napi::Value Query::StartIndexForPattern(const Napi::CallbackInfo &info) {
378 uint32_t pattern_index = info[0].As<Number>().Uint32Value();
379 return Number::New(info.Env(), ts_query_start_byte_for_pattern(query_, pattern_index));
380}
381
382Napi::Value Query::EndIndexForPattern(const Napi::CallbackInfo &info) {
383 uint32_t pattern_index = info[0].As<Number>().Uint32Value();
384 return Number::New(info.Env(), ts_query_end_byte_for_pattern(query_, pattern_index));
385}
386
387Napi::Value Query::DidExceedMatchLimit(const Napi::CallbackInfo &info) {
388 auto *data = info.Env().GetInstanceData<AddonData>();
389 return Boolean::New(info.Env(), ts_query_cursor_did_exceed_match_limit(data->ts_query_cursor));
390}
391
392Napi::Value Query::MatchLimit(const Napi::CallbackInfo &info) {
393 auto *data = info.Env().GetInstanceData<AddonData>();
394 return Number::New(info.Env(), ts_query_cursor_match_limit(data->ts_query_cursor));
395}
396
397} // namespace node_tree_sitter