@bablr/agast-helpers 0.9.0 → 0.10.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
@@ -3,27 +3,193 @@ import * as BTree from '@bablr/agast-helpers/btree';
3
3
  import * as Tags from './tags.js';
4
4
  import {
5
5
  ReferenceTag,
6
- InitializerTag,
7
- DoctypeTag,
8
6
  CloseNodeTag,
9
7
  GapTag,
10
8
  NullTag,
11
9
  ShiftTag,
12
10
  BindingTag,
13
- PropertyWrapper,
14
11
  Property,
12
+ OpenNodeTag,
13
+ AttributeDefinition,
14
+ LiteralTag,
15
+ TreeNode,
16
+ NullNode,
17
+ GapNode,
18
+ Document,
15
19
  } from './symbols.js';
16
- import { isPlainObject, isString } from './object.js';
17
- import { buildBinding, buildChild, buildProperty, buildPropertyWrapper } from './builders.js';
18
- import { buildReferenceTag, finalizeNode } from './tree.js';
20
+ import {
21
+ immSet,
22
+ isPlainObject,
23
+ isString,
24
+ has as objHas,
25
+ get as objGet,
26
+ isObject,
27
+ isSymbol,
28
+ } from './object.js';
29
+ import {
30
+ buildReferenceTag,
31
+ buildChild,
32
+ buildProperty,
33
+ buildGapTag,
34
+ buildNullTag,
35
+ buildOpenNodeTag,
36
+ buildShift,
37
+ buildPropertyTag,
38
+ } from './builders.js';
39
+
40
+ export const incrementShift = (shift) => {
41
+ if (!shift) return buildShift(1, 3);
42
+
43
+ return buildShift(shift.index + 1, shift.height + 1);
44
+ };
45
+
46
+ export const finalizeNode = (node) => {
47
+ freeze(node);
48
+ freeze(node.value);
19
49
 
20
- export const wrapperIsFull = (wrapper) => {
21
- let { tags } = wrapper.value;
50
+ return node;
51
+ };
22
52
 
23
- if (tags[1]?.type === InitializerTag) {
24
- return true;
53
+ export const buildNode = (tags) => {
54
+ if (isArray(tags) && !Number.isFinite(tags[0])) throw new Error();
55
+ if (!isArray(tags) && !isSymbol(tags.type)) throw new Error();
56
+ let tags_ = isArray(tags) ? tags : Tags.fromValues([tags]);
57
+
58
+ if (!Number.isFinite(tags_[0])) throw new Error();
59
+ if (!isArray(tags_[1]) || !tags_[1].length) throw new Error();
60
+
61
+ let openTag = tags_[1][0];
62
+ if (![GapTag, NullTag, OpenNodeTag].includes(openTag.type)) throw new Error();
63
+ // let tags = openTag ? Tags.fromValues([openTag]) : Tags.fromValues([]);
64
+
65
+ if (openTag.type === GapTag) {
66
+ return finalizeNode({
67
+ type: GapNode,
68
+ value: {
69
+ tags: Tags.fromValues([openTag]),
70
+ },
71
+ });
72
+ } else if (openTag.type === NullTag) {
73
+ return finalizeNode({
74
+ type: NullNode,
75
+ value: { tags: Tags.fromValues([openTag]) },
76
+ });
77
+ } else if (openTag.type === OpenNodeTag) {
78
+ return finalizeNode({
79
+ type: TreeNode,
80
+ value: {
81
+ flags: openTag.value.flags,
82
+ type: openTag.value.type,
83
+ name: openTag.value.name,
84
+ attributes: openTag.value.attributes,
85
+ tags: tags_,
86
+ children: tags_[1][1] || Tags.fromValues([]),
87
+ bounds: buildBoundsFromTags(tags_),
88
+ },
89
+ });
25
90
  } else {
26
- return tags.length === 3;
91
+ throw new Error();
92
+ }
93
+ };
94
+
95
+ export const buildBoundsFromTags = (tags) => {
96
+ let gapProperty = buildProperty([buildReferenceTag(), [], buildNode(buildGapTag())]);
97
+ let bounds = [BTree.fromValues([gapProperty]), BTree.fromValues([gapProperty])];
98
+ let firstProperty, lastProperty;
99
+
100
+ if (Tags.getAt(-1, tags)?.type !== CloseNodeTag) {
101
+ return freeze(bounds);
102
+ }
103
+
104
+ for (let i = 1; i < Tags.getSize(tags); i++) {
105
+ let tag = Tags.getAt(i, tags);
106
+
107
+ if (tag.type === Property && !isNullNode(tag.value.node)) {
108
+ firstProperty = tag;
109
+ break;
110
+ }
111
+ }
112
+
113
+ for (let i = Tags.getSize(tags) - 2; i >= 0; i--) {
114
+ let tag = Tags.getAt(i, tags);
115
+
116
+ if (tag.type === Property && !isNullNode(tag.value.node)) {
117
+ lastProperty = tag;
118
+ break;
119
+ }
120
+ }
121
+
122
+ if (firstProperty && firstProperty.value.node.type === TreeNode) {
123
+ let openStack = firstProperty.value.node.value.bounds[0];
124
+
125
+ openStack = BTree.pop(openStack);
126
+ openStack = BTree.push(openStack, firstProperty);
127
+ openStack = BTree.concat(openStack, BTree.pop(bounds[0]));
128
+ openStack = BTree.push(
129
+ openStack,
130
+ buildPropertyTag([buildReferenceTag(), [], buildNode(buildGapTag())]),
131
+ );
132
+
133
+ bounds[0] = openStack;
134
+ }
135
+
136
+ let closeStack = bounds[1];
137
+
138
+ if (lastProperty && lastProperty.value.type === TreeNode) {
139
+ if (!lastProperty.value.node) throw new Error();
140
+
141
+ closeStack = lastProperty.value.node.value.bounds[1];
142
+
143
+ closeStack = closeStack
144
+ ? BTree.replaceAt(-1, closeStack, lastProperty)
145
+ : BTree.fromValues([lastProperty]);
146
+
147
+ closeStack = BTree.push(
148
+ closeStack,
149
+ buildPropertyTag([buildReferenceTag(), [], buildNode(buildGapTag())]),
150
+ );
151
+ bounds[1] = closeStack;
152
+ }
153
+
154
+ return freeze(bounds);
155
+ };
156
+
157
+ export const isNode = (value) => {
158
+ return [TreeNode, NullNode, GapNode].includes(value?.type);
159
+ };
160
+
161
+ export const endsNode = (tag) => {
162
+ return (
163
+ [CloseNodeTag, NullTag, GapTag].includes(tag.type) ||
164
+ (tag.type === OpenNodeTag && tag.value.selfClosing)
165
+ );
166
+ };
167
+
168
+ export const propertyIsFull = (tag) => {
169
+ if (tag.type !== Property) return true;
170
+
171
+ let { tags } = tag.value;
172
+
173
+ return tags.length === 3;
174
+ };
175
+
176
+ export const nodeIsComplete = (node) => {
177
+ let sigilTag = getOpenTag(node);
178
+ return sigilTag.type !== OpenNodeTag || sigilTag.value.selfClosing ? true : !!getCloseTag(node);
179
+ };
180
+
181
+ export const getTags = (node) => {
182
+ switch (node.type) {
183
+ case TreeNode:
184
+ return node.value.tags;
185
+ case NullNode:
186
+ return node.value.tags;
187
+ case GapNode:
188
+ return node.value.tags;
189
+ case Document:
190
+ return getTags(node.value.tree);
191
+ default:
192
+ throw new Error();
27
193
  }
28
194
  };
29
195
 
@@ -31,11 +197,33 @@ export const offsetForTag = (tag) => {
31
197
  switch (tag.type) {
32
198
  case ReferenceTag:
33
199
  case ShiftTag:
34
- return 0;
200
+ return [0];
35
201
  case BindingTag:
36
- case InitializerTag:
37
- return 1;
202
+ return [1, -1];
203
+ default:
204
+ return [];
205
+ }
206
+ };
207
+
208
+ export const isNodeTag = (tag) => {
209
+ switch (tag.type) {
210
+ case TreeNode:
211
+ case NullNode:
212
+ case GapNode:
213
+ return true;
214
+ default:
215
+ return false;
216
+ }
217
+ };
218
+
219
+ export const arraysEqual = (a, b) => {
220
+ if (a.length !== b.length) return false;
221
+
222
+ for (let i = 0; i < a.length; i++) {
223
+ if (a[i] !== b[i]) return false;
38
224
  }
225
+
226
+ return true;
39
227
  };
40
228
 
41
229
  export const buildPathSegment = (name, index = null, shiftIndex = null) => {
@@ -52,63 +240,72 @@ export const buildFullPathSegment = (type, name, index = null, shiftIndex = null
52
240
  return { type, name, index, shiftIndex };
53
241
  };
54
242
 
55
- export const getRootProperty = (node, index = 0) => {
56
- if (node == null || !isFragmentNode(node)) {
243
+ export const getRootProperty = (node) => {
244
+ if (node == null || !isCover(node)) {
57
245
  return null;
58
246
  }
59
- let idx = getPropertyTagsIndex(node, '.', null, index);
247
+ let idx = getPropertyTagsIndex(node, '_', null);
60
248
 
61
249
  if (idx == null) return null;
62
250
 
63
- let tag = Tags.getAt(idx, node.tags);
251
+ let tag = Tags.getAt(idx, getTags(node));
64
252
 
65
- if (tag.type !== PropertyWrapper) throw new Error();
66
- return tag.value.property;
253
+ if (tag.type !== Property) throw new Error();
254
+ return tag;
67
255
  };
68
256
 
69
- export const getRoot = (node, index = 0) => {
70
- if (!isFragmentNode(node)) {
71
- return node;
257
+ // TODO prevent infinite recursion
258
+ export const getRoot = (node, depth = Infinity) => {
259
+ let node_ = node;
260
+ let i = 0;
261
+
262
+ while (isCover(node_) && i < depth) {
263
+ node_ = getRootProperty(node_)?.value.node;
264
+ i++;
72
265
  }
73
266
 
74
- return getRootProperty(node, index)?.node;
267
+ return node_;
75
268
  };
76
269
 
77
- export const getRootArray = (node) => {
78
- let arr = [];
79
- for (let i = 0; ; i++) {
80
- let root = getRoot(node, i);
81
- if (root) {
82
- arr.push(root);
83
- } else {
84
- break;
85
- }
86
- }
87
- return arr;
270
+ export const getSigilTag = (node) => {
271
+ return Tags.getValues(getTags(node))[0];
88
272
  };
89
273
 
90
274
  export const getOpenTag = (node) => {
91
- return Tags.getValues(node.tags)[0];
275
+ let tag = Tags.getValues(getTags(node))[0];
276
+ return tag.type === OpenNodeTag ? tag : null;
92
277
  };
93
278
 
94
279
  export const getCloseTag = (node) => {
95
- return Tags.getValues(node.tags)[2];
280
+ return Tags.getValues(getTags(node))[2];
96
281
  };
97
282
 
98
283
  export const isNullNode = (node) => {
99
- return node && node.type === null && Tags.getAt(0, node.tags).type === NullTag;
284
+ return node && Tags.getAt(0, getTags(node)).type === NullTag;
285
+ };
286
+
287
+ export const isCover = (node) => {
288
+ return node?.type === TreeNode && node.value.type === Symbol.for('_');
289
+ };
290
+
291
+ export const getChildren = (node) => {
292
+ return node?.type === TreeNode ? node.value.children : [];
100
293
  };
101
294
 
102
- export const isFragmentNode = (node) => {
103
- return node && node.type === null && getOpenTag(node)?.value?.type === null;
295
+ export const isFragment = (node) => {
296
+ return node?.type === TreeNode && [Symbol.for('__'), Symbol.for('_')].includes(node.value.type);
297
+ };
298
+
299
+ export const isMultiFragment = (node) => {
300
+ return node?.type === TreeNode && node.value.type === Symbol.for('__');
104
301
  };
105
302
 
106
303
  export const isGapNode = (node) => {
107
- return node && node.type === null && Tags.getAt(0, node.tags).type === GapTag;
304
+ return node && Tags.getAt(0, getTags(node)).type === GapTag;
108
305
  };
109
306
 
110
307
  export const isStubNode = (node) => {
111
- return node && node.type === null && [GapTag, NullTag].includes(Tags.getAt(0, node.tags).type);
308
+ return node && [GapTag, NullTag].includes(Tags.getAt(0, getTags(node)).type);
112
309
  };
113
310
 
114
311
  export const isStubTag = (tag) => {
@@ -116,19 +313,19 @@ export const isStubTag = (tag) => {
116
313
  };
117
314
 
118
315
  export const getChildPropertyIndex = (agAstNode, tagsIndex) => {
119
- let child = Tags.getAt(tagsIndex, agAstNode.tags);
316
+ let child = Tags.getAt(tagsIndex, agAstNode.value.tags);
120
317
 
121
- if (child.type !== PropertyWrapper) return null;
318
+ if (child.type !== Property) return null;
122
319
 
123
320
  let tag = child.value.tags[0];
124
321
 
125
322
  let refIndex = tag.type === ShiftTag ? tagsIndex - tag.value.index : tagsIndex;
126
323
 
127
- let stack = Tags.findPath(refIndex, agAstNode.tags);
324
+ let stack = Tags.findPath(refIndex, agAstNode.value.tags);
128
325
  let { node, index: leafIdx } = stack.value;
129
326
  let leaf = Tags.getAt(leafIdx, node);
130
327
 
131
- if (leaf.type !== PropertyWrapper) {
328
+ if (leaf.type !== Property) {
132
329
  return null;
133
330
  }
134
331
 
@@ -136,14 +333,16 @@ export const getChildPropertyIndex = (agAstNode, tagsIndex) => {
136
333
 
137
334
  if (leafRefTag.type !== ReferenceTag) return null;
138
335
 
139
- let { name, isArray } = leafRefTag.value;
336
+ let { name, flags } = leafRefTag.value;
140
337
  let count = -1;
141
338
 
142
- if (!isArray) return null;
339
+ if (!name) throw new Error();
340
+
341
+ if (!flags.array) return null;
143
342
 
144
343
  for (let i = leafIdx; i >= 0; i--) {
145
344
  let value = Tags.getAt(i, node);
146
- if (value.type === PropertyWrapper) {
345
+ if (value.type === Property) {
147
346
  let firstTag = value.value.tags[0];
148
347
 
149
348
  if (firstTag.type === ReferenceTag && firstTag.value.name === name) {
@@ -169,7 +368,6 @@ export const getChildPropertyIndex = (agAstNode, tagsIndex) => {
169
368
  if ((res = Tags.getAtName(name, references))) {
170
369
  count += res;
171
370
  }
172
- } else {
173
371
  }
174
372
  }
175
373
  stack = stack.pop();
@@ -185,39 +383,31 @@ export const getChildPropertyIndex = (agAstNode, tagsIndex) => {
185
383
 
186
384
  export const getPropertyTagsIndex = (agAstNode, type, name, index, shiftIndex) => {
187
385
  let firstPropsIndex = __getPropertyTagsIndex(agAstNode, type, name, 0);
188
- let prop = firstPropsIndex == null ? null : Tags.getAt(firstPropsIndex, agAstNode.tags);
189
- let initializerOffset = prop?.value.tags[1]?.type === InitializerTag ? 1 : 0;
190
- let ref = prop?.value.property.reference;
191
- let sums = Tags.getSums(agAstNode.tags);
386
+ let prop = firstPropsIndex == null ? null : Tags.getAt(firstPropsIndex, getTags(agAstNode));
387
+ let ref = prop?.value.reference;
388
+ let sums = Tags.getSums(agAstNode.value.tags);
389
+ let counts = name ? sums.references : sums.specialTypes;
192
390
 
193
- if (ref?.isArray) {
391
+ if (ref?.flags.array) {
194
392
  if (index < 0) {
195
- if (name) {
196
- index = Tags.getAtName(name, sums.references) - initializerOffset + index;
197
- } else {
198
- index = Tags.getAtName(type, sums.specialTypes) - initializerOffset + index;
199
- }
393
+ index = Tags.getAtName(type, counts) + index;
200
394
  } else if (index == null) {
201
- if (name) {
202
- index = Tags.getAtName(name, sums.references) - 1 - initializerOffset;
203
- } else {
204
- index = Tags.getAtName(type, sums.specialTypes) - 1 - initializerOffset;
205
- }
395
+ index = Tags.getAtName(name, counts) - 1;
206
396
 
207
397
  if (index < 0) return null;
208
398
  }
209
399
  }
210
400
 
211
- let parentIndex = __getPropertyTagsIndex(agAstNode, type, name, (index ?? 0) + initializerOffset);
401
+ let parentIndex = __getPropertyTagsIndex(agAstNode, type, name, index ?? 0);
212
402
 
213
- if (ref?.flags.expression) {
403
+ if (parentIndex != null && ref?.flags.expression) {
214
404
  let shiftIndex_ = shiftIndex == null ? -1 : shiftIndex;
215
405
  if (shiftIndex_ < 0) {
216
406
  let shifts = 0;
217
407
  // TODO speed this up for deeply nested shifts
218
408
  // algorithm: make big jump forward, then look at shift index to see if we overshot completely
219
409
  // works because we know the size of the thing and a bigger thing doesn't fit in a smaller one
220
- while (Tags.getAt(parentIndex + shifts + 1, agAstNode.tags, 0)?.type === ShiftTag) {
410
+ while (Tags.getAt(parentIndex + shifts, agAstNode.value.children)?.value.shift) {
221
411
  shifts++;
222
412
  }
223
413
 
@@ -225,7 +415,7 @@ export const getPropertyTagsIndex = (agAstNode, type, name, index, shiftIndex) =
225
415
 
226
416
  return parentIndex + shifts + shiftIndex_ + 1;
227
417
  } else {
228
- if (parentIndex + shiftIndex_ >= Tags.getSize(agAstNode.tags)) {
418
+ if (parentIndex + shiftIndex_ >= Tags.getSize(agAstNode.value.tags)) {
229
419
  return null;
230
420
  }
231
421
 
@@ -236,23 +426,9 @@ export const getPropertyTagsIndex = (agAstNode, type, name, index, shiftIndex) =
236
426
  return parentIndex;
237
427
  };
238
428
 
239
- export const getInitializerTagsIndex = (agAstNode, referenceTag) => {
240
- let { type, name } = referenceTag.value;
241
-
242
- if (type === '#') return null;
243
-
244
- let index = __getPropertyTagsIndex(agAstNode, type, name, 0);
245
-
246
- if (index == null) return null;
247
-
248
- let nextTag = Tags.getAt(index, agAstNode.tags, 1);
249
-
250
- return nextTag?.type === InitializerTag ? index : null;
251
- };
252
-
253
429
  const __getPropertyTagsIndex = (agAstNode, type, name, index) => {
254
430
  let nameCount = 0;
255
- let node = Tags.getValues(agAstNode.tags)[1];
431
+ let node = Tags.getValues(agAstNode.value.tags)[1];
256
432
  let idx = -1;
257
433
 
258
434
  // drill into subtrees, passing over subtrees with too few references of the desired name
@@ -274,9 +450,8 @@ const __getPropertyTagsIndex = (agAstNode, type, name, index) => {
274
450
  if (!isArray(value)) {
275
451
  idx++;
276
452
  let tag = value;
277
- if (tag.type === PropertyWrapper) {
278
- let { tags, property } = tag.value;
279
- let { reference } = property;
453
+ if (tag.type === Property) {
454
+ let { tags, reference } = tag.value;
280
455
  if (tags[0].type === ReferenceTag) {
281
456
  if (
282
457
  (name != null && reference.name === name) ||
@@ -319,10 +494,12 @@ const __getPropertyTagsIndex = (agAstNode, type, name, index) => {
319
494
  const { hasOwn, freeze } = Object;
320
495
  const { isArray } = Array;
321
496
 
322
- export const getOriginalFirstNode = (node, height = 1) => {
323
- for (let child of Tags.traverse(node.tags)) {
324
- if (child.type === PropertyWrapper) {
325
- return child.value.property.node;
497
+ export const getOriginalFirstNode = (node) => {
498
+ if (node.type !== TreeNode) return;
499
+
500
+ for (let child of Tags.traverse(getTags(node))) {
501
+ if (child.type === Property) {
502
+ return child.value.node;
326
503
  }
327
504
  }
328
505
  return null;
@@ -333,14 +510,14 @@ export const getFirstNode = (node) => {
333
510
  };
334
511
 
335
512
  export const getFirstNodeProperty = (node) => {
336
- for (let child of Tags.traverse(node.tags)) {
337
- if (child.type === PropertyWrapper) {
513
+ for (let child of Tags.traverse(getTags(node))) {
514
+ if (child.type === Property) {
338
515
  let ref = child.value.reference;
339
516
  if (!ref.flags.expression) {
340
- return child.value;
517
+ return child;
341
518
  } else {
342
519
  let tagsIndex = getPropertyTagsIndex(node, ref.type, ref.name, 0, -1);
343
- return Tags.getAt(tagsIndex, node.tags + 2)?.value.node;
520
+ return Tags.getAt(tagsIndex, getTags(node), 2)?.value;
344
521
  }
345
522
  // is it shifted?
346
523
  }
@@ -348,12 +525,17 @@ export const getFirstNodeProperty = (node) => {
348
525
  return null;
349
526
  };
350
527
 
528
+ export const referenceIsSingular = (ref) => {
529
+ return (ref.name && !['#', '@'].includes(ref.type)) || ref.type === '_';
530
+ };
531
+
351
532
  export const referencesAreEqual = (a, b) => {
352
533
  return (
353
534
  a === b ||
354
535
  (a.type === b.type &&
355
536
  a.name === b.name &&
356
- a.isArray === b.isArray &&
537
+ a.flags.array === b.flags.array &&
538
+ a.flags.intrinsic === b.flags.intrinsic &&
357
539
  a.flags.hasGap === b.flags.hasGap &&
358
540
  a.flags.expression === b.flags.expression)
359
541
  );
@@ -363,15 +545,15 @@ export const isDefined = (obj, key) => hasOwn(obj, key) && obj[key] !== undefine
363
545
  export const isUndefined = (obj, key) => !hasOwn(obj, key) || obj[key] === undefined;
364
546
 
365
547
  export function* relatedNodes(node) {
366
- for (const child of Tags.traverse(node.tags)) {
367
- if (child.type === PropertyWrapper) {
548
+ for (const child of Tags.traverse(getTags(node))) {
549
+ if (child.type === Property) {
368
550
  yield child.node;
369
551
  }
370
552
  }
371
553
  }
372
554
 
373
555
  export const getProperty = (pathSegment, node) => {
374
- if (!node) throw new Error();
556
+ if (!node || node.type !== TreeNode) return null;
375
557
 
376
558
  if (pathSegment == null) throw new Error('Bad path segment');
377
559
 
@@ -382,7 +564,7 @@ export const getProperty = (pathSegment, node) => {
382
564
 
383
565
  if (propIndex == null) return null;
384
566
 
385
- return Tags.getAt(propIndex, node.tags)?.value.property;
567
+ return Tags.getAt(propIndex, getTags(node));
386
568
  };
387
569
 
388
570
  export const has = (path, node) => {
@@ -393,20 +575,15 @@ export const has = (path, node) => {
393
575
  let pathArr = [...path];
394
576
  let node_ = get(pathArr.slice(0, -1), node);
395
577
  let seg = pathArr[pathArr.length - 1];
396
- let name = typeof seg === 'string' ? seg : seg.name;
397
578
 
398
- return (
399
- !!getProperty(seg, node_) ||
400
- getInitializerTagsIndex(node_, buildReferenceTag(null, name)) != null
401
- );
579
+ return !!getProperty(seg, node_);
402
580
  };
403
581
 
404
582
  export const get = (path, node) => getOr(null, path, node);
405
583
 
406
584
  export const getOr = (defaultValue, path, node) => {
407
- if (!node) throw new Error();
408
-
409
585
  if (path == null) throw new Error('Bad path');
586
+ if (!node) return null;
410
587
 
411
588
  if (!isArray(path)) {
412
589
  path = [path];
@@ -416,7 +593,8 @@ export const getOr = (defaultValue, path, node) => {
416
593
  let result = root || node;
417
594
 
418
595
  let start = 0;
419
- if (path[0]?.type === '.') {
596
+ // TODO allow dot dot at all levels
597
+ if (path[0]?.type === '_') {
420
598
  if (!root) return null;
421
599
  start = 1;
422
600
  }
@@ -441,10 +619,10 @@ export const getOr = (defaultValue, path, node) => {
441
619
 
442
620
  if (!property) return defaultValue;
443
621
 
444
- result = property.node;
622
+ result = getRoot(property.value.node);
445
623
  }
446
624
 
447
- return result;
625
+ return isNullNode(result) ? null : result;
448
626
  };
449
627
 
450
628
  export function* list(name, node) {
@@ -460,9 +638,7 @@ export const countList = (name, node) => {
460
638
 
461
639
  if (name == null) throw new Error('Bad path');
462
640
 
463
- let hasInitializer = getInitializerTagsIndex(node, buildReferenceTag(null, name)) != null;
464
-
465
- return Tags.getAtName(name, Tags.getSums(node.tags).references) - (hasInitializer ? 1 : 0);
641
+ return Tags.getAtName(name, Tags.getSums(getTags(node)).references);
466
642
  };
467
643
 
468
644
  export function* allTagPathsFor(range, options = {}) {
@@ -478,7 +654,7 @@ export function* allTagPathsFor(range, options = {}) {
478
654
 
479
655
  while (path) {
480
656
  if (path.inner && path.previousSibling.tag.type === ReferenceTag) {
481
- path = new TagPath(path.inner, 0);
657
+ path = TagPath.from(path.inner, 0);
482
658
  }
483
659
 
484
660
  if (path.path.depth < startPath.path.depth) {
@@ -513,7 +689,7 @@ export function* allTagsFor(range, options = {}) {
513
689
  }
514
690
 
515
691
  export const buildFullRange = (node) => {
516
- let sum = Tags.getSize(node.tags);
692
+ let sum = Tags.getSize(getTags(node));
517
693
  return sum ? [0, sum - 1] : null;
518
694
  };
519
695
 
@@ -532,6 +708,10 @@ export function* ownTagPathsFor(range) {
532
708
  }
533
709
  }
534
710
 
711
+ export const buildNullNode = () => {
712
+ return buildNode(buildNullTag());
713
+ };
714
+
535
715
  const findRight = (arr, predicate) => {
536
716
  for (let i = arr.length - 1; i >= 0; i--) {
537
717
  let value = arr[i];
@@ -578,6 +758,28 @@ const skipToDepth = (depth, frame) => {
578
758
  return parent;
579
759
  };
580
760
 
761
+ export const getRange = (node) => {
762
+ const { tags } = node;
763
+ let path = Path.from(node);
764
+ return Tags.getSize(tags) ? [TagPath.from(path, 0), TagPath.from(path, -1)] : null;
765
+ };
766
+
767
+ export const getFlags = (node) => {
768
+ return node.type === TreeNode ? node.value.flags : null;
769
+ };
770
+
771
+ export const getAttributes = (node) => {
772
+ return node.type === TreeNode ? node.value.attributes : freeze({});
773
+ };
774
+
775
+ export const isSelfClosingTag = (tag) => {
776
+ return tag.type === OpenNodeTag
777
+ ? tag.value.selfClosing
778
+ : [NullTag, GapTag].includes(tag.type)
779
+ ? true
780
+ : false;
781
+ };
782
+
581
783
  export let pathForTagPath = (tagPath) => {
582
784
  let refTagPath = tagPath;
583
785
  let isShift = tagPath.tag.type === ShiftTag;
@@ -596,34 +798,77 @@ export let pathForTagPath = (tagPath) => {
596
798
  return [{ type, name, index, shiftIndex }];
597
799
  };
598
800
 
801
+ let getSigilTag_ = (tag) => {
802
+ switch (tag.type) {
803
+ case Property:
804
+ return getTags(tag.value.property.node)[1][0];
805
+ case TreeNode:
806
+ case NullNode:
807
+ case GapNode:
808
+ return getTags(tag)[1][0];
809
+ case OpenNodeTag:
810
+ case GapTag:
811
+ case NullTag:
812
+ return tag;
813
+ }
814
+ };
815
+
599
816
  export const Path = class AgastPath extends WeakStackFrame {
817
+ static create(node) {
818
+ return node && new Path(node);
819
+ }
820
+
600
821
  static from(node) {
601
- let node_ = isPlainObject(node) ? node : node.node;
602
- return node_ && this.create(node_);
822
+ return node && new Path(node);
603
823
  }
604
824
 
605
- constructor(parent, node, parentIndex = null) {
606
- super(parent);
825
+ static fromTag(openTag) {
826
+ return Path.from(buildNode(openTag));
827
+ }
828
+
829
+ static set(node, path, value) {
830
+ return Path.from(node).replaceAt(path, value).node;
831
+ }
832
+
833
+ static get(node, path) {
834
+ return Path.from(node).get(path).node;
835
+ }
836
+
837
+ constructor(nodeOrParent, parentIndex = null, isGap = false) {
838
+ let isRoot = !(nodeOrParent instanceof Path);
839
+ let parent = isRoot ? null : nodeOrParent;
840
+ let node = isRoot ? nodeOrParent : parent.getChild(parentIndex - 1).value.node;
607
841
 
608
- if (!node || !(hasOwn(node, 'type') && hasOwn(node, 'tags'))) throw new Error();
842
+ if (isGap) {
843
+ ({ node } = BTree.getAt(-1, node.value.bounds[0]).value);
844
+ }
845
+
846
+ if (!node || ![TreeNode, NullNode, GapNode].includes(node.type)) throw new Error();
609
847
 
610
- if (parent && parentIndex == null) throw new Error();
611
848
  if (!node) throw new Error();
849
+ if (!Object.isFrozen(node)) throw new Error();
612
850
  if (isArray(node)) throw new Error();
613
851
 
852
+ super(parent);
853
+
614
854
  this.node = node;
615
- this.parentIndex = parentIndex; // in the parent
855
+ this.parentIndex = parentIndex;
856
+ this.isGap = isGap;
616
857
 
617
- let parentTag = parent && Tags.getAt(parentIndex, parent.node.tags);
858
+ this.name = node.type === TreeNode ? node.value.name : null;
859
+ this.type = node.type === TreeNode ? node.value.type : null;
860
+ this.flags = node.type === TreeNode ? node.value.flags : null;
861
+ this.attributes = node.type === TreeNode ? node.value.attributes : null;
862
+ this.tags = getTags(node);
618
863
 
619
- if (parentTag && parentTag.type !== PropertyWrapper) {
864
+ let parentTag = parentIndex == null ? null : Tags.getAt(parentIndex, getTags(parent.node));
865
+
866
+ if (parentTag && parentTag.type !== Property) {
620
867
  throw new Error();
621
868
  }
622
869
 
623
- parentIndex != null && parentTag?.value.tags[0];
624
-
625
870
  if (
626
- parent &&
871
+ parentIndex != null &&
627
872
  (!this.referenceTag || ![ReferenceTag, ShiftTag].includes(this.referenceTag.type))
628
873
  ) {
629
874
  throw new Error();
@@ -645,42 +890,74 @@ export const Path = class AgastPath extends WeakStackFrame {
645
890
  freeze(this);
646
891
  }
647
892
 
648
- get openTagPath() {
649
- let tagPath = TagPath.from(this, 0);
650
- if (tagPath.tag.type === DoctypeTag) {
651
- tagPath = tagPath.nextSibling;
652
- }
653
- return tagPath;
893
+ getChild(childrenIndex) {
894
+ let { node } = this;
895
+ if (isStubNode(node)) return null;
896
+ return Tags.getAt(childrenIndex, getChildren(node)) || null;
654
897
  }
655
898
 
656
- get openTag() {
657
- return this.openTagPath.tag;
899
+ getSigilTagPath() {
900
+ return TagPath.from(this, 0);
901
+ }
902
+
903
+ tagPathAt(tagsIndex, propertyIndex) {
904
+ return TagPath.from(this, tagsIndex, propertyIndex);
905
+ }
906
+
907
+ getPropertyTagPath(childrenIndex) {
908
+ let tagPath = this.tagPathAt(childrenIndex + 1);
909
+ return tagPath && tagPath.tag.type === Property ? tagPath : null;
910
+ }
911
+
912
+ getReferenceTagPath(childrenIndex) {
913
+ return this.tagPathAt(childrenIndex + 1, [0]);
914
+ }
915
+
916
+ // getBindings(childrenIndex) {
917
+ // let property = this.getPropertyTagPath(childrenIndex);
918
+ // return property && Tags.getAt([1], property.value.tags);
919
+ // }
920
+
921
+ getBindingTagPath(childrenIndex, bindingIndex) {
922
+ return this.tagPathAt(childrenIndex + 1, [1, bindingIndex]);
923
+ }
924
+
925
+ getNodeTagPath(childrenIndex) {
926
+ return this.tagPathAt(childrenIndex + 1, [2]);
927
+ }
928
+
929
+ getHashTagPath(childrenIndex) {
930
+ return this.tagPathAt(childrenIndex + 1, [3]);
931
+ }
932
+
933
+ get openTagPath() {
934
+ let tagPath = TagPath.from(this, 0);
935
+ return tagPath.tag.type === OpenNodeTag ? tagPath : null;
658
936
  }
659
937
 
660
938
  get closeTagPath() {
661
939
  let tagPath = TagPath.from(this, -1);
662
- if (tagPath.tag.type !== CloseNodeTag) return null;
663
- return tagPath;
940
+ return tagPath.tag.type === CloseNodeTag ? tagPath : null;
664
941
  }
665
942
 
666
- get closeTag() {
667
- return this.closeTagPath.tag;
943
+ get openTag() {
944
+ return this.openTagPath.tag;
668
945
  }
669
946
 
670
- tagPathAt(idx, offset) {
671
- return TagPath.from(this, idx, offset);
947
+ get open() {
948
+ return this.openTag.value;
672
949
  }
673
950
 
674
- get binding() {
675
- return this.bindingTag?.value ?? null;
951
+ get closeTag() {
952
+ return this.closeTagPath.tag;
676
953
  }
677
954
 
678
- get bindingTag() {
679
- return this.bindingTagPath?.tag ?? null;
955
+ get bindings() {
956
+ return this.parentPropertyPath?.tag.value.bindings;
680
957
  }
681
958
 
682
- get bindingTagPath() {
683
- return (this.parent?.node ?? null) && new TagPath(this.parent, this.parentIndex, 1);
959
+ get bindingTags() {
960
+ return this.parentPropertyPath?.tag.value.tags[1];
684
961
  }
685
962
 
686
963
  get reference() {
@@ -695,20 +972,23 @@ export const Path = class AgastPath extends WeakStackFrame {
695
972
  if (!this.parent?.node) {
696
973
  return null;
697
974
  }
698
- let path = new TagPath(this.parent, this.parentIndex, 0);
699
- if (path.tag.type === ShiftTag) {
700
- path = new TagPath(this.parent, this.parentIndex - path.tag.value.index, 0);
975
+ return TagPath.from(this.parent, this.parentIndex, 0).referenceTagPath;
976
+ }
977
+
978
+ get propertyKeyTagPath() {
979
+ if (!this.parent?.node) {
980
+ return null;
701
981
  }
702
- return path;
982
+ return TagPath.from(this.parent, this.parentIndex, 0);
703
983
  }
704
984
 
705
985
  get parentPropertyPath() {
706
- if (!this.parent?.node) {
986
+ if (!this.parent?.node || this.parentIndex == null) {
707
987
  return null;
708
988
  }
709
- let path = new TagPath(this.parent, this.parentIndex);
989
+ let path = TagPath.from(this.parent, this.parentIndex);
710
990
  if (path.tag.type === ShiftTag) {
711
- path = new TagPath(this.parent, this.parentIndex - path.tag.value.index);
991
+ path = TagPath.from(this.parent, this.parentIndex - path.tag.value.index);
712
992
  }
713
993
  return path;
714
994
  }
@@ -717,86 +997,63 @@ export const Path = class AgastPath extends WeakStackFrame {
717
997
  return this.parentPropertyPath?.tag ?? null;
718
998
  }
719
999
 
720
- get(path) {
721
- let path_ = path;
722
- if (typeof path_ === 'string') {
723
- path_ = [path_];
724
- }
725
- if (!isArray(path_)) throw new Error();
726
-
727
- let pathInst = this;
1000
+ get parentNode() {
1001
+ return this.parent.node;
1002
+ }
728
1003
 
729
- if (!pathInst.node.type && pathInst.node.flags.cover) {
730
- if (pathInst.depth) throw new Error();
731
- pathInst = pathInst.push(
732
- getRoot(pathInst.node),
733
- getPropertyTagsIndex(pathInst.node, '.', null, 0),
734
- );
1004
+ get firstPropertyTagPath() {
1005
+ let tagPath = this.tagPathAt(0);
1006
+ while (tagPath && (!tagPath.propertyPath || isNullNode(tagPath.propertyPath.tag.value.node))) {
1007
+ tagPath = tagPath.nextSibling;
1008
+ if (tagPath?.propertyPath) {
1009
+ tagPath = tagPath.propertyPath;
1010
+ }
735
1011
  }
1012
+ return tagPath;
1013
+ }
736
1014
 
737
- for (let i = 0; i < path_.length; i++) {
738
- let nameOrSeg = path_[i];
739
-
740
- let seg = nameOrSeg;
741
- if (isString(nameOrSeg)) {
742
- let index = path_[i + 1];
743
- if (typeof index === 'number') {
744
- i++;
745
- } else {
746
- index = undefined;
747
- }
748
- seg = buildPathSegment(nameOrSeg, index);
1015
+ get lastPropertyTagPath() {
1016
+ let tagPath = this.tagPathAt(-1);
1017
+ while (tagPath && (!tagPath.propertyPath || isNullNode(tagPath.propertyPath.tag.value.node))) {
1018
+ tagPath = tagPath.previousSibling;
1019
+ if (tagPath?.propertyPath) {
1020
+ tagPath = tagPath.propertyPath;
749
1021
  }
750
-
751
- let tagsIndex = getPropertyTagsIndex(
752
- pathInst.node,
753
- seg.type,
754
- seg.name,
755
- seg.index,
756
- seg.shiftIndex,
757
- );
758
-
759
- let { property } = Tags.getAt(tagsIndex, pathInst.node.tags).value;
760
-
761
- if (!property) return null;
762
-
763
- let { node } = property;
764
-
765
- pathInst = pathInst.push(node, tagsIndex);
766
1022
  }
767
-
768
- return pathInst;
1023
+ return tagPath;
769
1024
  }
770
1025
 
771
- replaceWith(node, binding = buildBinding()) {
772
- return this.replaceAt([], node, binding);
773
- }
1026
+ get coverBoundary() {
1027
+ let path = this;
774
1028
 
775
- replaceAt(path, node, binding = buildBinding()) {
776
- throw new Error('not working yet');
1029
+ if (path.parent && !isFragment(this.node) && isCover(path.parent.node)) {
1030
+ path = path.parent;
1031
+ }
777
1032
 
778
- let built;
779
- let path_ = path;
780
- if (typeof path_ === 'string') {
781
- path_ = [path_];
1033
+ while (path && isCover(path.node) && path.parent && isCover(path.parent.node)) {
1034
+ path = path.parent;
782
1035
  }
783
- if (!isArray(path_)) throw new Error();
784
1036
 
1037
+ return path;
1038
+ }
1039
+
1040
+ get(path) {
1041
+ let path_ = typeof path === 'string' ? [path] : [...path];
785
1042
  let pathInst = this;
786
1043
 
787
- if (!pathInst.node.type && pathInst.node.flags.cover) {
788
- if (pathInst.depth) throw new Error();
789
- pathInst = pathInst.push(
790
- getRoot(pathInst.node),
791
- getPropertyTagsIndex(pathInst.node, '.', null, 0),
792
- );
1044
+ let skippedRootFragment = false;
1045
+
1046
+ if (!(path.length && isObject(path[1]) && path[0].type === '_')) {
1047
+ while (!pathInst.node.value.name && pathInst.node.value.type === Symbol.for('_')) {
1048
+ if (pathInst.depth) throw new Error();
1049
+ skippedRootFragment = true;
1050
+ pathInst = pathInst.push(getPropertyTagsIndex(pathInst.node, '_', null, 0));
1051
+ }
793
1052
  }
794
1053
 
795
1054
  for (let i = 0; i < path_.length; i++) {
796
1055
  let nameOrSeg = path_[i];
797
1056
 
798
- built = { ...pathInst.node };
799
-
800
1057
  let seg = nameOrSeg;
801
1058
  if (isString(nameOrSeg)) {
802
1059
  let index = path_[i + 1];
@@ -808,6 +1065,8 @@ export const Path = class AgastPath extends WeakStackFrame {
808
1065
  seg = buildPathSegment(nameOrSeg, index);
809
1066
  }
810
1067
 
1068
+ if (i === 0 && seg.type === '_' && skippedRootFragment) continue;
1069
+
811
1070
  let tagsIndex = getPropertyTagsIndex(
812
1071
  pathInst.node,
813
1072
  seg.type,
@@ -816,258 +1075,852 @@ export const Path = class AgastPath extends WeakStackFrame {
816
1075
  seg.shiftIndex,
817
1076
  );
818
1077
 
819
- let tagPath = TagPath.from(pathInst, tagsIndex);
1078
+ if (tagsIndex == null) return null;
820
1079
 
821
- let {
822
- property,
823
- tags: { 0: refTag },
824
- } = tagPath.tag.value;
825
-
826
- if (refTag.type === ShiftTag) {
827
- let { index } = refTag.value;
1080
+ pathInst = pathInst.push(tagsIndex);
1081
+ }
828
1082
 
829
- for (let i = 0; i < index - 1; i++) {
830
- // update bounds!
831
- }
832
- tagsIndex -= index;
833
- pathInst = pathInst.replace(built, tagsIndex);
834
- }
1083
+ return pathInst;
1084
+ }
835
1085
 
836
- if (!property) return null;
1086
+ replaceWith(node, bindingTags) {
1087
+ let bindings = bindingTags?.map((tag) => tag.value); // TODO improve here
1088
+ if (bindings != null && !isArray(bindings)) throw new Error();
1089
+ if (!isPlainObject(node)) throw new Error();
837
1090
 
838
- let { node } = property;
1091
+ if (!node) throw new Error();
839
1092
 
840
- pathInst = pathInst.push(node, tagsIndex);
1093
+ if (!this.parent) {
1094
+ return Path.from(node);
841
1095
  }
842
1096
 
843
- let node_ = isPlainObject(node) ? node : node.node;
844
-
845
- if (!node_) throw new Error();
846
-
847
- path = pathInst.parent;
1097
+ let built = node;
1098
+ let path = this.parent;
848
1099
  let replacementPath = [];
849
1100
 
850
- let targetReference = pathInst.reference;
851
- let targetBinding = binding;
852
- let targetParentIndex = pathInst.parentIndex;
1101
+ let targetShift_ = this.parent?.parentProperty?.value.tags[0];
1102
+ let targetShift = targetShift_ && (targetShift_.type === ReferenceTag ? null : targetShift_);
1103
+ let targetReference = this.reference;
1104
+ let targetBindings = bindings == null ? this.bindings : bindings;
1105
+ let targetParentIndex = this.parentIndex;
1106
+ let held = null;
1107
+ let heldCount = 0;
1108
+ let value;
853
1109
 
854
1110
  while (path) {
855
- let value = built;
856
- built = { ...path.node };
857
-
858
- let { tags } = Tags.getAt(targetParentIndex, path.node.tags).value;
859
- let property = buildProperty(targetReference, targetBinding, value);
1111
+ value = built;
1112
+ built = buildNode(getTags(path.node));
860
1113
 
861
- let tags_ = [tags[0], buildChild(BindingTag, targetBinding), buildChild(Property, property)];
1114
+ let { tags, shift } = Tags.getAt(targetParentIndex, getTags(path.node)).value;
862
1115
 
863
- built.tags = Tags.replaceAt(
864
- targetParentIndex,
865
- built.tags,
866
- buildChild(PropertyWrapper, buildPropertyWrapper(tags_, property)),
867
- );
868
-
869
- finalizeNode(built);
1116
+ let firstTag = tags[0];
1117
+ let newFirstTag = firstTag;
1118
+ if (shift) {
1119
+ newFirstTag = buildChild(ReferenceTag, targetReference);
1120
+ }
870
1121
 
871
- if (targetReference.name) {
872
- replacementPath.push(
873
- buildPathSegment(
874
- targetReference.name,
875
- getChildPropertyIndex(path.node, targetParentIndex),
1122
+ let tags_ = [
1123
+ firstTag,
1124
+ targetBindings.map((binding) => buildChild(BindingTag, binding)),
1125
+ value,
1126
+ ];
1127
+
1128
+ if (!held && !shift) {
1129
+ built = buildNode(
1130
+ Tags.replaceAt(
1131
+ targetParentIndex,
1132
+ built.value.tags,
1133
+ buildChild(Property, buildProperty(tags_)),
876
1134
  ),
877
1135
  );
1136
+ replacementPath.push(targetParentIndex);
1137
+ } else {
1138
+ let i = targetParentIndex + 1;
1139
+ let nextProperty = Tags.getAt(i, built.value.tags);
1140
+
1141
+ let nextShift = nextProperty?.type === Property && nextProperty.value.shift;
1142
+
1143
+ if (nextShift) {
1144
+ let { reference, bindings } = BTree.getAt(0, nextProperty.value.node.value.bounds[0]);
1145
+
1146
+ path = path.push(i);
1147
+
1148
+ built = buildNode(
1149
+ Tags.replaceAt(
1150
+ targetParentIndex - shift.index,
1151
+ built.value.tags,
1152
+ buildChild(Property, buildProperty(tags_)),
1153
+ ),
1154
+ );
1155
+
1156
+ targetParentIndex -= shift.index;
1157
+ for (let i = shift.index - 1; i >= 0; i--) {
1158
+ built = buildNode(Tags.removeAt(targetParentIndex + 1, built.value.tags));
1159
+ }
1160
+
1161
+ tags_ = [newFirstTag, tags_[1], tags_[2]];
1162
+
1163
+ targetShift = null;
1164
+ targetReference = reference;
1165
+ targetBindings = bindings;
1166
+ targetParentIndex = getPropertyTagsIndex(
1167
+ nextProperty.value.node,
1168
+ reference.type,
1169
+ reference.name,
1170
+ 0,
1171
+ 0,
1172
+ );
1173
+ held = null;
1174
+
1175
+ replacementPath.push(targetParentIndex);
1176
+ continue;
1177
+ } else {
1178
+ built = buildNode(
1179
+ Tags.replaceAt(
1180
+ targetParentIndex,
1181
+ getTags(built),
1182
+ buildChild(Property, buildProperty(tags_, shift)),
1183
+ ),
1184
+ );
1185
+ replacementPath.push(targetParentIndex);
1186
+ }
878
1187
  }
879
1188
 
1189
+ held = targetShift ? value : null;
1190
+ heldCount = held ? heldCount + 1 : 0;
1191
+
1192
+ targetShift_ = path?.parentProperty?.value.tags[0];
1193
+ targetShift = targetShift_ && (targetShift_.type === ReferenceTag ? null : targetShift_);
880
1194
  targetReference = path.reference;
881
- targetBinding = path.binding;
1195
+ targetBindings = path.bindings;
882
1196
  targetParentIndex = path.parentIndex;
883
1197
  path = path.parent;
884
1198
  }
885
1199
 
886
- replacementPath.reverse();
887
-
888
- return Path.from(built).get(replacementPath).atDepth(this.depth);
1200
+ return this.mapOnto(built);
889
1201
  }
890
1202
 
891
- atDepth(depth) {
892
- return skipToDepth(depth, this);
1203
+ replaceAt(path, node, bindings) {
1204
+ return this.get(path).replaceWith(node, bindings).atDepth(0);
893
1205
  }
894
- };
895
-
896
- export const tagPathsAreEqual = (a, b) => {
897
- if (a == null || b == null) return b == a;
898
- return a.path.node === b.path.node && a.tagsIndex === b.tagsIndex;
899
- };
900
1206
 
901
- export class TagPath {
902
- constructor(path, tagsIndex, wrapperIndex) {
903
- if (path == null || tagsIndex == null) throw new Error();
904
-
905
- let tag = Tags.getAt(tagsIndex, path.node.tags);
1207
+ mapOnto(rootNode) {
1208
+ let path = Path.from(rootNode);
906
1209
 
907
- if (tag == null) throw new Error();
908
-
909
- if (tag.type === PropertyWrapper && wrapperIndex != null) {
910
- tag = tag.value.tags[wrapperIndex];
1210
+ for (let i = 1; i <= this.depth; i++) {
1211
+ let { parentIndex, isGap } = this.atDepth(i);
1212
+ path = path.push(parentIndex, isGap);
911
1213
  }
912
1214
 
913
- this.path = path;
914
- this.node = path.node;
915
- this.tag = tag;
916
- this.tagsIndex = tagsIndex;
917
- this.wrapperIndex = wrapperIndex;
1215
+ return path;
1216
+ }
918
1217
 
919
- freeze(this);
1218
+ atDepth(depth) {
1219
+ return skipToDepth(depth, this);
920
1220
  }
921
1221
 
922
- static from(path, tagsIndex, wrapperIndex) {
923
- let size = Tags.getSize(path.node.tags);
924
- let index = tagsIndex < 0 ? size + tagsIndex : tagsIndex;
1222
+ get rootNode() {
1223
+ return this.atDepth(0).node;
1224
+ }
925
1225
 
926
- let tag = Tags.getAt(index, path.node.tags);
1226
+ get done() {
1227
+ let { parent, node } = this;
1228
+ let sigilTag = getSigilTag(node);
927
1229
 
928
- if (!tag) return null;
1230
+ return !parent && sigilTag && !!(isSelfClosingTag(sigilTag) || getCloseTag(node));
1231
+ }
929
1232
 
930
- let offset =
931
- wrapperIndex == null || tag.type !== PropertyWrapper
932
- ? null
933
- : wrapperIndex < 0
934
- ? tag.value.tags.length + wrapperIndex
935
- : wrapperIndex;
1233
+ get held() {
1234
+ let tagPath = this.tagPathAt(-1);
936
1235
 
937
- return index >= 0 && index < size ? new TagPath(path, index, offset) : null;
938
- }
1236
+ // TODO is this safe
1237
+ if (tagPath.tag.type !== Property) return null;
939
1238
 
940
- static fromNode(node, tagsIndex, wrapperIndex) {
941
- return TagPath.from(Path.from(node), tagsIndex, wrapperIndex);
942
- }
1239
+ let { tags } = tagPath.tag.value;
1240
+
1241
+ if (tags[0]?.type === ShiftTag && !tags[2]) {
1242
+ return TagPath.from(tagPath.path, tagPath.tagsIndex - 1).tag.value;
1243
+ }
1244
+
1245
+ // if we're not at the left side, we aren't held
1246
+ if (
1247
+ !tagPath.equalTo(this.firstPropertyTagPath) ||
1248
+ propertyIsFull(tagPath.tag) ||
1249
+ (tagPath.inner && nodeIsComplete(tagPath.inner.node))
1250
+ ) {
1251
+ return null;
1252
+ }
1253
+
1254
+ let coverParent = this;
1255
+
1256
+ for (;;) {
1257
+ let refPath = coverParent.propertyKeyTagPath;
1258
+
1259
+ if (refPath?.tag.type === ShiftTag) {
1260
+ return TagPath.from(refPath.path, refPath.tagsIndex - 1).tag.value;
1261
+ }
1262
+
1263
+ coverParent = coverParent.parent;
1264
+ if (coverParent?.node.type !== Symbol.for('_')) break;
1265
+ }
1266
+
1267
+ return null;
1268
+ }
1269
+
1270
+ advance(tag) {
1271
+ if (!tag) throw new Error();
1272
+
1273
+ let tagPath = this.tagPathAt(-1, 2);
1274
+
1275
+ if (tagPath?.tag.type === Property ? tagPath?.nextSibling : tagPath?.next) throw new Error();
1276
+
1277
+ if (this.done) throw new Error();
1278
+
1279
+ let resultPath = this;
1280
+
1281
+ switch (tag.type) {
1282
+ case BindingTag: {
1283
+ if (!this.lastPropertyTagPath) {
1284
+ resultPath = this.advanceUnwrapped(buildReferenceTag()).path;
1285
+ }
1286
+ break;
1287
+ }
1288
+
1289
+ case TreeNode:
1290
+ case NullNode:
1291
+ case GapNode:
1292
+ case NullTag:
1293
+ case GapTag:
1294
+ case OpenNodeTag: {
1295
+ let sigilTag = getSigilTag_(tag);
1296
+
1297
+ if (sigilTag.type === OpenNodeTag && sigilTag.value.type === Symbol.for('__')) {
1298
+ break;
1299
+ }
1300
+
1301
+ let lastTagPath = this.lastPropertyTagPath;
1302
+ let newProperty = !lastTagPath || propertyIsFull(lastTagPath.tag);
1303
+ let reference = lastTagPath?.referenceTagPath.tag.value;
1304
+
1305
+ // TODO revisit this condition
1306
+ if (!getFlags(this.node)?.token || reference?.type === '@') {
1307
+ if (newProperty || !lastTagPath.tag.value.tags.length) {
1308
+ resultPath = this.advanceUnwrapped(buildReferenceTag()).path;
1309
+ }
1310
+ }
1311
+
1312
+ break;
1313
+ }
1314
+ }
1315
+
1316
+ if (tag.type === Property) {
1317
+ resultPath = Path.from(buildNode(Tags.push(getTags(resultPath.node), tag)));
1318
+ } else {
1319
+ resultPath = resultPath.advanceUnwrapped(tag).path;
1320
+ }
1321
+
1322
+ if (
1323
+ (tag.type === CloseNodeTag || (tag.type === OpenNodeTag && tag.value.selfClosing)) &&
1324
+ resultPath.depth
1325
+ ) {
1326
+ return resultPath.parent;
1327
+ }
1328
+
1329
+ return resultPath;
1330
+ }
1331
+
1332
+ advanceUnwrapped(tag) {
1333
+ if (!tag) throw new Error();
1334
+
1335
+ let tagPath = this.tagPathAt(-1, [-1, -1]);
1336
+
1337
+ if (
1338
+ [TreeNode, NullNode, GapNode].includes(tagPath?.tag.type)
1339
+ ? tagPath?.nextSibling
1340
+ : tagPath?.next
1341
+ )
1342
+ throw new Error();
1343
+
1344
+ if (this.done) throw new Error();
1345
+
1346
+ let targetPath = tagPath && endsNode(tagPath.tag) ? this.parent : this;
1347
+
1348
+ switch (tag.type) {
1349
+ case ReferenceTag: {
1350
+ let { type, name, flags } = tag.value;
1351
+
1352
+ if ([ReferenceTag, ShiftTag, BindingTag].includes(targetPath.tagPathAt(-1, -1)?.tag.type))
1353
+ throw new Error('invalid location for reference');
1354
+ if (!name && !type) throw new Error();
1355
+
1356
+ if (flags.hasGap && flags.intrinsic) throw new Error();
1357
+ if (type && !['_', '.', '#', '@'].includes(type)) throw new Error();
1358
+ if (flags.array && ['_', '#', '@'].includes(type)) throw new Error();
1359
+ if (getFlags(targetPath.node).token && type !== '@') throw new Error();
1360
+ if (targetPath.open.type === Symbol.for('_') && name != null) throw new Error();
1361
+ if (targetPath.open.type === Symbol.for('_') && !['_', '#'].includes(type))
1362
+ throw new Error();
1363
+
1364
+ if (tag.value.name) {
1365
+ let property = getProperty(tag.value.name, targetPath.node);
1366
+ if (
1367
+ getOr(null, tag.value.name, targetPath.node) &&
1368
+ !property.value.shift &&
1369
+ !referencesAreEqual(tag.value, property.value.reference)
1370
+ ) {
1371
+ throw new Error('mismatched references');
1372
+ }
1373
+ } else if (tag.value.type === '_') {
1374
+ let rootIdx = getPropertyTagsIndex(targetPath.node, '_', null, 0);
1375
+
1376
+ if (
1377
+ rootIdx != null &&
1378
+ !referencesAreEqual(
1379
+ tag.value,
1380
+ Tags.getAt(rootIdx, getTags(targetPath.node)).value.property.reference,
1381
+ )
1382
+ ) {
1383
+ throw new Error('mismatched references');
1384
+ }
1385
+ }
1386
+
1387
+ let property = buildChild(Property, buildProperty([tag]));
1388
+
1389
+ targetPath = targetPath.replaceWith(
1390
+ buildNode(Tags.push(getTags(targetPath.node), property)),
1391
+ );
1392
+ break;
1393
+ }
1394
+
1395
+ case BindingTag: {
1396
+ if (![ReferenceTag, ShiftTag].includes(this.tagPathAt(-1, -1).tag.type)) {
1397
+ throw new Error('Invalid location for BindingTag');
1398
+ }
1399
+ if (!tag.value.segments) throw new Error();
1400
+
1401
+ let refPath = this.tagPathAt(-1, 0);
1402
+ let propPath = TagPath.from(refPath.path, refPath.tagsIndex);
1403
+
1404
+ let { shift } = propPath.tag.value;
1405
+
1406
+ if (![ReferenceTag, ShiftTag].includes(refPath.tag.type)) throw new Error();
1407
+
1408
+ if (refPath.tag.type === ShiftTag) {
1409
+ refPath = TagPath.from(refPath.path, refPath.tagsIndex - shift.index, 0);
1410
+ }
1411
+
1412
+ let { 0: firstTag, 1: bindingTags = [] } = propPath.tag.value.tags;
1413
+ let tags = [firstTag, [...bindingTags, tag]];
1414
+ let property = buildChild(Property, buildProperty(tags, shift));
1415
+
1416
+ targetPath = this.replaceWith(
1417
+ buildNode(Tags.replaceAt(propPath.tagsIndex, getTags(this.node), property)),
1418
+ );
1419
+ break;
1420
+ }
1421
+
1422
+ case OpenNodeTag: {
1423
+ let { literalValue, flags, type } = tag.value;
1424
+
1425
+ if (this.held && flags.token) throw new Error();
1426
+
1427
+ if (type === Symbol.for('__')) {
1428
+ throw new Error('not implemented');
1429
+ }
1430
+
1431
+ let refTag = this.node && Tags.getAt(-1, getChildren(this.node)).value.tags[0];
1432
+
1433
+ if (![ReferenceTag, ShiftTag].includes(refTag.type)) throw new Error();
1434
+
1435
+ let node = buildNode(tag);
1436
+
1437
+ if (literalValue && !flags.token) {
1438
+ throw new Error();
1439
+ }
1440
+
1441
+ targetPath = this.advanceUnwrapped(node).path;
1442
+
1443
+ targetPath = targetPath.tagPathAt(-1).inner;
1444
+
1445
+ break;
1446
+ }
1447
+
1448
+ case GapTag:
1449
+ case NullTag: {
1450
+ // if (getSigilTag(this.node)) throw new Error();
1451
+ if (this.tagPathAt(-1, 0).tag.type !== ReferenceTag) {
1452
+ throw new Error('Invalid location for NullTag');
1453
+ }
1454
+
1455
+ let node = buildNode(tag);
1456
+
1457
+ targetPath = this.advanceUnwrapped(node).path;
1458
+ // targetPath = targetPath.tagPathAt(-1).inner; // ?
1459
+
1460
+ break;
1461
+ }
1462
+
1463
+ case CloseNodeTag: {
1464
+ let { node } = targetPath;
1465
+
1466
+ let lastChild = targetPath.getChild(-1);
1467
+
1468
+ if (lastChild?.type === Property && !propertyIsFull(lastChild)) throw new Error();
1469
+
1470
+ let openTag = getOpenTag(node);
1471
+
1472
+ if (!openTag) throw new Error();
1473
+ if (openTag.value.selfClosing) throw new Error();
1474
+
1475
+ targetPath = targetPath.replaceWith(buildNode(Tags.push(getTags(node), tag)));
1476
+ break;
1477
+ }
1478
+
1479
+ case NullNode:
1480
+ case GapNode:
1481
+ case TreeNode: {
1482
+ let node = tag;
1483
+ let parentPath = targetPath;
1484
+ let { node: parentNode } = parentPath;
1485
+
1486
+ let tagPath = TagPath.from(parentPath, -1);
1487
+
1488
+ if (isMultiFragment(node)) {
1489
+ let existingProperty = tagPath;
1490
+
1491
+ while (existingProperty?.tag.type === AttributeDefinition) {
1492
+ existingProperty = existingProperty.previousSibling.propertyPath;
1493
+ }
1494
+
1495
+ if (
1496
+ existingProperty.tag.type !== OpenNodeTag &&
1497
+ existingProperty.tag &&
1498
+ !propertyIsFull(existingProperty.tag)
1499
+ ) {
1500
+ throw new Error();
1501
+ }
1502
+
1503
+ let newPath = Path.from(buildNode(this.node.value.tags));
1504
+
1505
+ for (let tag of Tags.traverse(node.value.children)) {
1506
+ if (tag.type === Property) {
1507
+ newPath = newPath.advance(tag);
1508
+ } else {
1509
+ newPath = newPath.advanceUnwrapped(tag).path.atDepth(0);
1510
+ }
1511
+ }
1512
+
1513
+ targetPath = this.replaceWith(newPath.node);
1514
+ break;
1515
+ }
1516
+
1517
+ let shift = tagPath.tag.type === Property ? tagPath.tag.value.shift : undefined;
1518
+ let { held } = this;
1519
+
1520
+ if (held) {
1521
+ // let heldProperty = this.tagPathAt(-2, 2).tag.value;
1522
+ let matchesHeld = isGapNode(node);
1523
+ if (getRoot(node)) {
1524
+ matchesHeld =
1525
+ matchesHeld ||
1526
+ isGapNode(node) ||
1527
+ held.node === node ||
1528
+ held.node === (getOriginalFirstNode(getRoot(node)) || held.node);
1529
+ if (!matchesHeld) {
1530
+ // TODO We're passing by the node that shifted if it's a cover!
1531
+ throw new Error();
1532
+ }
1533
+ }
1534
+ }
1535
+
1536
+ if (propertyIsFull(tagPath.tag)) {
1537
+ let tags = [null, [], node];
1538
+ let property = buildChild(Property, buildProperty(tags, shift));
1539
+
1540
+ targetPath = this.replaceWith(buildNode(Tags.push(parentNode.value.tags, property)));
1541
+ } else {
1542
+ let { 0: firstTag, 1: bindingTags = [] } = tagPath.tag.value.tags;
1543
+ let tags = [firstTag, bindingTags, node];
1544
+ let property = buildChild(Property, buildProperty(tags, shift));
1545
+
1546
+ targetPath = this.replaceWith(
1547
+ buildNode(Tags.replaceAt(tagPath.tagsIndex, parentNode.value.tags, property)),
1548
+ );
1549
+ }
1550
+
1551
+ break;
1552
+ }
1553
+
1554
+ case AttributeDefinition: {
1555
+ if (this.held) throw new Error('invalid place for an attribute binding');
1556
+
1557
+ let lastChild = this.getChild(-1);
1558
+
1559
+ if (lastChild?.type === Property && !propertyIsFull(lastChild)) throw new Error();
1560
+
1561
+ // add undefined attributes from value
1562
+ let { node } = this;
1563
+ if (tag.type !== AttributeDefinition) throw new Error();
1564
+
1565
+ let { path, value } = tag.value;
1566
+ let openTag = getOpenTag(node);
1567
+ let { attributes } = node.value;
1568
+
1569
+ if (!objHas(attributes, path) && objGet(attributes, path) !== undefined)
1570
+ throw new Error('Can only define undefined attributes');
1571
+
1572
+ if (value === undefined) throw new Error('cannot define attribute to undefined');
1573
+
1574
+ let { flags, name } = openTag.value;
1575
+ attributes = immSet(attributes, path, value);
1576
+ let newOpenTag = buildOpenNodeTag(flags, name, null, attributes);
1577
+
1578
+ targetPath = this.replaceWith(
1579
+ buildNode(Tags.push(Tags.replaceAt(0, getTags(node), newOpenTag), tag)),
1580
+ );
1581
+ break;
1582
+ }
1583
+
1584
+ case ShiftTag: {
1585
+ let lastPropertyTag = this.tagPathAt(-1).tag;
1586
+
1587
+ if (lastPropertyTag.type !== Property) throw new Error();
1588
+
1589
+ let { reference, shift: heldShift } = lastPropertyTag.value;
1590
+
1591
+ if (!reference) {
1592
+ ({ reference } = this.tagPathAt(-1 - (heldShift?.index ?? 0)).tag.value);
1593
+ }
1594
+
1595
+ if (!reference.flags.expression && reference.type !== '_') throw new Error();
1596
+
1597
+ let shift = heldShift
1598
+ ? buildShift(heldShift.index + 1, heldShift.height + 1)
1599
+ : buildShift(1, 3);
1600
+
1601
+ let property = buildChild(Property, buildProperty([tag], shift));
1602
+
1603
+ targetPath = this.replaceWith(buildNode(Tags.push(this.node.value.tags, property)));
1604
+ break;
1605
+ }
1606
+
1607
+ case LiteralTag:
1608
+ if (typeof tag.value !== 'string') throw new Error();
1609
+
1610
+ targetPath = this.replaceWith(buildNode(Tags.push(this.node.value.tags, tag)));
1611
+ break;
1612
+
1613
+ default:
1614
+ throw new Error();
1615
+ }
1616
+
1617
+ return targetPath && TagPath.from(targetPath, -1, isNodeTag(tag) ? -1 : offsetForTag(tag));
1618
+ }
1619
+ };
1620
+
1621
+ Object.freeze(Path.prototype);
1622
+
1623
+ export const tagPathsAreEqual = (a, b) => {
1624
+ if (a == null || b == null) return b == a;
1625
+ return a.path.node === b.path.node && a.tagsIndex === b.tagsIndex;
1626
+ };
1627
+
1628
+ export class TagPath {
1629
+ static from(path, tagsIndex, propertyIndex) {
1630
+ let tags = getTags(path.node);
1631
+ let size = Tags.getSize(tags);
1632
+ let index = Number.isFinite(tagsIndex) && tagsIndex < 0 ? size + tagsIndex : tagsIndex;
1633
+
1634
+ let propertyIndex_ = typeof propertyIndex === 'number' ? [propertyIndex] : propertyIndex;
1635
+
1636
+ let propTag = Tags.getAt(index, tags);
1637
+
1638
+ if (
1639
+ [OpenNodeTag, CloseNodeTag, LiteralTag, AttributeDefinition, GapTag, NullTag].includes(
1640
+ propTag?.type,
1641
+ )
1642
+ ) {
1643
+ propertyIndex_ = [];
1644
+ }
1645
+
1646
+ let resolvedPropertyIndex =
1647
+ propTag && (propertyIndex_?.length ? Tags.findPath(propertyIndex_, propTag.value.tags) : []);
1648
+
1649
+ return resolvedPropertyIndex && new TagPath(path, index, resolvedPropertyIndex);
1650
+ }
1651
+
1652
+ static fromNode(node, tagsIndex, propertyIndex) {
1653
+ return TagPath.from(Path.from(node), tagsIndex, propertyIndex);
1654
+ }
1655
+
1656
+ static fromTag(openTag) {
1657
+ return TagPath.fromNode(buildNode(openTag), 0);
1658
+ }
1659
+
1660
+ constructor(path, tagsIndex, propertyIndex) {
1661
+ if (path == null) throw new Error();
1662
+ if (path.tag) throw new Error();
1663
+ if (!Number.isFinite(tagsIndex) || tagsIndex < 0) throw new Error();
1664
+ if (!isArray(propertyIndex)) throw new Error();
1665
+
1666
+ let propertyIndex_ = Object.freeze([...propertyIndex]);
1667
+
1668
+ let tag = Tags.getAt(tagsIndex, getTags(path.node));
1669
+
1670
+ if (propertyIndex_.length) {
1671
+ tag = Tags.getAt(propertyIndex_, tag.value.tags);
1672
+ }
1673
+
1674
+ if (tag == null || isArray(tag)) throw new Error();
1675
+
1676
+ for (let segment of propertyIndex_) {
1677
+ if (!isPlainObject(segment) || !(segment.index === 0 || segment.index > 0)) throw new Error();
1678
+ }
1679
+
1680
+ this.path = path;
1681
+ this.tagsIndex = tagsIndex;
1682
+ this.propertyIndex = propertyIndex_;
1683
+
1684
+ this.node = path.node;
1685
+ this.tag = tag;
1686
+
1687
+ if (tag.type === Property && propertyIndex_.length) throw new Error();
1688
+
1689
+ freeze(this);
1690
+ }
943
1691
 
944
1692
  get child() {
945
1693
  return this.tag;
946
1694
  }
947
1695
 
1696
+ get parentNode() {
1697
+ return this.path.parentNode;
1698
+ }
1699
+
1700
+ get depth() {
1701
+ return this.path.depth;
1702
+ }
1703
+
948
1704
  siblingAt(index) {
949
1705
  return TagPath.from(this.path, index);
950
1706
  }
951
1707
 
1708
+ mapOnto(rootNode) {
1709
+ let path = this.path.mapOnto(rootNode);
1710
+
1711
+ return TagPath.from(path, this.tagsIndex, this.propertyIndex);
1712
+ }
1713
+
1714
+ get referenceTagPath() {
1715
+ let path = TagPath.from(this.path, this.tagsIndex, 0);
1716
+ if (path && path.propertyPath) {
1717
+ let { shift } = path.propertyPath.tag.value;
1718
+ if (shift) {
1719
+ path = TagPath.from(this.path, this.tagsIndex - shift.index, 0);
1720
+ }
1721
+ }
1722
+ return path?.tag.type === ReferenceTag ? path : null;
1723
+ }
1724
+
1725
+ get nextProperty() {
1726
+ let { path, tagsIndex } = this;
1727
+
1728
+ let nextChild = TagPath.from(path, tagsIndex + 1);
1729
+
1730
+ return nextChild?.tag.type === Property ? nextChild : null;
1731
+ }
1732
+
952
1733
  get nextSibling() {
953
- const { path, tagsIndex, wrapperIndex } = this;
1734
+ let { path, tagsIndex, propertyIndex } = this;
1735
+
1736
+ let propertyIndex0 = propertyIndex[0]?.index;
954
1737
 
955
- let propPath = new TagPath(path, tagsIndex);
1738
+ let nextChildIndex = tagsIndex === 0 ? 0 : tagsIndex - 1 + 1;
1739
+ let nextChild = path.getChild(nextChildIndex);
956
1740
 
957
- if (wrapperIndex != null && wrapperIndex + 1 < Tags.getSize(propPath.tag.value.tags)) {
958
- return new TagPath(path, tagsIndex, wrapperIndex + 1);
959
- } else if (tagsIndex + 1 < Tags.getSize(path.node.tags)) {
960
- return new TagPath(path, tagsIndex + 1, 0);
1741
+ if (propertyIndex0 == null && !nextChild) {
1742
+ return this.tag.type === CloseNodeTag ? null : path.closeTagPath;
961
1743
  }
962
1744
 
963
- return TagPath.from(path, tagsIndex + 1, 0);
1745
+ switch (propertyIndex0) {
1746
+ case 0:
1747
+ case 1: {
1748
+ let childIndex = tagsIndex - 1;
1749
+ let bindingIndex = propertyIndex[1]?.index ?? -1;
1750
+ let binding = path.getBindingTagPath(childIndex, bindingIndex + 1);
1751
+ if (binding) return binding;
1752
+ return path.getNodeTagPath(childIndex);
1753
+ }
1754
+ case 2:
1755
+ default: {
1756
+ if (!nextChild || nextChild.type !== Property) {
1757
+ return path.tagPathAt(tagsIndex + 1);
1758
+ } else {
1759
+ let ref = path.getReferenceTagPath(nextChildIndex);
1760
+ if (ref) return ref;
1761
+ let binding = path.getBindingTagPath(nextChildIndex, 0);
1762
+ if (binding) return binding;
1763
+ return path.getNodeTagPath(nextChildIndex);
1764
+ }
1765
+ }
1766
+ }
1767
+ }
1768
+
1769
+ get previousSibling() {
1770
+ let { path, tagsIndex, propertyIndex } = this;
1771
+
1772
+ let propertyIndex0 = propertyIndex[0]?.index;
1773
+
1774
+ if (!tagsIndex) return null;
1775
+
1776
+ let prevChildIndex = tagsIndex === 0 ? 0 : tagsIndex - 1 - 1;
1777
+ let prevChild = path.getChild(prevChildIndex);
1778
+
1779
+ if (!prevChild) {
1780
+ return path.getOpenTagPath();
1781
+ }
1782
+
1783
+ if (propertyIndex0) {
1784
+ if (propertyIndex0 > 0) {
1785
+ let property = path.getChild(tagsIndex - 1);
1786
+ let bindingIndex = propertyIndex[1]?.index ?? property.value.bindings.length;
1787
+ let binding = path.getBindingTagPath(tagsIndex - 1, bindingIndex - 1);
1788
+ if (binding) return binding;
1789
+ }
1790
+
1791
+ let ref = path.getReferenceTagPath(tagsIndex - 1);
1792
+ if (ref) return ref;
1793
+ }
1794
+
1795
+ return prevChild && prevChild.type === Property
1796
+ ? path.getNodeTagPath(tagsIndex - 1 - 1)
1797
+ : prevChild;
964
1798
  }
965
1799
 
966
1800
  get next() {
967
- let { path, tagsIndex, wrapperIndex } = this;
1801
+ let { path, tagsIndex, propertyIndex } = this;
1802
+
1803
+ propertyIndex = [...propertyIndex];
968
1804
 
969
1805
  let leaving = false;
970
1806
 
971
1807
  for (;;) {
972
- let lastRef = Tags.getAt(tagsIndex, path.node.tags, 0);
973
- let tag = Tags.getAt(tagsIndex, path.node.tags);
974
- let wrapperTag = tag;
975
- let isInitialTag =
976
- path.node === this.path.node &&
977
- tagsIndex === this.tagsIndex &&
978
- wrapperIndex === this.wrapperIndex;
979
- let wasLeaving = leaving;
1808
+ let tag = Tags.getAt(tagsIndex, getTags(path.node));
1809
+ let propTag = tag;
1810
+ let lastRef = null;
980
1811
  leaving = false;
981
1812
 
982
1813
  if (!tag) return null;
983
1814
 
984
- wrapperIndex =
985
- tag.type === PropertyWrapper ? (wrapperIndex == null ? 0 : wrapperIndex) : null;
986
-
987
- if (wrapperIndex != null) {
988
- wrapperTag = tag.value.tags[wrapperIndex];
989
- }
1815
+ if (tag.type === Property) {
1816
+ if (propertyIndex.length === 0 && !leaving) {
1817
+ propertyIndex.push({ index: 0, node: tag.value.tags });
1818
+ }
990
1819
 
991
- // done
992
- if (!isInitialTag && wrapperTag.type !== Property) {
993
- return new TagPath(path, tagsIndex, wrapperIndex);
1820
+ if (propertyIndex != null) {
1821
+ propTag = Tags.getAt(propertyIndex, tag.value.tags);
1822
+ lastRef = Tags.getAt(0, tag.value.tags);
1823
+ }
994
1824
  }
995
1825
 
996
- // in
997
- if (wrapperTag.type === Property && !wasLeaving) {
998
- if (tag.value.tags[1]?.type === InitializerTag || !tag.value.property.node) {
1826
+ if (isArray(propTag)) {
1827
+ if (propTag.length) {
1828
+ // enter bindings array
1829
+ propertyIndex.push({ index: 0, node: propTag });
1830
+ propTag = propTag[propertyIndex[1].index];
999
1831
  } else {
1000
- // jump to gap tag?
1001
- let tag1;
1002
- if (
1003
- path.parent &&
1004
- tag.value.property.reference.flags.expression &&
1005
- (tagsIndex === 1 ||
1006
- (tagsIndex === 2 &&
1007
- (tag1 = Tags.getAt(1, path.node.tags)).type === PropertyWrapper &&
1008
- tag1.value.tags[1].type === InitializerTag)) &&
1009
- Tags.getAt(path.parentIndex, path.parent.node.tags, 0)?.type === ShiftTag
1010
- ) {
1011
- let shiftStack = tag.value.property.node.bounds[0];
1012
-
1013
- let gapNode = BTree.getAt(-1, shiftStack).node;
1832
+ propertyIndex = [{ index: propertyIndex[0].index + 1, node: tag }];
1833
+ }
1834
+ continue;
1835
+ }
1014
1836
 
1015
- path = new Path(path, gapNode, tagsIndex);
1016
- tagsIndex = 0;
1017
- continue;
1018
- }
1837
+ let isInitialTag =
1838
+ path.node === this.path.node &&
1839
+ tagsIndex === this.tagsIndex &&
1840
+ arraysEqual(propertyIndex, this.propertyIndex);
1019
1841
 
1020
- path = new Path(path, tag.value.property.node, tagsIndex);
1021
- tagsIndex = 0;
1022
- continue;
1023
- }
1842
+ // done
1843
+ if (!isInitialTag && !isNodeTag(propTag)) {
1844
+ return TagPath.from(path, tagsIndex, propertyIndex);
1024
1845
  }
1025
1846
 
1026
- tag;
1027
1847
  // shift
1028
- if (wrapperTag.type === BindingTag && lastRef.type === ShiftTag) {
1029
- let refIndex = tagsIndex - lastRef.value.index;
1848
+ if (propTag.type === BindingTag && lastRef.type === ShiftTag) {
1849
+ let lastProp = Tags.getAt(tagsIndex, path.tags);
1850
+ let { shift } = lastProp.value;
1851
+ let refIndex = tagsIndex - shift.index;
1030
1852
  if (refIndex < 0) throw new Error();
1031
- let refTag = Tags.getAt(refIndex, path.node.tags, 0);
1853
+ let refTag = Tags.getAt(refIndex, path.tags, 0);
1032
1854
 
1033
1855
  if (refTag.type !== ReferenceTag) throw new Error();
1034
1856
 
1035
- if (Tags.getAt(refIndex, path.node.tags, 1).type !== InitializerTag) {
1036
- path = new Path(path, tag.value.property.node, tagsIndex);
1857
+ let { node } = tag.value;
1858
+ if (!node) return null;
1037
1859
 
1038
- if (!path) return null;
1860
+ path = new Path(path, tagsIndex);
1039
1861
 
1040
- tagsIndex = 0;
1041
- continue;
1042
- }
1862
+ if (!path) return null;
1863
+
1864
+ tagsIndex = 0;
1865
+ propertyIndex = [];
1866
+ continue;
1043
1867
  }
1044
1868
 
1869
+ let { nextSibling } = this;
1870
+
1045
1871
  // over
1046
- if (tag.type === PropertyWrapper && wrapperIndex + 1 < tag.value.tags.length) {
1047
- wrapperIndex++;
1048
- continue;
1049
- } else if (path.node && tagsIndex + 1 < Tags.getSize(path.node.tags)) {
1050
- tagsIndex++;
1051
- wrapperIndex = 0;
1052
- continue;
1872
+ if (nextSibling) {
1873
+ ({ tagsIndex, propertyIndex } = nextSibling);
1874
+ // in
1875
+ if (isNodeTag(nextSibling.tag)) {
1876
+ let shiftPath = path;
1877
+
1878
+ while (
1879
+ shiftPath.parent &&
1880
+ !Tags.getAt(shiftPath.parentIndex - 1, shiftPath.parent.node.value.children).value.shift
1881
+ ) {
1882
+ shiftPath = shiftPath.parent;
1883
+ }
1884
+
1885
+ let isGap =
1886
+ path.parent &&
1887
+ tagsIndex === 1 &&
1888
+ shiftPath.parent &&
1889
+ Tags.getAt(shiftPath.parentIndex - 1, shiftPath.parent.node.value.children).value
1890
+ .shift &&
1891
+ tag.value.node ===
1892
+ Tags.getAt(shiftPath.parentIndex - 2, shiftPath.parent.node.value.children).value
1893
+ .node;
1894
+
1895
+ path = new Path(path, tagsIndex, isGap);
1896
+ tagsIndex = 0;
1897
+ propertyIndex = [];
1898
+ continue;
1899
+ } else {
1900
+ return nextSibling;
1901
+ }
1053
1902
  }
1054
1903
 
1055
1904
  // out
1056
1905
  if (path.parentIndex != null && path.parent) {
1057
1906
  do {
1058
1907
  if (
1059
- path.parent &&
1060
- Tags.getAt(path.parentIndex, path.parent.node.tags, 0)?.type === ShiftTag
1908
+ path.coverBoundary.parent &&
1909
+ Tags.getAt(
1910
+ [path.coverBoundary.parentIndex, 0],
1911
+ path.coverBoundary.parent.node.value.tags,
1912
+ )?.type === ShiftTag
1061
1913
  ) {
1914
+ // while
1062
1915
  tagsIndex =
1063
- Tags.getSize(path.parent.node.tags) > path.parentIndex + 1
1916
+ Tags.getSize(path.parent.node.value.tags) > path.parentIndex + 1
1064
1917
  ? path.parentIndex + 1
1065
1918
  : null;
1066
1919
  } else {
1067
1920
  tagsIndex = path.parentIndex + 1;
1068
1921
  }
1069
1922
 
1070
- wrapperIndex = 0;
1923
+ propertyIndex = [];
1071
1924
  path = path.parent;
1072
1925
  leaving = true;
1073
1926
 
@@ -1083,40 +1936,63 @@ export class TagPath {
1083
1936
  }
1084
1937
 
1085
1938
  get nextUnshifted() {
1086
- let { path, tagsIndex, wrapperIndex } = this;
1939
+ let { path, tagsIndex, propertyIndex } = this;
1940
+
1941
+ propertyIndex = [...propertyIndex];
1087
1942
 
1088
1943
  let leaving = false;
1089
1944
 
1090
1945
  for (;;) {
1091
- let tag = Tags.getAt(tagsIndex, path.node.tags);
1092
- let wrapperTag = tag;
1093
- let isInitialTag =
1094
- path.node === this.path.node &&
1095
- tagsIndex === this.tagsIndex &&
1096
- wrapperIndex === this.wrapperIndex;
1946
+ let tag = Tags.getAt(tagsIndex, getTags(path.node));
1947
+ let propTag = tag;
1097
1948
  let wasLeaving = leaving;
1098
1949
  leaving = false;
1099
1950
 
1100
1951
  if (!tag) return null;
1101
1952
 
1102
- wrapperIndex =
1103
- tag.type === PropertyWrapper ? (wrapperIndex == null ? 0 : wrapperIndex) : null;
1953
+ if (tag.type === Property) {
1954
+ if (propertyIndex.length === 0 && !leaving) {
1955
+ propertyIndex.push({ index: 0, node: tag.value.tags });
1956
+ }
1104
1957
 
1105
- if (wrapperIndex != null) {
1106
- wrapperTag = tag.value.tags[wrapperIndex];
1958
+ if (propertyIndex != null) {
1959
+ propTag = Tags.getAt(propertyIndex, tag.value.tags);
1960
+ }
1961
+ }
1962
+
1963
+ if (isArray(propTag)) {
1964
+ if (propTag.length) {
1965
+ // enter bindings array
1966
+ propertyIndex.push({ index: 0, node: propTag });
1967
+ propTag = propTag[propertyIndex[1].index];
1968
+ } else {
1969
+ propertyIndex = [{ index: propertyIndex[0].index + 1, node: tag }];
1970
+ }
1971
+ continue;
1972
+ } else if (isNodeTag(propTag) && !leaving) {
1973
+ path = path.push(tagsIndex);
1974
+ tagsIndex = 0;
1975
+ propertyIndex = [];
1976
+ continue;
1107
1977
  }
1108
1978
 
1109
- if (wrapperTag.type === ReferenceTag && wrapperTag.value.flags.expression) {
1979
+ let isInitialTag =
1980
+ path.node === this.path.node &&
1981
+ tagsIndex === this.tagsIndex &&
1982
+ arraysEqual(propertyIndex, this.propertyIndex);
1983
+
1984
+ if (!wasLeaving && propTag.type === ReferenceTag && propTag.value.flags.expression) {
1110
1985
  // move past shifts
1111
1986
 
1112
1987
  let shifts = 0;
1113
- let tag = Tags.getAt(tagsIndex + 1, path.node.tags);
1988
+ let shiftedTag = Tags.getAt(tagsIndex + 1, getTags(path.node));
1114
1989
 
1115
- while (tag?.type === PropertyWrapper && tag.value.tags[0].type === ShiftTag) {
1990
+ while (shiftedTag?.type === Property && shiftedTag.value.shift) {
1116
1991
  shifts++;
1117
- wrapperIndex = 1;
1118
- tag = Tags.getAt(tagsIndex + shifts + 1, path.node.tags);
1992
+ propertyIndex = [{ index: 1, node: shiftedTag.value.tags }];
1993
+ shiftedTag = Tags.getAt(tagsIndex + 1 + shifts, path.tags);
1119
1994
  }
1995
+
1120
1996
  if (shifts) {
1121
1997
  tagsIndex += shifts;
1122
1998
  continue;
@@ -1124,25 +2000,15 @@ export class TagPath {
1124
2000
  }
1125
2001
 
1126
2002
  // done
1127
- if (!isInitialTag && wrapperTag.type !== Property) {
1128
- return new TagPath(path, tagsIndex, wrapperIndex);
2003
+ if (!isInitialTag && !isNodeTag(propTag) && !isArray(propTag)) {
2004
+ return TagPath.from(path, tagsIndex, propertyIndex);
1129
2005
  }
1130
2006
 
1131
- // in
1132
- if (tag.type === PropertyWrapper) {
1133
- if (wrapperIndex + 1 < tag.value.tags.length) {
1134
- wrapperIndex++;
1135
- continue;
1136
- } else if (!wasLeaving && tag.value.tags[1] && tag.value.tags[1].type !== InitializerTag) {
1137
- path = path.push(tag.value.property.node, tagsIndex);
1138
- tagsIndex = 0;
1139
- continue;
1140
- }
1141
- }
2007
+ let { nextSibling } = TagPath.from(path, tagsIndex, propertyIndex);
1142
2008
 
1143
2009
  // over
1144
- if (path.node && tagsIndex + 1 < Tags.getSize(path.node.tags)) {
1145
- tagsIndex++;
2010
+ if (nextSibling) {
2011
+ ({ tagsIndex, propertyIndex } = nextSibling);
1146
2012
  continue;
1147
2013
  }
1148
2014
 
@@ -1153,6 +2019,7 @@ export class TagPath {
1153
2019
 
1154
2020
  path = path.parent;
1155
2021
  leaving = true;
2022
+ if (!path) return null;
1156
2023
  } while (tagsIndex == null);
1157
2024
 
1158
2025
  leaving = true;
@@ -1163,16 +2030,20 @@ export class TagPath {
1163
2030
  }
1164
2031
  }
1165
2032
 
1166
- get previousSibling() {
1167
- const { path, tagsIndex, wrapperIndex } = this;
2033
+ get previous() {
2034
+ throw new Error('not implemented');
2035
+ }
1168
2036
 
1169
- if (wrapperIndex > 0) {
1170
- return new TagPath(path, tagsIndex, wrapperIndex - 1);
1171
- }
2037
+ get previousUnshifted() {
2038
+ throw new Error('not implemented');
2039
+ }
1172
2040
 
1173
- const child = tagsIndex - 1 < 0 ? null : BTree.getAt(tagsIndex - 1, path.node.tags);
2041
+ get propertyPath() {
2042
+ let { path, tagsIndex } = this;
1174
2043
 
1175
- return child && TagPath.from(path, tagsIndex - 1, -1);
2044
+ let tag = Tags.getAt(tagsIndex, getTags(path.node));
2045
+
2046
+ return tag.type === Property ? path.tagPathAt(tagsIndex) : null;
1176
2047
  }
1177
2048
 
1178
2049
  get innerNode() {
@@ -1180,36 +2051,31 @@ export class TagPath {
1180
2051
  }
1181
2052
 
1182
2053
  get inner() {
1183
- let { path, tagsIndex, tag, previousSibling } = this;
1184
-
1185
- let refPath = TagPath.from(path, tagsIndex, 0);
2054
+ let { tagsIndex, tag } = this;
1186
2055
 
1187
- if (tag.type !== PropertyWrapper) {
1188
- return null;
1189
- }
1190
-
1191
- let shiftPath = null;
1192
- if (refPath.tag.type === ShiftTag) {
1193
- shiftPath = refPath;
1194
- throw new Error('not implemented');
1195
- refPath = TagPath.from(this.path, this.tagsIndex - refPath.tag.value.index - 2);
2056
+ let node;
2057
+ switch (tag.type) {
2058
+ case Property: {
2059
+ ({ node } = tag.value);
2060
+ break;
2061
+ }
2062
+ case TreeNode: {
2063
+ node = tag;
2064
+ break;
2065
+ }
2066
+ default:
2067
+ throw new Error();
1196
2068
  }
1197
-
1198
- if (refPath.tag.type !== ReferenceTag) throw new Error();
1199
-
1200
- const { type, name } = refPath.tag.value;
1201
-
1202
- return this.path.get([
1203
- buildFullPathSegment(
1204
- type,
1205
- name,
1206
- getChildPropertyIndex(refPath.path.node, refPath.tagsIndex),
1207
- shiftPath?.tag.value.index,
1208
- ),
1209
- ]);
2069
+ return node && this.path.push(tagsIndex);
1210
2070
  }
1211
2071
 
1212
2072
  equalTo(tagPath) {
1213
- return this.node === tagPath.node && this.tagsIndex === tagPath.tagsIndex;
2073
+ return (
2074
+ this.node === tagPath.node &&
2075
+ this.tagsIndex === tagPath.tagsIndex &&
2076
+ arraysEqual(this.propertyIndex, tagPath.propertyIndex)
2077
+ );
1214
2078
  }
1215
2079
  }
2080
+
2081
+ Object.freeze(TagPath.prototype);