@bablr/agast-helpers 0.5.3 → 0.6.1
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/builders.js +69 -215
- package/lib/path.js +921 -14
- package/lib/print.js +91 -70
- package/lib/shorthand.js +11 -31
- package/lib/stream.js +201 -62
- package/lib/symbols.js +4 -11
- package/lib/template.js +83 -69
- package/lib/tree.js +390 -297
- package/package.json +6 -1
package/lib/path.js
CHANGED
|
@@ -1,26 +1,933 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
155
|
+
newBinding = {
|
|
156
|
+
reference,
|
|
157
|
+
node: btree.push(shiftedNodes, value),
|
|
158
|
+
};
|
|
159
|
+
} else {
|
|
160
|
+
newBinding = { reference, node: value };
|
|
161
|
+
}
|
|
10
162
|
|
|
11
|
-
|
|
163
|
+
properties[name] =
|
|
164
|
+
shift != null
|
|
165
|
+
? btree.replaceAt(-1, properties[name], newBinding)
|
|
166
|
+
: btree.push(properties[name], newBinding);
|
|
12
167
|
|
|
13
|
-
|
|
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
|
-
|
|
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
|
|
19
|
-
if (
|
|
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
|
-
|
|
434
|
+
if (parent && (!this.reference || ![ReferenceTag, ShiftTag].includes(this.reference.type))) {
|
|
435
|
+
throw new Error();
|
|
436
|
+
}
|
|
22
437
|
|
|
23
|
-
|
|
438
|
+
if (!Number.isFinite(this.depth)) throw new Error();
|
|
24
439
|
|
|
25
|
-
|
|
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
|
+
}
|