OR-1 dataflow CPU sketch
1#include "./node.h"
2#include "./conversions.h"
3#include "./tree.h"
4#include "./tree_cursor.h"
5#include "tree_sitter/api.h"
6
7#include <napi.h>
8#include <vector>
9
10using std::vector;
11using namespace Napi;
12
13namespace node_tree_sitter::node_methods {
14
15const uint32_t FIELD_COUNT_PER_NODE = 6;
16
17namespace {
18
19inline void setup_transfer_buffer(Napi::Env env, uint32_t node_count) {
20 auto *data = env.GetInstanceData<AddonData>();
21
22 uint32_t new_length = node_count * FIELD_COUNT_PER_NODE;
23 if (new_length > data->transfer_buffer_length) {
24 data->transfer_buffer_length = new_length;
25
26 auto js_transfer_buffer = Uint32Array::New(env, data->transfer_buffer_length);
27 data->transfer_buffer = js_transfer_buffer.Data();
28
29 data->module_exports.Value()["nodeTransferArray"] = js_transfer_buffer;
30 }
31}
32
33inline bool operator<=(const TSPoint &left, const TSPoint &right) {
34 if (left.row < right.row) {
35 return true;
36 }
37 if (left.row > right.row) {
38 return false;
39 }
40 return left.column <= right.column;
41}
42
43Napi::Value MarshalNodes(const Napi::CallbackInfo &info,
44 const Tree *tree, const TSNode *nodes, uint32_t node_count) {
45 return GetMarshalNodes(info, tree, nodes, node_count);
46}
47
48} // namespace
49
50Napi::Value MarshalNode(const Napi::CallbackInfo &info, const Tree *tree, TSNode node) {
51 return GetMarshalNode(info, tree, node);
52}
53
54Napi::Value GetMarshalNodes(const Napi::CallbackInfo &info,
55 const Tree *tree, const TSNode *nodes, uint32_t node_count) {
56 Env env = info.Env();
57 auto *data = env.GetInstanceData<AddonData>();
58 auto result = Array::New(env, node_count);
59 setup_transfer_buffer(env, node_count);
60 uint32_t *p = data->transfer_buffer;
61 for (unsigned i = 0; i < node_count; i++) {
62 TSNode node = nodes[i];
63 const auto &cache_entry = tree->cached_nodes_.find(node.id);
64 Napi::Value value;
65 if (cache_entry != tree->cached_nodes_.end() && (value = cache_entry->second->node.Value(), !value.IsEmpty())) {
66 result[i] = value;
67 } else {
68 MarshalNodeId(node.id, p);
69 p += 2;
70 *(p++) = node.context[0];
71 *(p++) = node.context[1];
72 *(p++) = node.context[2];
73 *(p++) = node.context[3];
74 if (node.id != nullptr) {
75 result[i] = Number::New(env, ts_node_symbol(node));
76 } else {
77 result[i] = env.Null();
78 }
79 }
80 }
81 return result;
82}
83
84Napi::Value GetMarshalNode(const Napi::CallbackInfo &info, const Tree *tree, TSNode node) {
85 Env env = info.Env();
86 auto* data = env.GetInstanceData<AddonData>();
87 const auto &cache_entry = tree->cached_nodes_.find(node.id);
88 Napi::Value value;
89 if (cache_entry != tree->cached_nodes_.end() && (value = cache_entry->second->node.Value(), !value.IsEmpty())) {
90 return value;
91 } else {
92 setup_transfer_buffer(env, 1);
93 uint32_t *p = data->transfer_buffer;
94 MarshalNodeId(node.id, p);
95 p += 2;
96 *(p++) = node.context[0];
97 *(p++) = node.context[1];
98 *(p++) = node.context[2];
99 *(p++) = node.context[3];
100 if (node.id != nullptr) {
101 return Number::New(env, ts_node_symbol(node));
102 }
103 }
104 return env.Null();
105}
106
107TSNode UnmarshalNode(Napi::Env env, const Tree *tree, uint8_t offset) {
108 auto* data = env.GetInstanceData<AddonData>();
109 TSNode result = {{0, 0, 0, 0}, nullptr, nullptr};
110 result.tree = tree->tree_;
111 if (result.tree == nullptr) {
112 throw TypeError::New(env, "Argument must be a tree");
113 }
114
115 result.id = UnmarshalNodeId(&data->transfer_buffer[offset * FIELD_COUNT_PER_NODE]);
116 result.context[0] = data->transfer_buffer[offset * FIELD_COUNT_PER_NODE + 2];
117 result.context[1] = data->transfer_buffer[offset * FIELD_COUNT_PER_NODE + 3];
118 result.context[2] = data->transfer_buffer[offset * FIELD_COUNT_PER_NODE + 4];
119 result.context[3] = data->transfer_buffer[offset * FIELD_COUNT_PER_NODE + 5];
120 return result;
121}
122
123struct SymbolSet {
124 void add(TSSymbol symbol) { symbols.push_back(symbol); }
125 [[nodiscard]] bool contains(TSSymbol symbol) const { return std::find(symbols.begin(), symbols.end(), symbol) != symbols.end(); }
126 private:
127 std::vector<TSSymbol> symbols;
128};
129
130void symbol_set_from_js(SymbolSet *symbols, const Napi::Value &value, const TSLanguage *language) {
131 Env env = value.Env();
132
133 if (!value.IsArray()) {
134 throw TypeError::New(env, "Argument must be a string or array of strings");
135 }
136
137 unsigned symbol_count = ts_language_symbol_count(language);
138
139 auto js_types = value.As<Array>();
140 for (unsigned i = 0, n = js_types.Length(); i < n; i++) {
141 Value js_node_type_value = js_types[i];
142 if (js_node_type_value.IsString()) {
143 auto js_node_type = js_node_type_value.As<String>();
144 std::string node_type = js_node_type.Utf8Value();
145
146 if (node_type == "ERROR") {
147 symbols->add(static_cast<TSSymbol>(-1));
148 } else {
149 for (TSSymbol j = 0; j < static_cast<TSSymbol>(symbol_count); j++) {
150 if (node_type == ts_language_symbol_name(language, j)) {
151 symbols->add(j);
152 }
153 }
154 }
155
156 continue;
157 }
158
159 throw TypeError::New(env, "Argument must be a string or array of strings");
160 }
161}
162
163namespace {
164
165Napi::Value MarshalNullNode(Napi::Env env) {
166 auto *data = env.GetInstanceData<AddonData>();
167 memset(data->transfer_buffer, 0, FIELD_COUNT_PER_NODE * sizeof(data->transfer_buffer[0]));
168 return env.Undefined();
169}
170
171Napi::Value ToString(const Napi::CallbackInfo &info) {
172 Env env = info.Env();
173 const Tree *tree = Tree::UnwrapTree(info[0]);
174 TSNode node = UnmarshalNode(env, tree);
175 if (node.id != nullptr) {
176 char *string = ts_node_string(node);
177 String result = String::New(env, string);
178 free(string);
179 return result;
180 }
181
182 return env.Undefined();
183}
184
185Napi::Value FirstNamedChildForIndex(const Napi::CallbackInfo &info) {
186 Env env = info.Env();
187 const Tree *tree = Tree::UnwrapTree(info[0]);
188 TSNode node = UnmarshalNode(env, tree);
189 if (node.id != nullptr) {
190 Napi::Maybe<uint32_t> byte = ByteCountFromJS(info[1]);
191 if (byte.IsJust()) {
192 return MarshalNode(info, tree, ts_node_first_named_child_for_byte(node, byte.Unwrap()));
193 }
194 }
195 return MarshalNullNode(env);
196}
197
198Napi::Value FirstChildForIndex(const Napi::CallbackInfo &info) {
199 Env env = info.Env();
200 const Tree *tree = Tree::UnwrapTree(info[0]);
201 TSNode node = UnmarshalNode(env, tree);
202
203 if ((node.id != nullptr) && info.Length() > 1) {
204 Napi::Maybe<uint32_t> byte = ByteCountFromJS(info[1]);
205 if (byte.IsJust()) {
206 return MarshalNode(info, tree, ts_node_first_child_for_byte(node, byte.Unwrap()));
207 }
208 }
209 return MarshalNullNode(env);
210}
211
212Napi::Value NamedDescendantForIndex(const Napi::CallbackInfo &info) {
213 Env env = info.Env();
214 const Tree *tree = Tree::UnwrapTree(info[0]);
215 TSNode node = UnmarshalNode(env, tree);
216
217 if (node.id != nullptr) {
218 Napi::Maybe<uint32_t> maybe_min = ByteCountFromJS(info[1]);
219 Napi::Maybe<uint32_t> maybe_max = ByteCountFromJS(info[2]);
220 if (maybe_min.IsJust() && maybe_max.IsJust()) {
221 uint32_t min = maybe_min.Unwrap();
222 uint32_t max = maybe_max.Unwrap();
223 return MarshalNode(info, tree, ts_node_named_descendant_for_byte_range(node, min, max));
224 }
225 }
226 return MarshalNullNode(env);
227}
228
229Napi::Value DescendantForIndex(const Napi::CallbackInfo &info) {
230 Env env = info.Env();
231 const Tree *tree = Tree::UnwrapTree(info[0]);
232 TSNode node = UnmarshalNode(env, tree);
233
234 if (node.id != nullptr) {
235 Napi::Maybe<uint32_t> maybe_min = ByteCountFromJS(info[1]);
236 Napi::Maybe<uint32_t> maybe_max = ByteCountFromJS(info[2]);
237 if (maybe_min.IsJust() && maybe_max.IsJust()) {
238 uint32_t min = maybe_min.Unwrap();
239 uint32_t max = maybe_max.Unwrap();
240 return MarshalNode(info, tree, ts_node_descendant_for_byte_range(node, min, max));
241 }
242 }
243 return MarshalNullNode(env);
244}
245
246Napi::Value NamedDescendantForPosition(const Napi::CallbackInfo &info) {
247 Env env = info.Env();
248 const Tree *tree = Tree::UnwrapTree(info[0]);
249 TSNode node = UnmarshalNode(env, tree);
250
251 if (node.id != nullptr) {
252 Napi::Maybe<TSPoint> maybe_min = PointFromJS(info[1]);
253 Napi::Maybe<TSPoint> maybe_max = PointFromJS(info[2]);
254 if (maybe_min.IsJust() && maybe_max.IsJust()) {
255 TSPoint min = maybe_min.Unwrap();
256 TSPoint max = maybe_max.Unwrap();
257 return MarshalNode(info, tree, ts_node_named_descendant_for_point_range(node, min, max));
258 }
259 }
260 return MarshalNullNode(env);
261}
262
263Napi::Value DescendantForPosition(const Napi::CallbackInfo &info) {
264 Env env = info.Env();
265 const Tree *tree = Tree::UnwrapTree(info[0]);
266 TSNode node = UnmarshalNode(env, tree);
267
268 if (node.id != nullptr) {
269 Napi::Maybe<TSPoint> maybe_min = PointFromJS(info[1]);
270 Napi::Maybe<TSPoint> maybe_max = PointFromJS(info[2]);
271 if (maybe_min.IsJust() && maybe_max.IsJust()) {
272 TSPoint min = maybe_min.Unwrap();
273 TSPoint max = maybe_max.Unwrap();
274 return MarshalNode(info, tree, ts_node_descendant_for_point_range(node, min, max));
275 }
276 }
277 return MarshalNullNode(env);
278}
279
280Napi::Value Id(const Napi::CallbackInfo &info) {
281 Env env = info.Env();
282 const Tree *tree = Tree::UnwrapTree(info[0]);
283 TSNode node = UnmarshalNode(env, tree);
284
285 if (node.id != nullptr) {
286 return Number::New(env, static_cast<double>(reinterpret_cast<uintptr_t>(node.id)));
287 }
288
289 return env.Undefined();
290}
291
292Napi::Value TypeId(const Napi::CallbackInfo &info) {
293 Env env = info.Env();
294 const Tree *tree = Tree::UnwrapTree(info[0]);
295 TSNode node = UnmarshalNode(env, tree);
296
297 if (node.id != nullptr) {
298 return Number::New(env, ts_node_symbol(node));
299 }
300
301 return env.Undefined();
302}
303
304Napi::Value GrammarId(const Napi::CallbackInfo &info) {
305 Env env = info.Env();
306 const Tree *tree = Tree::UnwrapTree(info[0]);
307 TSNode node = UnmarshalNode(env, tree);
308
309 if (node.id != nullptr) {
310 return Number::New(env, ts_node_grammar_symbol(node));
311 }
312
313 return env.Undefined();
314}
315
316Napi::Value Type(const Napi::CallbackInfo &info) {
317 Env env = info.Env();
318 const Tree *tree = Tree::UnwrapTree(info[0]);
319 TSNode node = UnmarshalNode(env, tree);
320
321 if (node.id != nullptr) {
322 return String::New(env, ts_node_type(node));
323 }
324
325 return env.Undefined();
326}
327
328Napi::Value GrammarType(const Napi::CallbackInfo &info) {
329 Env env = info.Env();
330 const Tree *tree = Tree::UnwrapTree(info[0]);
331 TSNode node = UnmarshalNode(env, tree);
332
333 if (node.id != nullptr) {
334 return String::New(env, ts_node_grammar_type(node));
335 }
336
337 return env.Undefined();
338}
339
340Napi::Value IsNamed(const Napi::CallbackInfo &info) {
341 Env env = info.Env();
342 const Tree *tree = Tree::UnwrapTree(info[0]);
343 TSNode node = UnmarshalNode(env, tree);
344
345 if (node.id != nullptr) {
346 return Boolean::New(env, ts_node_is_named(node));
347 }
348
349 return env.Undefined();
350}
351
352Napi::Value IsExtra(const Napi::CallbackInfo &info) {
353 Env env = info.Env();
354 const Tree *tree = Tree::UnwrapTree(info[0]);
355 TSNode node = UnmarshalNode(env, tree);
356
357 if (node.id != nullptr) {
358 return Boolean::New(env, ts_node_is_extra(node));
359 }
360
361 return env.Undefined();
362}
363
364Napi::Value HasChanges(const Napi::CallbackInfo &info) {
365 Env env = info.Env();
366 const Tree *tree = Tree::UnwrapTree(info[0]);
367 TSNode node = UnmarshalNode(env, tree);
368 if (node.id != nullptr) {
369 bool result = ts_node_has_changes(node);
370 return Boolean::New(env, result);
371 }
372
373 return env.Undefined();
374}
375
376Napi::Value HasError(const Napi::CallbackInfo &info) {
377 Env env = info.Env();
378 const Tree *tree = Tree::UnwrapTree(info[0]);
379 TSNode node = UnmarshalNode(env, tree);
380 if (node.id != nullptr) {
381 bool result = ts_node_has_error(node);
382 return Boolean::New(env, result);
383 }
384
385 return env.Undefined();
386}
387
388Napi::Value IsError(const Napi::CallbackInfo &info) {
389 Env env = info.Env();
390 const Tree *tree = Tree::UnwrapTree(info[0]);
391 TSNode node = UnmarshalNode(env, tree);
392 if (node.id != nullptr) {
393 bool result = ts_node_is_error(node);
394 return Boolean::New(env, result);
395 }
396
397 return env.Undefined();
398}
399
400Napi::Value ParseState(const Napi::CallbackInfo &info) {
401 Env env = info.Env();
402 const Tree *tree = Tree::UnwrapTree(info[0]);
403 TSNode node = UnmarshalNode(env, tree);
404 if (node.id != nullptr) {
405 return Number::New(env, ts_node_parse_state(node));
406 }
407 return env.Undefined();
408}
409
410Napi::Value NextParseState(const Napi::CallbackInfo &info) {
411 Env env = info.Env();
412 const Tree *tree = Tree::UnwrapTree(info[0]);
413 TSNode node = UnmarshalNode(env, tree);
414 if (node.id != nullptr) {
415 return Number::New(env, ts_node_next_parse_state(node));
416 }
417 return env.Undefined();
418}
419
420Napi::Value IsMissing(const Napi::CallbackInfo &info) {
421 Env env = info.Env();
422 const Tree *tree = Tree::UnwrapTree(info[0]);
423 TSNode node = UnmarshalNode(env, tree);
424 if (node.id != nullptr) {
425 bool result = ts_node_is_missing(node);
426 return Boolean::New(env, result);
427 }
428
429 return env.Undefined();
430}
431
432Napi::Value StartIndex(const Napi::CallbackInfo &info) {
433 Env env = info.Env();
434 const Tree *tree = Tree::UnwrapTree(info[0]);
435 TSNode node = UnmarshalNode(env, tree);
436
437 if (node.id != nullptr) {
438 auto result = static_cast<int32_t>(ts_node_start_byte(node) / 2);
439 return Number::New(env, result);
440 }
441
442 return env.Undefined();
443}
444
445Napi::Value EndIndex(const Napi::CallbackInfo &info) {
446 Env env = info.Env();
447 const Tree *tree = Tree::UnwrapTree(info[0]);
448 TSNode node = UnmarshalNode(env, tree);
449
450 if (node.id != nullptr) {
451 auto result = static_cast<int32_t>(ts_node_end_byte(node) / 2);
452 return Number::New(env, result);
453 }
454
455 return env.Undefined();
456}
457
458Napi::Value StartPosition(const Napi::CallbackInfo &info) {
459 Env env = info.Env();
460 const Tree *tree = Tree::UnwrapTree(info[0]);
461 TSNode node = UnmarshalNode(env, tree);
462
463 if (node.id != nullptr) {
464 TransferPoint(env, ts_node_start_point(node));
465 }
466
467 return env.Undefined();
468}
469
470Napi::Value EndPosition(const Napi::CallbackInfo &info) {
471 Env env = info.Env();
472 const Tree *tree = Tree::UnwrapTree(info[0]);
473 TSNode node = UnmarshalNode(env, tree);
474
475 if (node.id != nullptr) {
476 TransferPoint(env, ts_node_end_point(node));
477 }
478
479 return env.Undefined();
480}
481
482Napi::Value Child(const Napi::CallbackInfo &info) {
483 Env env = info.Env();
484 const Tree *tree = Tree::UnwrapTree(info[0]);
485 TSNode node = UnmarshalNode(env, tree);
486
487 if (node.id != nullptr) {
488 if (!info[1].IsNumber()) {
489 throw TypeError::New(env, "Second argument must be an integer");
490 }
491 uint32_t index = info[1].As<Number>().Uint32Value();
492 return MarshalNode(info, tree, ts_node_child(node, index));
493 }
494 return MarshalNullNode(env);
495}
496
497Napi::Value ChildCount(const Napi::CallbackInfo &info) {
498 Env env = info.Env();
499 const Tree *tree = Tree::UnwrapTree(info[0]);
500 TSNode node = UnmarshalNode(env, tree);
501
502 if (node.id != nullptr) {
503 return Number::New(env, ts_node_child_count(node));
504 }
505
506 return env.Undefined();
507}
508
509Napi::Value NamedChild(const Napi::CallbackInfo &info) {
510 Env env = info.Env();
511 const Tree *tree = Tree::UnwrapTree(info[0]);
512 TSNode node = UnmarshalNode(env, tree);
513
514 if (node.id != nullptr) {
515 if (!info[1].IsNumber()) {
516 throw TypeError::New(env, "Second argument must be an integer");
517 }
518 uint32_t index = info[1].As<Number>().Uint32Value();
519 return MarshalNode(info, tree, ts_node_named_child(node, index));
520 }
521 return MarshalNullNode(env);
522}
523
524Napi::Value NamedChildCount(const Napi::CallbackInfo &info) {
525 Env env = info.Env();
526 const Tree *tree = Tree::UnwrapTree(info[0]);
527 TSNode node = UnmarshalNode(env, tree);
528
529 if (node.id != nullptr) {
530 return Number::New(env, ts_node_named_child_count(node));
531 }
532
533 return env.Undefined();
534}
535
536Napi::Value ChildForFieldName(const Napi::CallbackInfo &info) {
537 Env env = info.Env();
538 const Tree *tree = Tree::UnwrapTree(info[0]);
539 TSNode node = UnmarshalNode(env, tree);
540
541 if (node.id != nullptr) {
542 if (!info[1].IsString()) {
543 throw TypeError::New(env, "Second argument must be a string");
544 }
545 std::string field_name = info[1].As<String>().Utf8Value();
546 return MarshalNode(info, tree, ts_node_child_by_field_name(node, field_name.c_str(), field_name.length()));
547 }
548
549 return MarshalNullNode(env);
550}
551
552Napi::Value ChildForFieldId(const Napi::CallbackInfo &info) {
553 Env env = info.Env();
554 const Tree *tree = Tree::UnwrapTree(info[0]);
555 TSNode node = UnmarshalNode(env, tree);
556 if (node.id != nullptr) {
557 if (!info[1].IsNumber()) {
558 throw TypeError::New(env, "Second argument must be an integer");
559 }
560 uint32_t field_id = info[1].As<Number>().Uint32Value();
561 return MarshalNode(info, tree, ts_node_child_by_field_id(node, field_id));
562 }
563 return MarshalNullNode(env);
564}
565
566Napi::Value FieldNameForChild(const Napi::CallbackInfo &info) {
567 Env env = info.Env();
568 const Tree *tree = Tree::UnwrapTree(info[0]);
569 TSNode node = UnmarshalNode(env, tree);
570 if (node.id != nullptr) {
571 if (!info[1].IsNumber()) {
572 throw TypeError::New(env, "Second argument must be an integer");
573 }
574 uint32_t child_id = info[1].As<Number>().Uint32Value();
575 const char *field_name = ts_node_field_name_for_child(node, child_id);
576 if (field_name != nullptr) {
577 return String::New(env, field_name);
578 }
579 }
580 return env.Undefined();
581}
582
583Napi::Value FieldNameForNamedChild(const Napi::CallbackInfo &info) {
584 Env env = info.Env();
585 const Tree *tree = Tree::UnwrapTree(info[0]);
586 TSNode node = UnmarshalNode(env, tree);
587 if (node.id != nullptr) {
588 if (!info[1].IsNumber()) {
589 throw TypeError::New(env, "Second argument must be an integer");
590 }
591 uint32_t child_id = info[1].As<Number>().Uint32Value();
592 const char *field_name = ts_node_field_name_for_named_child(node, child_id);
593 if (field_name != nullptr) {
594 return String::New(env, field_name);
595 }
596 }
597 return env.Undefined();
598}
599
600
601Napi::Value Children(const Napi::CallbackInfo &info) {
602 Env env = info.Env();
603 auto* data = env.GetInstanceData<AddonData>();
604 const Tree *tree = Tree::UnwrapTree(info[0]);
605 TSNode node = UnmarshalNode(env, tree);
606 if (node.id == nullptr) {
607 return env.Undefined();
608 }
609
610 vector<TSNode> result;
611 ts_tree_cursor_reset(&data->scratch_cursor, node);
612 if (ts_tree_cursor_goto_first_child(&data->scratch_cursor)) {
613 do {
614 TSNode child = ts_tree_cursor_current_node(&data->scratch_cursor);
615 result.push_back(child);
616 } while (ts_tree_cursor_goto_next_sibling(&data->scratch_cursor));
617 }
618
619 return MarshalNodes(info, tree, result.data(), result.size());
620}
621
622Napi::Value NamedChildren(const Napi::CallbackInfo &info) {
623 Env env = info.Env();
624 auto* data = env.GetInstanceData<AddonData>();
625 const Tree *tree = Tree::UnwrapTree(info[0]);
626 TSNode node = UnmarshalNode(env, tree);
627 if (node.id == nullptr) {
628 return env.Undefined();
629 }
630
631 vector<TSNode> result;
632 ts_tree_cursor_reset(&data->scratch_cursor, node);
633 if (ts_tree_cursor_goto_first_child(&data->scratch_cursor)) {
634 do {
635 TSNode child = ts_tree_cursor_current_node(&data->scratch_cursor);
636 if (ts_node_is_named(child)) {
637 result.push_back(child);
638 }
639 } while (ts_tree_cursor_goto_next_sibling(&data->scratch_cursor));
640 }
641
642 return MarshalNodes(info, tree, result.data(), result.size());
643}
644
645Napi::Value ChildrenForFieldName(const Napi::CallbackInfo &info) {
646 Env env = info.Env();
647 const Tree *tree = Tree::UnwrapTree(info[0]);
648 TSNode node = UnmarshalNode(env, tree);
649 if (node.id == nullptr) {
650 return env.Undefined();
651 }
652
653 if (!info[1].IsString()) {
654 throw TypeError::New(env, "First argument must be a string");
655 }
656 std::string field_name = info[1].As<String>().Utf8Value();
657
658 TSTreeCursor cursor = ts_tree_cursor_new(node);
659
660 const TSLanguage *language = ts_tree_language(node.tree);
661 TSFieldId field_id = ts_language_field_id_for_name(language, field_name.c_str(), field_name.length());
662
663 bool done = field_id == 0;
664 if (!done) {
665 ts_tree_cursor_reset(&cursor, node);
666 ts_tree_cursor_goto_first_child(&cursor);
667 }
668
669 vector<TSNode> result;
670 while (!done) {
671 while (ts_tree_cursor_current_field_id(&cursor) != field_id) {
672 if (!ts_tree_cursor_goto_next_sibling(&cursor)) {
673 done = true;
674 break;
675 }
676 }
677 if (done) {
678 break;
679 }
680 TSNode result_node = ts_tree_cursor_current_node(&cursor);
681 if (!ts_tree_cursor_goto_next_sibling(&cursor)) {
682 done = true;
683 }
684 result.push_back(result_node);
685 }
686
687 return MarshalNodes(info, tree, result.data(), result.size());
688}
689
690Napi::Value ChildrenForFieldId(const Napi::CallbackInfo &info) {
691 Env env = info.Env();
692 const Tree *tree = Tree::UnwrapTree(info[0]);
693 TSNode node = UnmarshalNode(env, tree);
694 if (node.id == nullptr) {
695 return env.Undefined();
696 }
697
698 if (!info[1].IsNumber()) {
699 throw TypeError::New(env, "First argument must be an integer");
700 }
701 uint32_t field_id = info[1].As<Number>().Uint32Value();
702
703
704 TSTreeCursor cursor = ts_tree_cursor_new(node);
705
706 bool done = field_id == 0;
707 if (!done) {
708 ts_tree_cursor_reset(&cursor, node);
709 ts_tree_cursor_goto_first_child(&cursor);
710 }
711
712 vector<TSNode> result;
713 while (!done) {
714 while (ts_tree_cursor_current_field_id(&cursor) != field_id) {
715 if (!ts_tree_cursor_goto_next_sibling(&cursor)) {
716 done = true;
717 break;
718 }
719 }
720 if (done) {
721 break;
722 }
723 TSNode result_node = ts_tree_cursor_current_node(&cursor);
724 if (!ts_tree_cursor_goto_next_sibling(&cursor)) {
725 done = true;
726 }
727 result.push_back(result_node);
728 }
729
730 return MarshalNodes(info, tree, result.data(), result.size());
731}
732
733Napi::Value FirstChild(const Napi::CallbackInfo &info) {
734 Env env = info.Env();
735 const Tree *tree = Tree::UnwrapTree(info[0]);
736 TSNode node = UnmarshalNode(env, tree);
737 if (node.id != nullptr) {
738 return MarshalNode(info, tree, ts_node_child(node, 0));
739 }
740 return MarshalNullNode(env);
741}
742
743Napi::Value FirstNamedChild(const Napi::CallbackInfo &info) {
744 Env env = info.Env();
745 const Tree *tree = Tree::UnwrapTree(info[0]);
746 TSNode node = UnmarshalNode(env, tree);
747 if (node.id != nullptr) {
748 return MarshalNode(info, tree, ts_node_named_child(node, 0));
749 }
750 return MarshalNullNode(env);
751}
752
753Napi::Value LastChild(const Napi::CallbackInfo &info) {
754 Env env = info.Env();
755 const Tree *tree = Tree::UnwrapTree(info[0]);
756 TSNode node = UnmarshalNode(env, tree);
757 if (node.id != nullptr) {
758 uint32_t child_count = ts_node_child_count(node);
759 if (child_count > 0) {
760 return MarshalNode(info, tree, ts_node_child(node, child_count - 1));
761 }
762 }
763 return MarshalNullNode(env);
764}
765
766Napi::Value LastNamedChild(const Napi::CallbackInfo &info) {
767 Env env = info.Env();
768 const Tree *tree = Tree::UnwrapTree(info[0]);
769 TSNode node = UnmarshalNode(env, tree);
770 if (node.id != nullptr) {
771 uint32_t child_count = ts_node_named_child_count(node);
772 if (child_count > 0) {
773 return MarshalNode(info, tree, ts_node_named_child(node, child_count - 1));
774 }
775 }
776 return MarshalNullNode(env);
777}
778
779Napi::Value Parent(const Napi::CallbackInfo &info) {
780 Env env = info.Env();
781 const Tree *tree = Tree::UnwrapTree(info[0]);
782 TSNode node = UnmarshalNode(env, tree);
783 if (node.id != nullptr) {
784 return MarshalNode(info, tree, ts_node_parent(node));
785 }
786 return MarshalNullNode(env);
787}
788
789Napi::Value ChildWithDescendant(const Napi::CallbackInfo &info) {
790 Env env = info.Env();
791 const Tree *tree = Tree::UnwrapTree(info[0]);
792 TSNode self = UnmarshalNode(env, tree);
793 const Tree *child_tree = Tree::UnwrapTree(info[1]);
794 TSNode child_node = UnmarshalNode(env, child_tree, 1);
795 if (self.id != nullptr && child_node.id != nullptr) {
796 return MarshalNode(info, tree, ts_node_child_with_descendant(self, child_node));
797 }
798 return MarshalNullNode(env);
799}
800
801Napi::Value NextSibling(const Napi::CallbackInfo &info) {
802 Env env = info.Env();
803 const Tree *tree = Tree::UnwrapTree(info[0]);
804 TSNode node = UnmarshalNode(env, tree);
805 if (node.id != nullptr) {
806 return MarshalNode(info, tree, ts_node_next_sibling(node));
807 }
808 return MarshalNullNode(env);
809}
810
811Napi::Value NextNamedSibling(const Napi::CallbackInfo &info) {
812 Env env = info.Env();
813 const Tree *tree = Tree::UnwrapTree(info[0]);
814 TSNode node = UnmarshalNode(env, tree);
815 if (node.id != nullptr) {
816 return MarshalNode(info, tree, ts_node_next_named_sibling(node));
817 }
818 return MarshalNullNode(env);
819}
820
821Napi::Value PreviousSibling(const Napi::CallbackInfo &info) {
822 Env env = info.Env();
823 const Tree *tree = Tree::UnwrapTree(info[0]);
824 TSNode node = UnmarshalNode(env, tree);
825 if (node.id != nullptr) {
826 return MarshalNode(info, tree, ts_node_prev_sibling(node));
827 }
828 return MarshalNullNode(env);
829}
830
831Napi::Value PreviousNamedSibling(const Napi::CallbackInfo &info) {
832 Env env = info.Env();
833 const Tree *tree = Tree::UnwrapTree(info[0]);
834 TSNode node = UnmarshalNode(env, tree);
835 if (node.id != nullptr) {
836 return MarshalNode(info, tree, ts_node_prev_named_sibling(node));
837 }
838 return MarshalNullNode(env);
839}
840
841Napi::Value DescendantCount(const Napi::CallbackInfo &info) {
842 Env env = info.Env();
843 const Tree *tree = Tree::UnwrapTree(info[0]);
844 TSNode node = UnmarshalNode(env, tree);
845 if (node.id != nullptr) {
846 return Number::New(env, ts_node_descendant_count(node));
847 }
848 return env.Undefined();
849}
850
851Napi::Value DescendantsOfType(const Napi::CallbackInfo &info) {
852 Env env = info.Env();
853 auto* data = env.GetInstanceData<AddonData>();
854 const Tree *tree = Tree::UnwrapTree(info[0]);
855 TSNode node = UnmarshalNode(env, tree);
856 if (node.id == nullptr) {
857 return env.Undefined();
858 }
859
860 SymbolSet symbols;
861 symbol_set_from_js(&symbols, info[1], ts_tree_language(node.tree));
862
863 TSPoint start_point = {0, 0};
864 TSPoint end_point = {UINT32_MAX, UINT32_MAX};
865
866 if (info.Length() > 2 && info[2].IsObject()) {
867 auto maybe_start_point = PointFromJS(info[2]);
868 if (maybe_start_point.IsNothing()) {
869 return env.Undefined();
870 }
871 start_point = maybe_start_point.Unwrap();
872 }
873
874 if (info.Length() > 3 && info[3].IsObject()) {
875 auto maybe_end_point = PointFromJS(info[3]);
876 if (maybe_end_point.IsNothing()) {
877 return env.Undefined();
878 }
879 end_point = maybe_end_point.Unwrap();
880 }
881
882 vector<TSNode> found;
883 ts_tree_cursor_reset(&data->scratch_cursor, node);
884 auto already_visited_children = false;
885 while (true) {
886 TSNode descendant = ts_tree_cursor_current_node(&data->scratch_cursor);
887
888 if (!already_visited_children) {
889 if (ts_node_end_point(descendant) <= start_point) {
890 if (ts_tree_cursor_goto_next_sibling(&data->scratch_cursor)) {
891 already_visited_children = false;
892 } else {
893 if (!ts_tree_cursor_goto_parent(&data->scratch_cursor)) {
894 break;
895 }
896 already_visited_children = true;
897 }
898 continue;
899 }
900
901 if (end_point <= ts_node_start_point(descendant)) {
902 break;
903 }
904
905 if (symbols.contains(ts_node_symbol(descendant))) {
906 found.push_back(descendant);
907 }
908
909 if (ts_tree_cursor_goto_first_child(&data->scratch_cursor)) {
910 already_visited_children = false;
911 } else if (ts_tree_cursor_goto_next_sibling(&data->scratch_cursor)) {
912 already_visited_children = false;
913 } else {
914 if (!ts_tree_cursor_goto_parent(&data->scratch_cursor)) {
915 break;
916 }
917 already_visited_children = true;
918 }
919 } else {
920 if (ts_tree_cursor_goto_next_sibling(&data->scratch_cursor)) {
921 already_visited_children = false;
922 } else {
923 if (!ts_tree_cursor_goto_parent(&data->scratch_cursor)) {
924 break;
925 }
926 }
927 }
928 }
929
930 return MarshalNodes(info, tree, found.data(), found.size());
931}
932
933Napi::Value ChildNodesForFieldId(const Napi::CallbackInfo &info) {
934 Env env = info.Env();
935 auto* data = env.GetInstanceData<AddonData>();
936 const Tree *tree = Tree::UnwrapTree(info[0]);
937 TSNode node = UnmarshalNode(env, tree);
938 if (node.id == nullptr) {
939 return env.Undefined();
940 }
941
942 if (!info[1].IsNumber()) {
943 throw TypeError::New(env, "Second argument must be an integer");
944 }
945 auto maybe_field_id = info[1].As<Number>();
946 uint32_t field_id = maybe_field_id.Uint32Value();
947
948 vector<TSNode> result;
949 ts_tree_cursor_reset(&data->scratch_cursor, node);
950 if (ts_tree_cursor_goto_first_child(&data->scratch_cursor)) {
951 do {
952 TSNode child = ts_tree_cursor_current_node(&data->scratch_cursor);
953 if (ts_tree_cursor_current_field_id(&data->scratch_cursor) == field_id) {
954 result.push_back(child);
955 }
956 } while (ts_tree_cursor_goto_next_sibling(&data->scratch_cursor));
957 }
958
959 return MarshalNodes(info, tree, result.data(), result.size());
960}
961
962Napi::Value ChildNodeForFieldId(const Napi::CallbackInfo &info) {
963 Env env = info.Env();
964 const Tree *tree = Tree::UnwrapTree(info[0]);
965 TSNode node = UnmarshalNode(env, tree);
966
967 if (node.id != nullptr) {
968 if (!info[1].IsNumber()) {
969 throw TypeError::New(env, "Second argument must be an integer");
970 }
971 auto maybe_field_id = info[1].As<Number>();
972 uint32_t field_id = maybe_field_id.Uint32Value();
973 return MarshalNode(info, tree, ts_node_child_by_field_id(node, field_id));
974 }
975 return MarshalNullNode(env);
976}
977
978Napi::Value Closest(const Napi::CallbackInfo &info) {
979 Env env = info.Env();
980 const Tree *tree = Tree::UnwrapTree(info[0]);
981 TSNode node = UnmarshalNode(env, tree);
982 if (node.id == nullptr) {
983 return env.Undefined();
984 }
985
986 SymbolSet symbols;
987 symbol_set_from_js(&symbols, info[1], ts_tree_language(node.tree));
988
989 for (;;) {
990 TSNode parent = ts_node_parent(node);
991 if (parent.id == nullptr) {
992 break;
993 }
994 if (symbols.contains(ts_node_symbol(parent))) {
995 return MarshalNode(info, tree, parent);
996 }
997 node = parent;
998 }
999
1000 return MarshalNullNode(env);
1001}
1002
1003Napi::Value Walk(const Napi::CallbackInfo &info) {
1004 Env env = info.Env();
1005 const Tree *tree = Tree::UnwrapTree(info[0]);
1006 TSNode node = UnmarshalNode(env, tree);
1007 TSTreeCursor cursor = ts_tree_cursor_new(node);
1008 return TreeCursor::NewInstance(env, cursor);
1009}
1010
1011} // namespace
1012
1013void Init(Napi::Env env, Napi::Object exports) {
1014 auto *data = env.GetInstanceData<AddonData>();
1015
1016 Object result = Object::New(env);
1017
1018 struct FunctionPair {
1019 const char *name;
1020 Napi::Function::Callback callback;
1021 };
1022
1023 FunctionPair methods[] = {
1024 {"id", Id},
1025 {"typeId", TypeId},
1026 {"grammarId", GrammarId},
1027 {"type", Type},
1028 {"grammarType", GrammarType},
1029 {"isNamed", IsNamed},
1030 {"isExtra", IsExtra},
1031 {"hasChanges", HasChanges},
1032 {"hasError", HasError},
1033 {"isError", IsError},
1034 {"parseState", ParseState},
1035 {"nextParseState", NextParseState},
1036 {"isMissing", IsMissing},
1037 {"startIndex", StartIndex},
1038 {"endIndex", EndIndex},
1039 {"startPosition", StartPosition},
1040 {"endPosition", EndPosition},
1041 {"child", Child},
1042 {"childCount", ChildCount},
1043 {"namedChild", NamedChild},
1044 {"namedChildCount", NamedChildCount},
1045 {"childForFieldName", ChildForFieldName},
1046 {"childForFieldId", ChildForFieldId},
1047 {"fieldNameForChild", FieldNameForChild},
1048 {"fieldNameForNamedChild", FieldNameForNamedChild},
1049 {"children", Children},
1050 {"namedChildren", NamedChildren},
1051 {"childrenForFieldName", ChildrenForFieldName},
1052 {"childrenForFieldId", ChildrenForFieldId},
1053 {"parent", Parent},
1054 {"childWithDescendant", ChildWithDescendant},
1055 {"nextSibling", NextSibling},
1056 {"previousSibling", PreviousSibling},
1057 {"nextNamedSibling", NextNamedSibling},
1058 {"previousNamedSibling", PreviousNamedSibling},
1059 {"descendantCount", DescendantCount},
1060 {"descendantForIndex", DescendantForIndex},
1061 {"namedDescendantForIndex", NamedDescendantForIndex},
1062 {"descendantForPosition", DescendantForPosition},
1063 {"namedDescendantForPosition", NamedDescendantForPosition},
1064 {"toString", ToString},
1065 {"walk", Walk},
1066 {"firstChild", FirstChild},
1067 {"lastChild", LastChild},
1068 {"firstNamedChild", FirstNamedChild},
1069 {"lastNamedChild", LastNamedChild},
1070 {"firstChildForIndex", FirstChildForIndex},
1071 {"firstNamedChildForIndex", FirstNamedChildForIndex},
1072 {"descendantsOfType", DescendantsOfType},
1073 {"closest", Closest},
1074 {"childNodeForFieldId", ChildNodeForFieldId},
1075 {"childNodesForFieldId", ChildNodesForFieldId},
1076 };
1077
1078 for (auto & method : methods) {
1079 result[method.name] = Napi::Function::New(env, method.callback);
1080 }
1081
1082 data->module_exports = Napi::Persistent(exports);
1083 setup_transfer_buffer(env, 1);
1084
1085 exports["NodeMethods"] = result;
1086}
1087
1088} // namespace node_tree_sitter::node_methods