@bablr/agast-helpers 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/path.js CHANGED
@@ -1,26 +1,933 @@
1
- export const parsePath = (str) => {
2
- let name = str;
3
- const hasGap = name.endsWith('$');
1
+ import { WeakStackFrame } from '@bablr/weak-stack';
2
+ import * as btree from '@bablr/agast-helpers/btree';
3
+ import {
4
+ ReferenceTag,
5
+ ArrayInitializerTag,
6
+ EmbeddedNode,
7
+ DoctypeTag,
8
+ OpenNodeTag,
9
+ CloseNodeTag,
10
+ GapTag,
11
+ NullTag,
12
+ ShiftTag,
13
+ } from './symbols.js';
14
+ import {
15
+ buildArrayInitializerTag,
16
+ buildEmbeddedNode,
17
+ buildGapTag,
18
+ buildReferenceTag,
19
+ buildShiftTag,
20
+ } from './builders.js';
4
21
 
5
- if (hasGap) name = name.slice(0, -1);
22
+ export const getOpenTag = (node) => {
23
+ let tag = btree.getAt(0, node.children);
24
+ if (tag.type === NullTag || tag.type === GapTag) return null;
25
+ if (tag.type === DoctypeTag) {
26
+ tag = btree.getAt(1, node.children);
27
+ }
28
+ if (tag && tag.type !== OpenNodeTag) throw new Error();
29
+ return tag;
30
+ };
31
+
32
+ export const getCloseTag = (node) => {
33
+ const { children } = node;
34
+ const tag = btree.getAt(-1, children);
35
+ if (tag.type !== CloseNodeTag) return null;
36
+ return tag;
37
+ };
38
+
39
+ export const isNullNode = (node) => {
40
+ return node && node.type === null && btree.getAt(0, node.children).type === NullTag;
41
+ };
42
+
43
+ export const isFragmentNode = (node) => {
44
+ return node && node.type === null && getOpenTag(node)?.value.type === null;
45
+ };
46
+
47
+ export const isGapNode = (node) => {
48
+ return node && node.type === null && btree.getAt(0, node.children).type === GapTag;
49
+ };
50
+
51
+ const { hasOwn } = Object;
52
+ const { isArray } = Array;
53
+
54
+ export const referencesAreEqual = (a, b) => {
55
+ return (
56
+ a === b ||
57
+ (a.value.name === b.value.name &&
58
+ a.value.isArray === b.value.isArray &&
59
+ a.value.flags.hasGap === b.value.flags.hasGap &&
60
+ a.value.flags.expression === b.value.flags.expression)
61
+ );
62
+ };
63
+
64
+ export const getProperties = (ref, properties) => {
65
+ const { name, index, isArray } = ref.value;
66
+
67
+ if (name === '.') {
68
+ if (!hasOwn(properties, name)) {
69
+ return null;
70
+ }
71
+ }
72
+
73
+ if (isArray) {
74
+ return btree.getAt(index ?? -1, properties[name]);
75
+ } else {
76
+ return properties[name];
77
+ }
78
+ };
79
+
80
+ export const getPropertiesSimple = (ref, properties) => {
81
+ const { name, index, isArray } = ref.value;
82
+
83
+ if (!hasOwn(properties, name)) {
84
+ return null;
85
+ }
86
+
87
+ if (isArray) {
88
+ return properties[name][index == null ? properties[name].length - 1 : index];
89
+ } else {
90
+ return properties[name];
91
+ }
92
+ };
93
+
94
+ export const get = (ref, node) => {
95
+ const { flags } = ref.value;
96
+ const result = getProperties(ref, node.properties);
97
+
98
+ return flags.expression ? btree.getAt(-1, result.node) : result?.node;
99
+ };
100
+
101
+ export const getShifted = (shiftIndex, ref, node) => {
102
+ const { flags } = ref.value;
6
103
 
7
- const isArray = name.endsWith('[]');
104
+ const result = getProperties(ref, node.properties);
105
+ return flags.expression ? btree.getAt(shiftIndex ?? -1, result.node) : result?.node;
106
+ };
107
+
108
+ export const add = (node, reference, value, shift = null) => {
109
+ if (node == null || reference == null || value == null) throw new Error();
110
+
111
+ const { properties } = node;
112
+ const { name, isArray, flags } = reference.value;
113
+
114
+ if (node.type && name === '.') {
115
+ throw new Error('Only fragments can have . properties');
116
+ }
117
+
118
+ if (name == null) throw new Error();
119
+
120
+ const lastChild = btree.getAt(-1, node.children);
121
+
122
+ if (lastChild.type === ReferenceTag) {
123
+ if (!referencesAreEqual(lastChild, reference)) throw new Error();
124
+ } else if (lastChild.type !== ShiftTag) {
125
+ node.children = btree.push(node.children, shift == null ? reference : buildShiftTag(shift));
126
+ }
127
+
128
+ if (name === '#' || name === '@') {
129
+ node.children = btree.push(node.children, buildEmbeddedNode(value));
130
+ } else {
131
+ if (isArray) {
132
+ let isInitializer = Array.isArray(value);
133
+ let exists = !isInitializer && hasOwn(properties, name);
134
+
135
+ let existed = exists;
136
+ if (!existed) {
137
+ if (isInitializer && value.length)
138
+ throw new Error('Array value only allowed for initialization');
139
+
140
+ properties[name] = [];
141
+ node.children = btree.push(node.children, buildArrayInitializerTag(value));
142
+ exists = !isInitializer;
143
+ }
144
+
145
+ if (exists) {
146
+ if (!existed) {
147
+ if (btree.getAt(-1, node.children).type === ReferenceTag) throw new Error();
148
+ node.children = btree.push(node.children, reference);
149
+ }
150
+
151
+ let newBinding;
152
+ if (flags.expression) {
153
+ let shiftedNodes = shift != null ? btree.getAt(-1, properties[name])?.node : [];
8
154
 
9
- if (isArray) name = name.slice(0, -2);
155
+ newBinding = {
156
+ reference,
157
+ node: btree.push(shiftedNodes, value),
158
+ };
159
+ } else {
160
+ newBinding = { reference, node: value };
161
+ }
10
162
 
11
- if (!/^(\.|[a-zA-Z]+)$/.test(name)) throw new Error();
163
+ properties[name] =
164
+ shift != null
165
+ ? btree.replaceAt(-1, properties[name], newBinding)
166
+ : btree.push(properties[name], newBinding);
12
167
 
13
- const isRoot = name === '.';
168
+ node.children = btree.push(node.children, buildGapTag(value));
169
+ }
170
+ } else {
171
+ if (hasOwn(properties, name)) {
172
+ throw new Error();
173
+ }
14
174
 
15
- return { name, hasGap, isArray, isRoot };
175
+ if (flags.expression) {
176
+ let shiftedNodes = shift ? properties[name]?.node : [];
177
+ properties[name] = { reference, node: btree.push(shiftedNodes, value) };
178
+ } else {
179
+ properties[name] = { reference, node: value };
180
+ }
181
+ node.children = btree.push(node.children, buildGapTag(value));
182
+ }
183
+ }
16
184
  };
17
185
 
18
- export const printPath = (path) => {
19
- if (!path) return null;
186
+ export function* allTagPathsFor(range) {
187
+ if (range == null) return;
188
+
189
+ let startPath = range[0];
190
+ let endPath = range[1];
191
+ let path = startPath;
192
+
193
+ while (path) {
194
+ if (path.inner) {
195
+ path = new TagPath(path.innerPath, 0);
196
+ }
197
+
198
+ yield path;
199
+
200
+ if (
201
+ endPath &&
202
+ path.childrenIndex === endPath.childrenIndex &&
203
+ path.path.node === endPath.path.node
204
+ ) {
205
+ return;
206
+ }
207
+
208
+ path = path.next;
209
+ }
210
+ }
211
+
212
+ export function* allTagsFor(range) {
213
+ for (const path of allTagPathsFor(range)) {
214
+ yield path.tag;
215
+ }
216
+ }
217
+
218
+ export function* ownTagPathsFor(range) {
219
+ if (!isArray(range)) throw new Error();
220
+
221
+ const startPath = range[0];
222
+ const endPath = range[1];
223
+
224
+ let path = startPath;
225
+
226
+ if (startPath.outer !== endPath.outer) throw new Error();
227
+
228
+ const { children } = startPath.outer;
229
+
230
+ for (let i = startPath.childrenIndex; i < endPath.childrenIndex; i++) {
231
+ yield children[i];
232
+ }
233
+ }
234
+
235
+ export class PathResolver {
236
+ constructor() {
237
+ this.childrenIndex = -1;
238
+ this.counters = {};
239
+ this.reference = null;
240
+ }
241
+
242
+ advance(tag) {
243
+ this.childrenIndex++;
244
+
245
+ const { counters } = this;
246
+ if (tag.type === ReferenceTag) {
247
+ const { isArray, name, flags } = tag.value;
248
+
249
+ let resolvedReference = tag;
250
+
251
+ this.reference = tag;
252
+
253
+ if (isArray) {
254
+ if (hasOwn(counters, name)) {
255
+ const counter = ++counters[name];
256
+
257
+ resolvedReference = buildReferenceTag(name, isArray, flags, counter);
258
+ }
259
+ } else if (name !== '@' && name !== '#') {
260
+ if (hasOwn(counters, name)) throw new Error();
261
+
262
+ counters[name] = true;
263
+ }
264
+
265
+ return resolvedReference;
266
+ } else if (tag.type === ArrayInitializerTag) {
267
+ counters[this.reference.value.name] = -1;
268
+ return this.reference.value.name;
269
+ }
270
+ }
271
+ }
272
+
273
+ Object.freeze(PathResolver.prototype);
274
+
275
+ const findRight = (arr, predicate) => {
276
+ for (let i = arr.length - 1; i >= 0; i--) {
277
+ const value = arr[i];
278
+ if (predicate(value)) return value;
279
+ }
280
+ return null;
281
+ };
282
+
283
+ const skipLevels = 3;
284
+ const skipShiftExponentGrowth = 4;
285
+ const skipAmounts = new Array(skipLevels)
286
+ .fill(null)
287
+ .map((_, i) => 2 >> (i * skipShiftExponentGrowth));
288
+ const skipsByFrame = new WeakMap();
289
+
290
+ const buildSkips = (frame) => {
291
+ let skipIdx = 0;
292
+ let skipAmount = skipAmounts[skipIdx];
293
+ let skips;
294
+ while ((frame.depth & skipAmount) === skipAmount) {
295
+ if (!skips) {
296
+ skips = [];
297
+ skipsByFrame.set(frame, skips);
298
+ }
299
+
300
+ skips[skipIdx] = frame.at(frame.depth - skipAmount);
301
+
302
+ skipIdx++;
303
+ skipAmount = skipAmounts[skipIdx];
304
+ }
305
+ };
306
+
307
+ const skipToDepth = (depth, frame) => {
308
+ let parent = frame;
309
+
310
+ if (depth > frame.depth) throw new Error();
311
+
312
+ let d = frame.depth;
313
+ for (; d > depth; ) {
314
+ const skips = skipsByFrame.get(frame);
315
+ parent = (skips && findRight(skips, (skip) => d - skip > depth)) || parent.parent;
316
+ d = parent.depth;
317
+ }
318
+ return parent;
319
+ };
320
+
321
+ const buildBindings = (node) => {
322
+ const { children, properties } = node;
323
+ const referenceIndexes = new Array(children.length);
324
+ const childrenIndexes = Object.fromEntries(Object.keys(properties).map((key) => [key, null]));
325
+
326
+ const resolver = new PathResolver();
327
+
328
+ for (const tag of btree.traverse(children)) {
329
+ resolver.advance(tag);
330
+ const i = resolver.childrenIndex;
331
+
332
+ if (tag.type === ReferenceTag) {
333
+ const { name, isArray, index } = tag.value;
334
+
335
+ if (!name) throw new Error();
336
+ // if (name === '.') throw new Error();
337
+
338
+ const counter = isArray
339
+ ? hasOwn(resolver.counters, name)
340
+ ? resolver.counters[name]
341
+ : null
342
+ : null;
343
+
344
+ if (index != null && index !== counter) throw new Error();
345
+
346
+ referenceIndexes[i] = counter;
347
+
348
+ if (isArray) {
349
+ if (childrenIndexes[name] === null || !hasOwn(childrenIndexes, name)) {
350
+ childrenIndexes[name] = [];
351
+ } else if (counter >= 0) {
352
+ childrenIndexes[name][counter] = i;
353
+ } else {
354
+ throw new Error();
355
+ }
356
+ } else {
357
+ if (name !== '#' && name !== '@') {
358
+ childrenIndexes[name] = i;
359
+ }
360
+ }
361
+ } else {
362
+ referenceIndexes[i] = null;
363
+ }
364
+ }
365
+
366
+ return { referenceIndexes, childrenIndexes };
367
+ };
368
+
369
+ const nodeStates = new WeakMap();
370
+
371
+ // TODO remove this; it is a very bad API to have to support!!
372
+ export const updatePath = (path, tag) => {
373
+ const { node, childrenIndexes, referenceIndexes } = path;
374
+ const i = btree.getSum(node.children) - 1;
375
+
376
+ if (tag.type === ReferenceTag) {
377
+ const { name, isArray, index: literalArrayIndex } = tag.value;
378
+
379
+ const arrayIndex = isArray
380
+ ? hasOwn(node.properties, name)
381
+ ? btree.getSum(node.properties[name])
382
+ : -1
383
+ : null;
384
+
385
+ if (literalArrayIndex != null && literalArrayIndex !== arrayIndex) throw new Error();
386
+
387
+ referenceIndexes[i] = arrayIndex;
388
+
389
+ if (isArray) {
390
+ if (!hasOwn(childrenIndexes, name) || childrenIndexes[name] === null) {
391
+ childrenIndexes[name] = [];
392
+ } else {
393
+ if (arrayIndex >= 0) {
394
+ childrenIndexes[name][arrayIndex] = i;
395
+ }
396
+ }
397
+ } else {
398
+ if (name !== '#' && name !== '@') {
399
+ childrenIndexes[name] = i;
400
+ }
401
+ }
402
+ } else if (tag.type === ArrayInitializerTag) {
403
+ referenceIndexes[i] = -1;
404
+ } else {
405
+ referenceIndexes[i] = null;
406
+ }
407
+ };
408
+
409
+ export const Path = class AgastPath extends WeakStackFrame {
410
+ static from(node) {
411
+ return this.create(node);
412
+ }
413
+
414
+ constructor(parent, node, referenceIndex = null) {
415
+ super(parent);
416
+
417
+ if (!(hasOwn(node, 'type') && hasOwn(node, 'language'))) throw new Error();
418
+
419
+ if (parent && referenceIndex == null) throw new Error();
420
+ if (!node) throw new Error();
421
+ if (isArray(node)) throw new Error();
422
+
423
+ this.node = node;
424
+ this.referenceIndex = referenceIndex; // in the parent
425
+
426
+ if (
427
+ referenceIndex != null &&
428
+ ![ReferenceTag, ShiftTag].includes(btree.getAt(referenceIndex, parent.node.children).type)
429
+ )
430
+ throw new Error();
431
+
432
+ nodeStates.set(node, buildBindings(node));
20
433
 
21
- const { isArray, isRoot, hasGap, name } = path;
434
+ if (parent && (!this.reference || ![ReferenceTag, ShiftTag].includes(this.reference.type))) {
435
+ throw new Error();
436
+ }
22
437
 
23
- if (isRoot && name !== '.') throw new Error();
438
+ if (!Number.isFinite(this.depth)) throw new Error();
24
439
 
25
- return `${name}${isArray ? '[]' : ''}${hasGap ? '$' : ''}`;
440
+ buildSkips(this);
441
+ }
442
+
443
+ get referenceIndexes() {
444
+ return nodeStates.get(this.node).referenceIndexes;
445
+ }
446
+
447
+ get childrenIndexes() {
448
+ return nodeStates.get(this.node).childrenIndexes;
449
+ }
450
+
451
+ get reference() {
452
+ return this.outer && btree.getAt(this.referenceIndex, this.outer.children);
453
+ }
454
+
455
+ get referencePath() {
456
+ return this.outer && new TagPath(this.parent, this.referenceIndex);
457
+ }
458
+
459
+ get gap() {
460
+ return this.outer && btree.getAt(this.referenceIndex + 1, this.outer.children);
461
+ }
462
+
463
+ get gapPath() {
464
+ return this.outer && new TagPath(this.parent, this.referenceIndex + 1);
465
+ }
466
+
467
+ get outer() {
468
+ return this.parent?.node;
469
+ }
470
+
471
+ get(reference, shiftIndex) {
472
+ let node = getShifted(shiftIndex, reference, this.node);
473
+
474
+ let shiftOffset = (shiftIndex ?? 0) * 2;
475
+
476
+ return (
477
+ node && this.push(node, getPropertiesSimple(reference, this.childrenIndexes) + shiftOffset)
478
+ );
479
+ }
480
+
481
+ at(depth) {
482
+ return skipToDepth(depth, this);
483
+ }
484
+ };
485
+
486
+ export const tagPathsAreEqual = (a, b) => {
487
+ if (a == null || b == null) return b == a;
488
+ return a.path.node === b.path.node && a.childrenIndex === b.childrenIndex;
26
489
  };
490
+
491
+ export class TagPath {
492
+ constructor(path, childrenIndex) {
493
+ if (path == null || childrenIndex == null) throw new Error();
494
+
495
+ this.path = path;
496
+ this.childrenIndex = childrenIndex;
497
+
498
+ if (this.tag == null) throw new Error();
499
+ }
500
+
501
+ static from(path, childrenIndex) {
502
+ let size = btree.getSum(path.node.children);
503
+ let index = childrenIndex < 0 ? size + childrenIndex : childrenIndex;
504
+
505
+ return index >= 0 && index < size ? new TagPath(path, index) : null;
506
+ }
507
+
508
+ get tag() {
509
+ return this.child;
510
+ }
511
+
512
+ get node() {
513
+ return this.path.node;
514
+ }
515
+
516
+ get child() {
517
+ return btree.getAt(this.childrenIndex, this.path.node.children);
518
+ }
519
+
520
+ get nextSibling() {
521
+ const { path, childrenIndex } = this;
522
+
523
+ const child =
524
+ childrenIndex + 1 >= btree.getSum(path.node.children)
525
+ ? null
526
+ : btree.getAt(childrenIndex + 1, path.node.children);
527
+
528
+ return child && new TagPath(path, childrenIndex + 1);
529
+ }
530
+
531
+ get next() {
532
+ let { path, childrenIndex } = this;
533
+
534
+ let leaving = false;
535
+
536
+ for (;;) {
537
+ let prevTag = btree.getAt(childrenIndex - 1, path.node.children);
538
+ let tag = btree.getAt(childrenIndex, path.node.children);
539
+ let isInitialTag = path.node === this.path.node && childrenIndex === this.childrenIndex;
540
+ let wasLeaving = leaving;
541
+ leaving = false;
542
+
543
+ if (!tag) return null;
544
+
545
+ // done
546
+ if (
547
+ !isInitialTag &&
548
+ tag.type !== EmbeddedNode &&
549
+ (tag.type !== GapTag || isGapNode(path.node) || prevTag.type === ShiftTag)
550
+ ) {
551
+ return new TagPath(path, childrenIndex);
552
+ }
553
+
554
+ // in
555
+ if (tag.type === EmbeddedNode && !wasLeaving) {
556
+ path = path.push(tag.value, childrenIndex - 1);
557
+ childrenIndex = 0;
558
+ continue;
559
+ }
560
+
561
+ // in
562
+ if (tag.type === GapTag && !wasLeaving && !isGapNode(path.node)) {
563
+ let refIndex = childrenIndex - 1;
564
+ let refTag;
565
+ let prevTag = btree.getAt(childrenIndex - 1, path.node.children);
566
+ let nextTag = btree.getAt(childrenIndex + 1, path.node.children);
567
+
568
+ if (
569
+ path.parent &&
570
+ btree.getAt(path.referenceIndex, path.outer.children)?.type === ShiftTag &&
571
+ childrenIndex === 2
572
+ ) {
573
+ childrenIndex = path.referenceIndex + 1;
574
+ path = path.parent;
575
+ leaving = true;
576
+ continue;
577
+ }
578
+
579
+ if (prevTag.type === ReferenceTag) {
580
+ refTag = prevTag;
581
+
582
+ if (nextTag && nextTag.type === ShiftTag) {
583
+ const shifts = getProperties(refTag, path.node.properties).node;
584
+
585
+ if (!Array.isArray(shifts)) throw new Error();
586
+
587
+ const { name, isArray, flags } = refTag.value;
588
+ let resolvedReference = refTag;
589
+ if (isArray) {
590
+ let index = path.referenceIndexes[refIndex];
591
+ resolvedReference =
592
+ index === -1 ? null : buildReferenceTag(name, index != null, flags, index);
593
+ }
594
+
595
+ path = path.get(resolvedReference, 0);
596
+ childrenIndex = 0;
597
+
598
+ if (!path) {
599
+ return null;
600
+ }
601
+ continue;
602
+ } else {
603
+ if (
604
+ !['#', '@'].includes(refTag.value.name) &&
605
+ (!refTag.value.isArray || path.referenceIndexes[refIndex] != null)
606
+ ) {
607
+ const { name, isArray, flags } = refTag.value;
608
+ let resolvedReference = refTag;
609
+ if (isArray) {
610
+ let index = path.referenceIndexes[refIndex];
611
+ resolvedReference =
612
+ index === -1 ? null : buildReferenceTag(name, index != null, flags, index);
613
+ }
614
+
615
+ if (resolvedReference) {
616
+ path = path.get(resolvedReference);
617
+ childrenIndex = 0;
618
+
619
+ if (!path) {
620
+ return null;
621
+ }
622
+ continue;
623
+ }
624
+ }
625
+ }
626
+ } else if (prevTag.type === ShiftTag) {
627
+ let refIndex = childrenIndex - prevTag.value.index * 2 - 1;
628
+ let refTag = btree.getAt(refIndex, path.node.children);
629
+
630
+ const { name, isArray, flags } = refTag.value;
631
+ let resolvedReference = refTag;
632
+ if (isArray) {
633
+ let index = path.referenceIndexes[refIndex];
634
+ resolvedReference =
635
+ index === -1 ? null : buildReferenceTag(name, index != null, flags, index);
636
+ }
637
+
638
+ if (resolvedReference) {
639
+ path = path.get(resolvedReference);
640
+ // this was introducing errors
641
+ // caused us to return to a point before we left
642
+ path.referenceIndex = childrenIndex;
643
+ childrenIndex = 3;
644
+ continue;
645
+ }
646
+ } else {
647
+ throw new Error();
648
+ }
649
+ }
650
+
651
+ // shift
652
+ if (tag.type === ShiftTag) {
653
+ let refIndex = childrenIndex - tag.value.index * 2;
654
+ let refTag = btree.getAt(refIndex, path.node.children);
655
+
656
+ const { name, isArray, flags } = refTag.value;
657
+ let resolvedReference = null;
658
+ if (isArray) {
659
+ let index = path.referenceIndexes[refIndex];
660
+ resolvedReference =
661
+ index === -1 ? null : buildReferenceTag(name, index != null, flags, index);
662
+ } else {
663
+ resolvedReference = refTag;
664
+ }
665
+
666
+ if (resolvedReference) {
667
+ path = path.get(resolvedReference, tag.value.index);
668
+ childrenIndex = 0;
669
+ continue;
670
+ }
671
+
672
+ // go backwards through any other shifts until we're done
673
+ // path = path.parent;
674
+ // childrenIndex = 0;
675
+ // continue;
676
+ }
677
+
678
+ // over
679
+ if (path.node && childrenIndex + 1 < btree.getSum(path.node.children)) {
680
+ childrenIndex++;
681
+ continue;
682
+ }
683
+
684
+ // out
685
+ if (path.referenceIndex != null) {
686
+ do {
687
+ if (btree.getAt(path.referenceIndex + 2, path.outer.children)?.type === ShiftTag) {
688
+ childrenIndex =
689
+ btree.getSum(path.outer.children) > path.referenceIndex + 2
690
+ ? path.referenceIndex + 2
691
+ : null;
692
+ } else {
693
+ childrenIndex = path.referenceIndex + 1;
694
+ }
695
+
696
+ path = path.parent;
697
+ leaving = true;
698
+ } while (childrenIndex == null);
699
+
700
+ leaving = true;
701
+ continue;
702
+ }
703
+
704
+ return null;
705
+ }
706
+ }
707
+
708
+ get nextUnshifted() {
709
+ let { path, childrenIndex } = this;
710
+
711
+ let leaving = false;
712
+
713
+ for (;;) {
714
+ let tag = btree.getAt(childrenIndex, path.node.children);
715
+ let isInitialTag = path.node === this.path.node && childrenIndex === this.childrenIndex;
716
+ let wasLeaving = leaving;
717
+ leaving = false;
718
+
719
+ if (!tag) return null;
720
+
721
+ // done
722
+ if (
723
+ !isInitialTag &&
724
+ tag.type !== EmbeddedNode &&
725
+ tag.type !== ShiftTag &&
726
+ (tag.type !== GapTag || isGapNode(path.node))
727
+ ) {
728
+ return new TagPath(path, childrenIndex);
729
+ }
730
+
731
+ // in
732
+ if (tag.type === EmbeddedNode && !wasLeaving) {
733
+ path = path.push(tag.value, childrenIndex - 1);
734
+ childrenIndex = 0;
735
+ continue;
736
+ }
737
+
738
+ // in
739
+ if (tag.type === GapTag && !wasLeaving && !isGapNode(path.node)) {
740
+ let refIndex = childrenIndex - 1;
741
+ let refTag;
742
+ let prevTag = btree.getAt(childrenIndex - 1, path.node.children);
743
+
744
+ if (prevTag.type === ShiftTag) {
745
+ // continue
746
+ } else if (prevTag.type === ReferenceTag) {
747
+ refTag = prevTag;
748
+
749
+ if (
750
+ !['#', '@'].includes(refTag.value.name) &&
751
+ (!refTag.value.isArray || path.referenceIndexes[refIndex] != null)
752
+ ) {
753
+ const { name, isArray, flags } = refTag.value;
754
+ let resolvedReference = refTag;
755
+ if (isArray) {
756
+ let index = path.referenceIndexes[refIndex];
757
+ resolvedReference =
758
+ index === -1 ? null : buildReferenceTag(name, index != null, flags, index);
759
+ }
760
+
761
+ if (resolvedReference) {
762
+ path = path.get(resolvedReference);
763
+ childrenIndex = 0;
764
+
765
+ if (!path) {
766
+ return null;
767
+ }
768
+ continue;
769
+ }
770
+ }
771
+ } else {
772
+ throw new Error();
773
+ }
774
+ }
775
+
776
+ // over
777
+ if (path.node && childrenIndex + 1 < btree.getSum(path.node.children)) {
778
+ childrenIndex++;
779
+ continue;
780
+ }
781
+
782
+ // out
783
+ if (path.referenceIndex != null) {
784
+ do {
785
+ childrenIndex = path.referenceIndex + 1;
786
+
787
+ path = path.parent;
788
+ leaving = true;
789
+ } while (childrenIndex == null);
790
+
791
+ leaving = true;
792
+ continue;
793
+ }
794
+
795
+ return null;
796
+ }
797
+ }
798
+
799
+ get previousSibling() {
800
+ const { path, childrenIndex } = this;
801
+
802
+ const child = childrenIndex - 1 < 0 ? null : btree.getAt(childrenIndex - 1, path.node.children);
803
+
804
+ return child && new TagPath(path, childrenIndex - 1);
805
+ }
806
+
807
+ get previous() {
808
+ throw new Error('not implemented');
809
+ }
810
+
811
+ get previousUnshifted() {
812
+ let { path, childrenIndex } = this;
813
+
814
+ let leaving = false;
815
+
816
+ for (;;) {
817
+ let tag = btree.getAt(childrenIndex, path.node.children);
818
+ let isInitialTag = path.node === this.path.node && childrenIndex === this.childrenIndex;
819
+ let wasLeaving = leaving;
820
+ leaving = false;
821
+
822
+ if (!tag) return null;
823
+
824
+ // done
825
+ if (
826
+ !isInitialTag &&
827
+ tag.type !== EmbeddedNode &&
828
+ tag.type !== ShiftTag &&
829
+ (tag.type !== GapTag || isGapNode(path.node))
830
+ ) {
831
+ return new TagPath(path, childrenIndex);
832
+ }
833
+
834
+ // in
835
+ if (tag.type === EmbeddedNode && !wasLeaving) {
836
+ path = path.push(tag.value, childrenIndex - 1);
837
+ childrenIndex = btree.getSum(tag.value.children) - 1;
838
+ continue;
839
+ }
840
+
841
+ // in
842
+ if (tag.type === GapTag && !wasLeaving && !isGapNode(path.node)) {
843
+ let refIndex = childrenIndex - 1;
844
+ let refTag;
845
+ let prevTag = btree.getAt(childrenIndex - 1, path.node.children);
846
+
847
+ if (prevTag.type === ShiftTag) {
848
+ // continue
849
+ } else if (prevTag.type === ReferenceTag) {
850
+ refTag = prevTag;
851
+
852
+ if (
853
+ !['#', '@'].includes(refTag.value.name) &&
854
+ (!refTag.value.isArray || path.referenceIndexes[refIndex] != null)
855
+ ) {
856
+ const { name, isArray, flags } = refTag.value;
857
+ let resolvedReference = refTag;
858
+ if (isArray) {
859
+ let index = path.referenceIndexes[refIndex];
860
+ resolvedReference =
861
+ index === -1 ? null : buildReferenceTag(name, index != null, flags, index);
862
+ }
863
+
864
+ if (resolvedReference) {
865
+ path = path.get(resolvedReference);
866
+ childrenIndex = 0;
867
+
868
+ if (!path) {
869
+ return null;
870
+ }
871
+ continue;
872
+ }
873
+ }
874
+ } else {
875
+ throw new Error();
876
+ }
877
+ }
878
+
879
+ // over
880
+ if (path.node && childrenIndex + 1 < btree.getSum(path.node.children)) {
881
+ childrenIndex--;
882
+ continue;
883
+ }
884
+
885
+ // out
886
+ if (path.referenceIndex != null) {
887
+ do {
888
+ childrenIndex = path.referenceIndex;
889
+
890
+ path = path.parent;
891
+ leaving = true;
892
+ } while (childrenIndex == null);
893
+
894
+ leaving = true;
895
+ continue;
896
+ }
897
+
898
+ return null;
899
+ }
900
+ }
901
+
902
+ get inner() {
903
+ return this.innerPath?.node;
904
+ }
905
+
906
+ get innerPath() {
907
+ let { tag, previousSibling: ref } = this;
908
+
909
+ if (tag.type !== GapTag || isGapNode(this.node) || ref.tag.type === ShiftTag) {
910
+ return null;
911
+ }
912
+
913
+ if (ref.tag.type !== ReferenceTag) throw new Error();
914
+
915
+ let resolvedRef = ref.tag;
916
+
917
+ if (ref.tag.value.isArray) {
918
+ const { name, flags, isArray } = ref.tag.value;
919
+ resolvedRef = buildReferenceTag(
920
+ name,
921
+ isArray,
922
+ flags,
923
+ ref.path.referenceIndexes[ref.childrenIndex],
924
+ );
925
+ }
926
+
927
+ return this.path.get(resolvedRef);
928
+ }
929
+
930
+ equalTo(tagPath) {
931
+ return this.node === tagPath.node && this.childrenIndex === tagPath.childrenIndex;
932
+ }
933
+ }