OR-1 dataflow CPU sketch
1const binding = typeof process.versions.bun === "string" ?
2 // Statically analyzable enough for `bun build --compile` to embed the tree-sitter.node napi addon
3 require(`./prebuilds/${process.platform}-${process.arch}/tree-sitter.node`) :
4 require('node-gyp-build')(__dirname);
5const {Query, Parser, NodeMethods, Tree, TreeCursor, LookaheadIterator} = binding;
6
7const util = require('util');
8
9/*
10 * Tree
11 */
12
13const {rootNode, rootNodeWithOffset, edit} = Tree.prototype;
14
15Object.defineProperty(Tree.prototype, 'rootNode', {
16 get() {
17 /*
18 Due to a race condition arising from Jest's worker pool, "this"
19 has no knowledge of the native extension if the extension has not
20 yet loaded when multiple Jest tests are being run simultaneously.
21 If the extension has correctly loaded, "this" should be an instance
22 of the class whose prototype we are acting on (in this case, Tree).
23 Furthermore, the race condition sometimes results in the function in
24 question being undefined even when the context is correct, so we also
25 perform a null function check.
26 */
27 if (this instanceof Tree && rootNode) {
28 return unmarshalNode(rootNode.call(this), this);
29 }
30 },
31 // Jest worker pool may attempt to override property due to race condition,
32 // we don't want to error on this
33 configurable: true
34});
35
36Tree.prototype.rootNodeWithOffset = function(offset_bytes, offset_extent) {
37 return unmarshalNode(rootNodeWithOffset.call(this, offset_bytes, offset_extent.row, offset_extent.column), this);
38}
39
40Tree.prototype.edit = function(arg) {
41 if (this instanceof Tree && edit) {
42 edit.call(
43 this,
44 arg.startPosition.row, arg.startPosition.column,
45 arg.oldEndPosition.row, arg.oldEndPosition.column,
46 arg.newEndPosition.row, arg.newEndPosition.column,
47 arg.startIndex,
48 arg.oldEndIndex,
49 arg.newEndIndex
50 );
51 }
52};
53
54Tree.prototype.walk = function() {
55 return this.rootNode.walk()
56};
57
58/*
59 * Node
60 */
61
62class SyntaxNode {
63 constructor(tree) {
64 this.tree = tree;
65 }
66
67 [util.inspect.custom]() {
68 return this.constructor.name + ' {\n' +
69 ' type: ' + this.type + ',\n' +
70 ' startPosition: ' + pointToString(this.startPosition) + ',\n' +
71 ' endPosition: ' + pointToString(this.endPosition) + ',\n' +
72 ' childCount: ' + this.childCount + ',\n' +
73 '}'
74 }
75
76 get id() {
77 marshalNode(this);
78 return NodeMethods.id(this.tree);
79 }
80
81 get typeId() {
82 marshalNode(this);
83 return NodeMethods.typeId(this.tree);
84 }
85
86 get grammarId() {
87 marshalNode(this);
88 return NodeMethods.grammarId(this.tree);
89 }
90
91 get type() {
92 marshalNode(this);
93 return NodeMethods.type(this.tree);
94 }
95
96 get grammarType() {
97 marshalNode(this);
98 return NodeMethods.grammarType(this.tree);
99 }
100
101 get isExtra() {
102 marshalNode(this);
103 return NodeMethods.isExtra(this.tree);
104 }
105
106 get isNamed() {
107 marshalNode(this);
108 return NodeMethods.isNamed(this.tree);
109 }
110
111 get isMissing() {
112 marshalNode(this);
113 return NodeMethods.isMissing(this.tree);
114 }
115
116 get hasChanges() {
117 marshalNode(this);
118 return NodeMethods.hasChanges(this.tree);
119 }
120
121 get hasError() {
122 marshalNode(this);
123 return NodeMethods.hasError(this.tree);
124 }
125
126 get isError() {
127 marshalNode(this);
128 return NodeMethods.isError(this.tree);
129 }
130
131 get text() {
132 return this.tree.getText(this);
133 }
134
135 get startPosition() {
136 marshalNode(this);
137 NodeMethods.startPosition(this.tree);
138 return unmarshalPoint();
139 }
140
141 get endPosition() {
142 marshalNode(this);
143 NodeMethods.endPosition(this.tree);
144 return unmarshalPoint();
145 }
146
147 get startIndex() {
148 marshalNode(this);
149 return NodeMethods.startIndex(this.tree);
150 }
151
152 get endIndex() {
153 marshalNode(this);
154 return NodeMethods.endIndex(this.tree);
155 }
156
157 get parent() {
158 marshalNode(this);
159 return unmarshalNode(NodeMethods.parent(this.tree), this.tree);
160 }
161
162 get children() {
163 marshalNode(this);
164 return unmarshalNodes(NodeMethods.children(this.tree), this.tree);
165 }
166
167 get namedChildren() {
168 marshalNode(this);
169 return unmarshalNodes(NodeMethods.namedChildren(this.tree), this.tree);
170 }
171
172 get childCount() {
173 marshalNode(this);
174 return NodeMethods.childCount(this.tree);
175 }
176
177 get namedChildCount() {
178 marshalNode(this);
179 return NodeMethods.namedChildCount(this.tree);
180 }
181
182 get firstChild() {
183 marshalNode(this);
184 return unmarshalNode(NodeMethods.firstChild(this.tree), this.tree);
185 }
186
187 get firstNamedChild() {
188 marshalNode(this);
189 return unmarshalNode(NodeMethods.firstNamedChild(this.tree), this.tree);
190 }
191
192 get lastChild() {
193 marshalNode(this);
194 return unmarshalNode(NodeMethods.lastChild(this.tree), this.tree);
195 }
196
197 get lastNamedChild() {
198 marshalNode(this);
199 return unmarshalNode(NodeMethods.lastNamedChild(this.tree), this.tree);
200 }
201
202 get nextSibling() {
203 marshalNode(this);
204 return unmarshalNode(NodeMethods.nextSibling(this.tree), this.tree);
205 }
206
207 get nextNamedSibling() {
208 marshalNode(this);
209 return unmarshalNode(NodeMethods.nextNamedSibling(this.tree), this.tree);
210 }
211
212 get previousSibling() {
213 marshalNode(this);
214 return unmarshalNode(NodeMethods.previousSibling(this.tree), this.tree);
215 }
216
217 get previousNamedSibling() {
218 marshalNode(this);
219 return unmarshalNode(NodeMethods.previousNamedSibling(this.tree), this.tree);
220 }
221
222 get parseState() {
223 marshalNode(this);
224 return NodeMethods.parseState(this.tree);
225 }
226
227 get nextParseState() {
228 marshalNode(this);
229 return NodeMethods.nextParseState(this.tree);
230 }
231
232 get descendantCount() {
233 marshalNode(this);
234 return NodeMethods.descendantCount(this.tree);
235 }
236
237 toString() {
238 marshalNode(this);
239 return NodeMethods.toString(this.tree);
240 }
241
242 child(index) {
243 marshalNode(this);
244 return unmarshalNode(NodeMethods.child(this.tree, index), this.tree);
245 }
246
247 namedChild(index) {
248 marshalNode(this);
249 return unmarshalNode(NodeMethods.namedChild(this.tree, index), this.tree);
250 }
251
252 childForFieldName(fieldName) {
253 marshalNode(this);
254 return unmarshalNode(NodeMethods.childForFieldName(this.tree, fieldName), this.tree);
255 }
256
257 childForFieldId(fieldId) {
258 marshalNode(this);
259 return unmarshalNode(NodeMethods.childForFieldId(this.tree, fieldId), this.tree);
260 }
261
262 fieldNameForChild(childIndex) {
263 marshalNode(this);
264 return NodeMethods.fieldNameForChild(this.tree, childIndex);
265 }
266
267 fieldNameForNamedChild(namedChildIndex) {
268 marshalNode(this);
269 return NodeMethods.fieldNameForNamedChild(this.tree, namedChildIndex);
270 }
271
272 childrenForFieldName(fieldName) {
273 marshalNode(this);
274 return unmarshalNodes(NodeMethods.childrenForFieldName(this.tree, fieldName), this.tree);
275 }
276
277 childrenForFieldId(fieldId) {
278 marshalNode(this);
279 return unmarshalNodes(NodeMethods.childrenForFieldId(this.tree, fieldId), this.tree);
280 }
281
282 firstChildForIndex(index) {
283 marshalNode(this);
284 return unmarshalNode(NodeMethods.firstChildForIndex(this.tree, index), this.tree);
285 }
286
287 firstNamedChildForIndex(index) {
288 marshalNode(this);
289 return unmarshalNode(NodeMethods.firstNamedChildForIndex(this.tree, index), this.tree);
290 }
291
292 childWithDescendant(descendant) {
293 marshalNodes([this, descendant]);
294 return unmarshalNode(NodeMethods.childWithDescendant(this.tree, descendant.tree), this.tree);
295 }
296
297 namedDescendantForIndex(start, end) {
298 marshalNode(this);
299 if (end == null) end = start;
300 return unmarshalNode(NodeMethods.namedDescendantForIndex(this.tree, start, end), this.tree);
301 }
302
303 descendantForIndex(start, end) {
304 marshalNode(this);
305 if (end == null) end = start;
306 return unmarshalNode(NodeMethods.descendantForIndex(this.tree, start, end), this.tree);
307 }
308
309 descendantsOfType(types, start, end) {
310 marshalNode(this);
311 if (typeof types === 'string') types = [types]
312 return unmarshalNodes(NodeMethods.descendantsOfType(this.tree, types, start, end), this.tree);
313 }
314
315 namedDescendantForPosition(start, end) {
316 marshalNode(this);
317 if (end == null) end = start;
318 return unmarshalNode(NodeMethods.namedDescendantForPosition(this.tree, start, end), this.tree);
319 }
320
321 descendantForPosition(start, end) {
322 marshalNode(this);
323 if (end == null) end = start;
324 return unmarshalNode(NodeMethods.descendantForPosition(this.tree, start, end), this.tree);
325 }
326
327 closest(types) {
328 marshalNode(this);
329 if (typeof types === 'string') types = [types]
330 return unmarshalNode(NodeMethods.closest(this.tree, types), this.tree);
331 }
332
333 walk () {
334 marshalNode(this);
335 const cursor = NodeMethods.walk(this.tree);
336 cursor.tree = this.tree;
337 unmarshalNode(cursor.currentNode, this.tree);
338 return cursor;
339 }
340}
341
342/*
343 * Parser
344 */
345
346const {parse, setLanguage} = Parser.prototype;
347const languageSymbol = Symbol('parser.language');
348
349Parser.prototype.setLanguage = function(language) {
350 if (this instanceof Parser && setLanguage) {
351 setLanguage.call(this, language);
352 }
353 this[languageSymbol] = language;
354 if (!language.nodeSubclasses) {
355 initializeLanguageNodeClasses(language)
356 }
357 return this;
358};
359
360Parser.prototype.getLanguage = function(_language) {
361 return this[languageSymbol] || null;
362};
363
364Parser.prototype.parse = function(input, oldTree, {bufferSize, includedRanges}={}) {
365 let getText, treeInput = input
366 if (typeof input === 'string') {
367 const inputString = input;
368 input = (offset, _position) => inputString.slice(offset)
369 getText = getTextFromString
370 } else {
371 getText = getTextFromFunction
372 }
373 const tree = this instanceof Parser && parse
374 ? parse.call(
375 this,
376 input,
377 oldTree,
378 bufferSize,
379 includedRanges,
380 )
381 : undefined;
382
383 if (tree) {
384 tree.input = treeInput
385 tree.getText = getText
386 tree.language = this.getLanguage()
387 }
388 return tree
389};
390
391/*
392 * TreeCursor
393 */
394
395const {startPosition, endPosition, currentNode} = TreeCursor.prototype;
396
397Object.defineProperties(TreeCursor.prototype, {
398 currentNode: {
399 get() {
400 if (this instanceof TreeCursor && currentNode) {
401 return unmarshalNode(currentNode.call(this), this.tree);
402 }
403 },
404 configurable: true
405 },
406 startPosition: {
407 get() {
408 if (this instanceof TreeCursor && startPosition) {
409 startPosition.call(this);
410 return unmarshalPoint();
411 }
412 },
413 configurable: true
414 },
415 endPosition: {
416 get() {
417 if (this instanceof TreeCursor && endPosition) {
418 endPosition.call(this);
419 return unmarshalPoint();
420 }
421 },
422 configurable: true
423 },
424 nodeText: {
425 get() {
426 return this.tree.getText(this)
427 },
428 configurable: true
429 }
430});
431
432/*
433 * Query
434 */
435
436const {_matches, _captures} = Query.prototype;
437
438const PREDICATE_STEP_TYPE = {
439 DONE: 0,
440 CAPTURE: 1,
441 STRING: 2,
442}
443
444const ZERO_POINT = { row: 0, column: 0 };
445
446Query.prototype._init = function() {
447 /*
448 * Initialize predicate functions
449 * format: [type1, value1, type2, value2, ...]
450 */
451 const predicateDescriptions = this._getPredicates();
452 const patternCount = predicateDescriptions.length;
453
454 const setProperties = new Array(patternCount);
455 const assertedProperties = new Array(patternCount);
456 const refutedProperties = new Array(patternCount);
457 const predicates = new Array(patternCount);
458
459 const FIRST = 0
460 const SECOND = 2
461 const THIRD = 4
462
463 for (let i = 0; i < predicateDescriptions.length; i++) {
464 predicates[i] = [];
465
466 for (let j = 0; j < predicateDescriptions[i].length; j++) {
467
468 const steps = predicateDescriptions[i][j];
469 const stepsLength = steps.length / 2;
470
471 if (steps[FIRST] !== PREDICATE_STEP_TYPE.STRING) {
472 throw new Error('Predicates must begin with a literal value');
473 }
474
475 const operator = steps[FIRST + 1];
476
477 let isPositive = true;
478 let matchAll = true;
479 let captureName;
480 switch (operator) {
481 case 'any-not-eq?':
482 case 'not-eq?':
483 isPositive = false;
484 case 'any-eq?':
485 case 'eq?':
486 if (stepsLength !== 3) throw new Error(
487 `Wrong number of arguments to \`#eq?\` predicate. Expected 2, got ${stepsLength - 1}`
488 );
489 if (steps[SECOND] !== PREDICATE_STEP_TYPE.CAPTURE) throw new Error(
490 `First argument of \`#eq?\` predicate must be a capture. Got "${steps[SECOND + 1]}"`
491 );
492 matchAll = !operator.startsWith('any-');
493 if (steps[THIRD] === PREDICATE_STEP_TYPE.CAPTURE) {
494 const captureName1 = steps[SECOND + 1];
495 const captureName2 = steps[THIRD + 1];
496 predicates[i].push(function (captures) {
497 let nodes_1 = [];
498 let nodes_2 = [];
499 for (const c of captures) {
500 if (c.name === captureName1) nodes_1.push(c.node);
501 if (c.name === captureName2) nodes_2.push(c.node);
502 }
503 let compare = (n1, n2, positive) => {
504 return positive ?
505 n1.text === n2.text :
506 n1.text !== n2.text;
507 };
508 return matchAll
509 ? nodes_1.every(n1 => nodes_2.some(n2 => compare(n1, n2, isPositive)))
510 : nodes_1.some(n1 => nodes_2.some(n2 => compare(n1, n2, isPositive)));
511 });
512 } else {
513 captureName = steps[SECOND + 1];
514 const stringValue = steps[THIRD + 1];
515 let matches = (n) => n.text === stringValue;
516 let doesNotMatch = (n) => n.text !== stringValue;
517 predicates[i].push(function (captures) {
518 let nodes = [];
519 for (const c of captures) {
520 if (c.name === captureName) nodes.push(c.node);
521 }
522 let test = isPositive ? matches : doesNotMatch;
523 return matchAll
524 ? nodes.every(test)
525 : nodes.some(test);
526 });
527 }
528 break;
529
530 case 'any-not-match?':
531 case 'not-match?':
532 isPositive = false;
533 case 'any-match?':
534 case 'match?':
535 if (stepsLength !== 3) throw new Error(
536 `Wrong number of arguments to \`#match?\` predicate. Expected 2, got ${stepsLength - 1}.`
537 );
538 if (steps[SECOND] !== PREDICATE_STEP_TYPE.CAPTURE) throw new Error(
539 `First argument of \`#match?\` predicate must be a capture. Got "${steps[SECOND + 1]}".`
540 );
541 if (steps[THIRD] !== PREDICATE_STEP_TYPE.STRING) throw new Error(
542 `Second argument of \`#match?\` predicate must be a string. Got @${steps[THIRD + 1]}.`
543 );
544 captureName = steps[SECOND + 1];
545 const regex = new RegExp(steps[THIRD + 1]);
546 matchAll = !operator.startsWith('any-');
547 predicates[i].push(function (captures) {
548 const nodes = [];
549 for (const c of captures) {
550 if (c.name === captureName) nodes.push(c.node.text);
551 }
552 let test = (text, positive) => {
553 return positive ?
554 regex.test(text) :
555 !regex.test(text);
556 };
557 if (nodes.length === 0) return !isPositive;
558 return matchAll
559 ? nodes.every(text => test(text, isPositive))
560 : nodes.some(text => test(text, isPositive))
561 });
562 break;
563
564 case 'set!':
565 if (stepsLength < 2 || stepsLength > 3) throw new Error(
566 `Wrong number of arguments to \`#set!\` predicate. Expected 1 or 2. Got ${stepsLength - 1}.`
567 );
568 if (steps.some((s, i) => (i % 2 !== 1) && s !== PREDICATE_STEP_TYPE.STRING)) throw new Error(
569 `Arguments to \`#set!\` predicate must be a strings.".`
570 );
571 if (!setProperties[i]) setProperties[i] = {};
572 setProperties[i][steps[SECOND + 1]] = steps[THIRD] ? steps[THIRD + 1] : null;
573 break;
574
575 case 'is?':
576 case 'is-not?':
577 if (stepsLength < 2 || stepsLength > 3) throw new Error(
578 `Wrong number of arguments to \`#${operator}\` predicate. Expected 1 or 2. Got ${stepsLength - 1}.`
579 );
580 if (steps.some((s, i) => (i % 2 !== 1) && s !== PREDICATE_STEP_TYPE.STRING)) throw new Error(
581 `Arguments to \`#${operator}\` predicate must be a strings.".`
582 );
583 const properties = operator === 'is?' ? assertedProperties : refutedProperties;
584 if (!properties[i]) properties[i] = {};
585 properties[i][steps[SECOND + 1]] = steps[THIRD] ? steps[THIRD + 1] : null;
586 break;
587
588 case 'not-any-of?':
589 isPositive = false;
590 case 'any-of?':
591 if (stepsLength < 2) throw new Error(
592 `Wrong number of arguments to \`#${operator}\` predicate. Expected at least 1. Got ${stepsLength - 1}.`
593 );
594 if (steps[SECOND] !== PREDICATE_STEP_TYPE.CAPTURE) throw new Error(
595 `First argument of \`#${operator}\` predicate must be a capture. Got "${steps[1].value}".`
596 );
597 const stringValues = [];
598 for (let k = THIRD; k < 2 * stepsLength; k += 2) {
599 if (steps[k] !== PREDICATE_STEP_TYPE.STRING) throw new Error(
600 `Arguments to \`#${operator}\` predicate must be a strings.".`
601 );
602 stringValues.push(steps[k + 1]);
603 }
604 captureName = steps[SECOND + 1];
605 predicates[i].push(function (captures) {
606 const nodes = [];
607 for (const c of captures) {
608 if (c.name === captureName) nodes.push(c.node.text);
609 }
610 if (nodes.length === 0) return !isPositive;
611 return nodes.every(text => stringValues.includes(text)) === isPositive;
612 });
613 break;
614
615 default:
616 throw new Error(`Unknown query predicate \`#${steps[FIRST + 1]}\``);
617 }
618 }
619 }
620
621 this.predicates = Object.freeze(predicates);
622 this.setProperties = Object.freeze(setProperties);
623 this.assertedProperties = Object.freeze(assertedProperties);
624 this.refutedProperties = Object.freeze(refutedProperties);
625}
626
627Query.prototype.matches = function(
628 node,
629 {
630 startPosition = ZERO_POINT,
631 endPosition = ZERO_POINT,
632 startIndex = 0,
633 endIndex = 0,
634 matchLimit = 0xFFFFFFFF,
635 maxStartDepth = 0xFFFFFFFF,
636 timeoutMicros = 0
637 } = {}
638) {
639 marshalNode(node);
640 const [returnedMatches, returnedNodes] = _matches.call(this, node.tree,
641 startPosition.row, startPosition.column,
642 endPosition.row, endPosition.column,
643 startIndex, endIndex, matchLimit, maxStartDepth, timeoutMicros
644 );
645 const nodes = unmarshalNodes(returnedNodes, node.tree);
646 const results = [];
647
648 let i = 0
649 let nodeIndex = 0;
650 while (i < returnedMatches.length) {
651 const patternIndex = returnedMatches[i++];
652 const captures = [];
653
654 while (i < returnedMatches.length && typeof returnedMatches[i] === 'string') {
655 const captureName = returnedMatches[i++];
656 captures.push({
657 name: captureName,
658 node: nodes[nodeIndex++],
659 })
660 }
661
662 if (this.predicates[patternIndex].every(p => p(captures))) {
663 const result = {pattern: patternIndex, captures};
664 const setProperties = this.setProperties[patternIndex];
665 const assertedProperties = this.assertedProperties[patternIndex];
666 const refutedProperties = this.refutedProperties[patternIndex];
667 if (setProperties) result.setProperties = setProperties;
668 if (assertedProperties) result.assertedProperties = assertedProperties;
669 if (refutedProperties) result.refutedProperties = refutedProperties;
670 results.push(result);
671 }
672 }
673
674 return results;
675}
676
677Query.prototype.captures = function(
678 node,
679 {
680 startPosition = ZERO_POINT,
681 endPosition = ZERO_POINT,
682 startIndex = 0,
683 endIndex = 0,
684 matchLimit = 0xFFFFFFFF,
685 maxStartDepth = 0xFFFFFFFF,
686 timeoutMicros = 0,
687 } = {}
688) {
689 marshalNode(node);
690 const [returnedMatches, returnedNodes] = _captures.call(this, node.tree,
691 startPosition.row, startPosition.column,
692 endPosition.row, endPosition.column,
693 startIndex, endIndex, matchLimit, maxStartDepth, timeoutMicros
694 );
695 const nodes = unmarshalNodes(returnedNodes, node.tree);
696 const results = [];
697
698 let i = 0
699 let nodeIndex = 0;
700 while (i < returnedMatches.length) {
701 const patternIndex = returnedMatches[i++];
702 const captureIndex = returnedMatches[i++];
703 const captures = [];
704
705 while (i < returnedMatches.length && typeof returnedMatches[i] === 'string') {
706 const captureName = returnedMatches[i++];
707 captures.push({
708 name: captureName,
709 node: nodes[nodeIndex++],
710 })
711 }
712
713 if (this.predicates[patternIndex].every(p => p(captures))) {
714 const result = captures[captureIndex];
715 const setProperties = this.setProperties[patternIndex];
716 const assertedProperties = this.assertedProperties[patternIndex];
717 const refutedProperties = this.refutedProperties[patternIndex];
718 if (setProperties) result.setProperties = setProperties;
719 if (assertedProperties) result.assertedProperties = assertedProperties;
720 if (refutedProperties) result.refutedProperties = refutedProperties;
721 results.push(result);
722 }
723 }
724
725 return results;
726}
727
728/*
729 * LookaheadIterator
730 */
731
732LookaheadIterator.prototype[Symbol.iterator] = function() {
733 const self = this;
734 return {
735 next() {
736 if (self._next()) {
737 return {done: false, value: self.currentType};
738 }
739
740 return {done: true, value: ''};
741 },
742 };
743}
744
745/*
746 * Other functions
747 */
748
749function getTextFromString (node) {
750 return this.input.substring(node.startIndex, node.endIndex);
751}
752
753function getTextFromFunction ({startIndex, endIndex}) {
754 const {input} = this
755 let result = '';
756 const goalLength = endIndex - startIndex;
757 while (result.length < goalLength) {
758 const text = input(startIndex + result.length);
759 result += text;
760 }
761 return result.slice(0, goalLength);
762}
763
764const {pointTransferArray} = binding;
765
766const NODE_FIELD_COUNT = 6;
767const ERROR_TYPE_ID = 0xFFFF
768
769function getID(buffer, offset) {
770 const low = BigInt(buffer[offset]);
771 const high = BigInt(buffer[offset + 1]);
772 return (high << 32n) + low;
773}
774
775function unmarshalNode(value, tree, offset = 0, cache = null) {
776 /* case 1: node from the tree cache */
777 if (typeof value === 'object') {
778 const node = value;
779 return node;
780 }
781
782 /* case 2: node being transferred */
783 const nodeTypeId = value;
784 const NodeClass = nodeTypeId === ERROR_TYPE_ID
785 ? SyntaxNode
786 : tree.language.nodeSubclasses[nodeTypeId];
787
788 const {nodeTransferArray} = binding;
789 const id = getID(nodeTransferArray, offset)
790 if (id === 0n) {
791 return null
792 }
793
794 let cachedResult;
795 if (cache && (cachedResult = cache.get(id)))
796 return cachedResult;
797
798 const result = new NodeClass(tree);
799 for (let i = 0; i < NODE_FIELD_COUNT; i++) {
800 result[i] = nodeTransferArray[offset + i];
801 }
802
803 if (cache)
804 cache.set(id, result);
805 else
806 tree._cacheNode(result);
807
808 return result;
809}
810
811function unmarshalNodes(nodes, tree) {
812 const cache = new Map();
813
814 let offset = 0;
815 for (let i = 0, {length} = nodes; i < length; i++) {
816 const node = unmarshalNode(nodes[i], tree, offset, cache);
817 if (node !== nodes[i]) {
818 nodes[i] = node;
819 offset += NODE_FIELD_COUNT
820 }
821 }
822
823 tree._cacheNodes(Array.from(cache.values()));
824
825 return nodes;
826}
827
828function marshalNode(node, offset = 0) {
829 if (!(node.tree instanceof Tree)) {
830 throw new TypeError("SyntaxNode must belong to a Tree")
831 }
832 const { nodeTransferArray } = binding;
833 for (let i = 0; i < NODE_FIELD_COUNT; i++) {
834 nodeTransferArray[offset * NODE_FIELD_COUNT + i] = node[i];
835 }
836}
837
838function marshalNodes(nodes) {
839 for (let i = 0, { length } = nodes; i < length; i++) {
840 marshalNode(nodes[i], i);
841 }
842}
843
844function unmarshalPoint() {
845 return {row: pointTransferArray[0], column: pointTransferArray[1]};
846}
847
848function pointToString(point) {
849 return `{row: ${point.row}, column: ${point.column}}`;
850}
851
852function initializeLanguageNodeClasses(language) {
853 const nodeTypeNamesById = binding.getNodeTypeNamesById(language);
854 const nodeFieldNamesById = binding.getNodeFieldNamesById(language);
855 const nodeTypeInfo = language.nodeTypeInfo || [];
856
857 const nodeSubclasses = [];
858 for (let id = 0, n = nodeTypeNamesById.length; id < n; id++) {
859 nodeSubclasses[id] = SyntaxNode;
860
861 const typeName = nodeTypeNamesById[id];
862 if (!typeName) continue;
863
864 const typeInfo = nodeTypeInfo.find(info => info.named && info.type === typeName);
865 if (!typeInfo) continue;
866
867 const fieldNames = [];
868 let classBody = '\n';
869 if (typeInfo.fields) {
870 for (const fieldName in typeInfo.fields) {
871 const fieldId = nodeFieldNamesById.indexOf(fieldName);
872 if (fieldId === -1) continue;
873 if (typeInfo.fields[fieldName].multiple) {
874 const getterName = camelCase(fieldName) + 'Nodes';
875 fieldNames.push(getterName);
876 classBody += `
877 get ${getterName}() {
878 marshalNode(this);
879 return unmarshalNodes(NodeMethods.childNodesForFieldId(this.tree, ${fieldId}), this.tree);
880 }
881 `.replace(/\s+/g, ' ') + '\n';
882 } else {
883 const getterName = camelCase(fieldName, false) + 'Node';
884 fieldNames.push(getterName);
885 classBody += `
886 get ${getterName}() {
887 marshalNode(this);
888 return unmarshalNode(NodeMethods.childNodeForFieldId(this.tree, ${fieldId}), this.tree);
889 }
890 `.replace(/\s+/g, ' ') + '\n';
891 }
892 }
893 }
894
895 const className = camelCase(typeName, true) + 'Node';
896 const nodeSubclass = eval(`class ${className} extends SyntaxNode {${classBody}}; ${className}`);
897 nodeSubclass.prototype.type = typeName;
898 nodeSubclass.prototype.fields = Object.freeze(fieldNames.sort())
899 nodeSubclasses[id] = nodeSubclass;
900 }
901
902 language.nodeSubclasses = nodeSubclasses
903}
904
905function camelCase(name, upperCase) {
906 name = name.replace(/_(\w)/g, (_match, letter) => letter.toUpperCase());
907 if (upperCase) name = name[0].toUpperCase() + name.slice(1);
908 return name;
909}
910
911module.exports = Parser;
912module.exports.Query = Query;
913module.exports.Tree = Tree;
914module.exports.SyntaxNode = SyntaxNode;
915module.exports.TreeCursor = TreeCursor;
916module.exports.LookaheadIterator = LookaheadIterator;