@bablr/agast-helpers 0.10.9 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/path.js CHANGED
@@ -1,4 +1,4 @@
1
- import * as BTree from '@bablr/agast-helpers/btree';
1
+ import * as BList from './b-list.js';
2
2
  import * as Tags from './tags.js';
3
3
  import {
4
4
  ReferenceTag,
@@ -14,28 +14,43 @@ import {
14
14
  TreeNode,
15
15
  NullNode,
16
16
  GapNode,
17
- Document,
18
17
  } from './symbols.js';
19
18
  import {
20
19
  immSet,
21
- isPlainObject,
22
20
  isString,
23
21
  has as objHas,
24
22
  get as objGet,
25
23
  isObject,
24
+ isArray,
26
25
  isSymbol,
26
+ recordValues,
27
+ freezeClass,
27
28
  } from './object.js';
28
29
  import {
29
- buildReferenceTag,
30
30
  buildChild,
31
31
  buildProperty,
32
- buildGapTag,
33
32
  buildNullTag,
34
33
  buildOpenNodeTag,
35
34
  buildShift,
36
35
  buildPropertyTag,
37
36
  buildPathFrame,
37
+ buildBounds,
38
+ parseOpenNodeTag,
39
+ parseTag,
40
+ parseTagType,
41
+ buildTag,
42
+ buildFullOpenNodeTag,
43
+ tokenFlags,
44
+ parseObject,
38
45
  } from './builders.js';
46
+ import { printObject, printTag } from './print.js';
47
+ import { arrayValues, map } from './iterable.js';
48
+ import { freezeRecord } from '@bablr/record';
49
+
50
+ let { hasOwn, freeze } = Object;
51
+ let { getTags } = Tags;
52
+
53
+ let arrayLast = (arr) => arr[arr.length - 1];
39
54
 
40
55
  export const incrementShift = (shift) => {
41
56
  if (!shift) return buildShift(1, 3);
@@ -43,74 +58,82 @@ export const incrementShift = (shift) => {
43
58
  return buildShift(shift.index + 1, shift.height + 1);
44
59
  };
45
60
 
46
- export const finalizeNode = (node) => {
47
- freeze(node);
48
- freeze(node.value);
49
-
50
- return node;
61
+ export const stringName = (name) => {
62
+ switch (typeof name) {
63
+ case 'symbol':
64
+ return name.description;
65
+ case 'string':
66
+ return name;
67
+ case 'null':
68
+ case 'undefined':
69
+ return null;
70
+ default:
71
+ throw new Error();
72
+ }
51
73
  };
52
74
 
53
75
  export const buildNode = (tags) => {
54
76
  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]);
77
+ if (!isArray(tags) && !isString(tags) && !isSymbol(tags.type)) throw new Error();
78
+ let tags_ = isArray(tags) ? tags : Tags.fromValues([printTag(tags)]);
57
79
 
58
80
  if (!Number.isFinite(tags_[0])) throw new Error();
59
81
  if (!isArray(tags_[1]) || !tags_[1].length) throw new Error();
60
82
 
61
83
  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
- });
90
- } else {
91
- throw new Error();
84
+
85
+ switch (parseTagType(openTag)) {
86
+ case GapTag:
87
+ return buildTag(GapNode, { tags: Tags.fromValues([openTag]) });
88
+ case NullTag:
89
+ return buildTag(NullNode, { tags: Tags.fromValues([openTag]) });
90
+ case OpenNodeTag: {
91
+ let { flags, type, name, attributes } = parseOpenNodeTag(openTag).value;
92
+
93
+ let children = tags_[1][1] || Tags.fromValues([]);
94
+
95
+ if (!Tags.isNode(children)) throw new Error();
96
+
97
+ return freezeRecord({
98
+ type: TreeNode,
99
+ value: freezeRecord({
100
+ flags,
101
+ type,
102
+ name,
103
+ attributes: parseObject(attributes),
104
+ tags: tags_,
105
+ children,
106
+ bounds: buildBoundsFromTags(tags_),
107
+ }),
108
+ });
109
+ }
110
+ default:
111
+ throw new Error();
92
112
  }
93
113
  };
94
114
 
95
- export const buildBoundsFromTags = (tags) => {
96
- let gapFrame = buildPathFrame(
97
- buildPropertyTag([buildReferenceTag(), [], buildNode(buildGapTag())]),
98
- null,
99
- true,
100
- );
115
+ let gapFrame = buildPathFrame(
116
+ buildPropertyTag(
117
+ BList.fromValues([Tags.fromValues([]), Tags.fromValues([]), buildNode('<//>')], 1),
118
+ ),
119
+ null,
120
+ true,
121
+ );
101
122
 
102
- let bounds = [BTree.fromValues([gapFrame]), BTree.fromValues([gapFrame])];
123
+ export const buildBoundsFromTags = (tags) => {
124
+ let leading = BList.fromValues([gapFrame]);
125
+ let trailing = leading;
103
126
  let firstProperty, lastProperty;
104
127
  let firstPropertyIndex, lastPropertyIndex;
105
128
 
106
- if (Tags.getAt(-1, tags)?.type !== CloseNodeTag) {
107
- return freeze(bounds);
129
+ if (parseTagType(Tags.getAt(-1, tags)) !== CloseNodeTag) {
130
+ return buildBounds(leading, trailing);
108
131
  }
109
132
 
110
133
  for (let i = 1; i < Tags.getSize(tags); i++) {
111
134
  let tag = Tags.getAt(i, tags);
112
135
 
113
- if (tag.type === Property && !isNullNode(tag.value.node)) {
136
+ if (parseTagType(tag) === Property && !isNullNode(tag.value.node)) {
114
137
  firstProperty = tag;
115
138
  firstPropertyIndex = i;
116
139
  break;
@@ -120,107 +143,90 @@ export const buildBoundsFromTags = (tags) => {
120
143
  for (let i = Tags.getSize(tags) - 2; i >= 0; i--) {
121
144
  let tag = Tags.getAt(i, tags);
122
145
 
123
- if (tag.type === Property && !isNullNode(tag.value.node)) {
146
+ if (parseTagType(tag) === Property && !isNullNode(tag.value.node)) {
124
147
  lastProperty = tag;
125
148
  lastPropertyIndex = i;
126
149
  break;
127
150
  }
128
151
  }
129
152
 
130
- let { 0: openStack, 1: closeStack } = bounds;
131
-
132
153
  if (firstProperty && firstProperty.value.node.type === TreeNode) {
133
- openStack = firstProperty.value.node.value.bounds[0];
154
+ leading = firstProperty.value.node.value.bounds.leading;
134
155
 
135
- let rootFrame = BTree.getAt(1, openStack);
156
+ let rootFrame = BList.getAt(1, leading);
136
157
 
137
- openStack = BTree.replaceAt(0, openStack, buildPathFrame(firstProperty));
158
+ leading = BList.replaceAt(0, buildPathFrame(firstProperty), leading);
138
159
  if (rootFrame) {
139
- openStack = BTree.replaceAt(
140
- 1,
141
- openStack,
142
- buildPathFrame(rootFrame.property, firstPropertyIndex),
143
- );
160
+ leading = BList.replaceAt(1, buildPathFrame(rootFrame.property, firstPropertyIndex), leading);
144
161
  }
145
- openStack = BTree.unshift(gapFrame, openStack);
162
+ leading = BList.unshift(gapFrame, leading);
146
163
  }
147
164
 
148
- if (lastProperty && lastProperty.value.type === TreeNode) {
165
+ if (lastProperty && lastProperty.value.node.type === TreeNode) {
149
166
  if (!lastProperty.value.node) throw new Error();
150
167
 
151
- closeStack = lastProperty.value.node.value.bounds[1];
168
+ trailing = lastProperty.value.node.value.bounds.trailing;
152
169
 
153
- let rootFrame = BTree.getAt(1, openStack);
170
+ let rootFrame = BList.getAt(1, trailing);
154
171
 
155
- closeStack = BTree.replaceAt(0, closeStack, buildPathFrame(lastProperty));
172
+ trailing = BList.replaceAt(0, buildPathFrame(lastProperty), trailing);
156
173
  if (rootFrame) {
157
- closeStack = BTree.replaceAt(
174
+ trailing = BList.replaceAt(
158
175
  1,
159
- closeStack,
160
176
  buildPathFrame(rootFrame.property, lastPropertyIndex),
177
+ trailing,
161
178
  );
162
179
  }
163
- closeStack = BTree.unshift(gapFrame, closeStack);
180
+ trailing = BList.unshift(gapFrame, trailing);
164
181
  }
165
182
 
166
- bounds[0] = openStack;
167
- bounds[1] = closeStack;
168
-
169
- return freeze(bounds);
183
+ return buildBounds(leading, trailing);
170
184
  };
171
185
 
172
186
  export const isNode = (value) => {
173
- return [TreeNode, NullNode, GapNode].includes(value?.type);
187
+ return isObject(value) && [TreeNode, NullNode, GapNode].includes(value?.type);
174
188
  };
175
189
 
176
190
  export const endsNode = (tag) => {
177
- return (
178
- [CloseNodeTag, NullTag, GapTag].includes(tag.type) ||
179
- (tag.type === OpenNodeTag && tag.value.selfClosing)
180
- );
191
+ let tagType = parseTagType(tag);
192
+ if ([CloseNodeTag, NullTag, GapTag].includes(tagType)) return true;
193
+
194
+ return tagType === OpenNodeTag ? parseTag(tag).value.selfClosing : false;
181
195
  };
182
196
 
183
197
  export const propertyIsFull = (tag) => {
184
- if (tag.type !== Property) return true;
198
+ if (isString(tag) || tag.type !== Property) return true;
185
199
 
186
200
  let { tags } = tag.value;
187
201
 
188
- return tags.length === 3;
202
+ return tags[1].length === 3;
189
203
  };
190
204
 
191
205
  export const nodeIsComplete = (node) => {
192
- let sigilTag = getOpenTag(node);
193
- return sigilTag.type !== OpenNodeTag || sigilTag.value.selfClosing ? true : !!getCloseTag(node);
194
- };
195
-
196
- export const getTags = (node) => {
197
- switch (node.type) {
198
- case TreeNode:
199
- return node.value.tags;
200
- case NullNode:
201
- return node.value.tags;
202
- case GapNode:
203
- return node.value.tags;
204
- case Document:
205
- return getTags(node.value.tree);
206
- default:
207
- throw new Error();
208
- }
206
+ let sigilTag = parseTag(getOpenTag(node));
207
+ return sigilTag.type !== OpenNodeTag || sigilTag.value.selfClosing
208
+ ? true
209
+ : !!getCloseNodeTag(node);
209
210
  };
210
211
 
211
212
  export const offsetForTag = (tag) => {
212
- switch (tag.type) {
213
+ switch (parseTagType(tag)) {
213
214
  case ReferenceTag:
214
215
  case ShiftTag:
215
216
  return [0];
216
217
  case BindingTag:
217
218
  return [1, -1];
219
+ case TreeNode:
220
+ case NullNode:
221
+ case GapNode:
222
+ return [2];
218
223
  default:
219
224
  return [];
220
225
  }
221
226
  };
222
227
 
223
228
  export const isNodeTag = (tag) => {
229
+ if (isString(tag)) return false;
224
230
  switch (tag.type) {
225
231
  case TreeNode:
226
232
  case NullNode:
@@ -259,13 +265,13 @@ export const getRootProperty = (node) => {
259
265
  if (node == null || !isCover(node)) {
260
266
  return null;
261
267
  }
262
- let idx = getPropertyTagsIndex(node, '_', null);
268
+ let idx = Tags.getIndex(buildFullPathSegment('_', null), node.value.tags);
263
269
 
264
270
  if (idx == null) return null;
265
271
 
266
272
  let tag = Tags.getAt(idx, getTags(node));
267
273
 
268
- if (tag.type !== Property) throw new Error();
274
+ if (isString(tag) || tag.type !== Property) throw new Error();
269
275
  return tag;
270
276
  };
271
277
 
@@ -288,15 +294,15 @@ export const getSigilTag = (node) => {
288
294
 
289
295
  export const getOpenTag = (node) => {
290
296
  let tag = Tags.getValues(getTags(node))[0];
291
- return tag.type === OpenNodeTag ? tag : null;
297
+ return parseTagType(tag) === OpenNodeTag ? tag : null;
292
298
  };
293
299
 
294
- export const getCloseTag = (node) => {
300
+ export const getCloseNodeTag = (node) => {
295
301
  return Tags.getValues(getTags(node))[2];
296
302
  };
297
303
 
298
304
  export const isNullNode = (node) => {
299
- return node && Tags.getAt(0, getTags(node)).type === NullTag;
305
+ return node && parseTagType(Tags.getAt(0, getTags(node))) === NullTag;
300
306
  };
301
307
 
302
308
  export const isCover = (node) => {
@@ -316,197 +322,17 @@ export const isMultiFragment = (node) => {
316
322
  };
317
323
 
318
324
  export const isGapNode = (node) => {
319
- return node && Tags.getAt(0, getTags(node)).type === GapTag;
325
+ return node && parseTagType(Tags.getAt(0, getTags(node))) === GapTag;
320
326
  };
321
327
 
322
328
  export const isStubNode = (node) => {
323
- return node && [GapTag, NullTag].includes(Tags.getAt(0, getTags(node)).type);
329
+ return node && [GapTag, NullTag].includes(parseTagType(Tags.getAt(0, getTags(node))));
324
330
  };
325
331
 
326
332
  export const isStubTag = (tag) => {
327
- return [GapTag, NullTag].includes(tag.type);
328
- };
329
-
330
- export const getChildPropertyIndex = (agAstNode, tagsIndex) => {
331
- let child = Tags.getAt(tagsIndex, agAstNode.value.tags);
332
-
333
- if (child.type !== Property) return null;
334
-
335
- let tag = child.value.tags[0];
336
-
337
- let refIndex = tag.type === ShiftTag ? tagsIndex - tag.value.index : tagsIndex;
338
-
339
- let stack = Tags.findPath(refIndex, agAstNode.value.tags);
340
- let { node, index: leafIdx } = stack.value;
341
- let leaf = Tags.getAt(leafIdx, node);
342
-
343
- if (leaf.type !== Property) {
344
- return null;
345
- }
346
-
347
- let leafRefTag = leaf.value.tags[0];
348
-
349
- if (leafRefTag.type !== ReferenceTag) return null;
350
-
351
- let { name, flags } = leafRefTag.value;
352
- let count = -1;
353
-
354
- if (!name) throw new Error();
355
-
356
- if (!flags.array) return null;
357
-
358
- for (let i = leafIdx; i >= 0; i--) {
359
- let value = Tags.getAt(i, node);
360
- if (value.type === Property) {
361
- let firstTag = value.value.tags[0];
362
-
363
- if (firstTag.type === ReferenceTag && firstTag.value.name === name) {
364
- count++;
365
- }
366
- }
367
- }
368
- stack = stack.pop();
369
-
370
- if (!stack.size) return count - 1;
371
-
372
- ({ node, index: leafIdx } = stack.value);
373
-
374
- do {
375
- for (let i = leafIdx - 1; i >= 0; i--) {
376
- let value = Tags.getValues(node)[i];
377
-
378
- if (Array.isArray(value)) {
379
- let childNode = value;
380
- let { names } = Tags.getSums(childNode);
381
-
382
- let res;
383
- if ((res = Tags.getAtName(name, names))) {
384
- count += res;
385
- }
386
- }
387
- }
388
- stack = stack.pop();
389
- if (stack.size) {
390
- ({ node, index: leafIdx } = stack.value);
391
- }
392
- } while (stack.size);
393
-
394
- // the initializer doesn't matter to us
395
- // also we're going from fenceposts to gaps
396
- return count - 1;
333
+ return [GapTag, NullTag].includes(parseTagType(tag));
397
334
  };
398
335
 
399
- export const getPropertyTagsIndex = (agAstNode, type, name, index, shiftIndex) => {
400
- let firstPropsIndex = __getPropertyTagsIndex(agAstNode, type, name, 0);
401
- let prop = firstPropsIndex == null ? null : Tags.getAt(firstPropsIndex, getTags(agAstNode));
402
- let ref = prop?.value.reference;
403
- let sums = Tags.getSums(agAstNode.value.tags);
404
- let counts = name ? sums.names : sums.types;
405
-
406
- if (ref?.flags.array) {
407
- if (index < 0) {
408
- index = Tags.getAtName(type, counts) + index;
409
- } else if (index == null) {
410
- index = Tags.getAtName(name, counts) - 1;
411
-
412
- if (index < 0) return null;
413
- }
414
- }
415
-
416
- let parentIndex = __getPropertyTagsIndex(agAstNode, type, name, index ?? 0);
417
-
418
- if (parentIndex != null && ref?.flags.expression) {
419
- let shiftIndex_ = shiftIndex == null ? -1 : shiftIndex;
420
- if (shiftIndex_ < 0) {
421
- let shifts = 0;
422
- // TODO speed this up for deeply nested shifts
423
- // algorithm: make big jump forward, then look at shift index to see if we overshot completely
424
- // works because we know the size of the thing and a bigger thing doesn't fit in a smaller one
425
- while (Tags.getAt(parentIndex + shifts, agAstNode.value.children)?.value.shift) {
426
- shifts++;
427
- }
428
-
429
- if (-shiftIndex_ > shifts + 1) return null;
430
-
431
- return parentIndex + shifts + shiftIndex_ + 1;
432
- } else {
433
- if (parentIndex + shiftIndex_ >= Tags.getSize(agAstNode.value.tags)) {
434
- return null;
435
- }
436
-
437
- return parentIndex + shiftIndex_;
438
- }
439
- }
440
-
441
- return parentIndex;
442
- };
443
-
444
- const __getPropertyTagsIndex = (agAstNode, type, name, index) => {
445
- let nameCount = 0;
446
- let node = Tags.getValues(agAstNode.value.tags)[1];
447
- let idx = -1;
448
-
449
- // drill into subtrees, passing over subtrees with too few references of the desired name
450
- outer: while (node) {
451
- let sums = Tags.getSums(node);
452
-
453
- if (!sums) return null;
454
-
455
- let valueNameCount =
456
- name != null ? Tags.getAtName(name, sums.names) : Tags.getAtName(type, sums.types);
457
-
458
- if (nameCount + valueNameCount < index) {
459
- return null;
460
- }
461
-
462
- for (const value of Tags.getValues(node)) {
463
- if (!isArray(value)) {
464
- idx++;
465
- let tag = value;
466
- if (tag.type === Property) {
467
- let { tags, reference } = tag.value;
468
- if (tags[0].type === ReferenceTag) {
469
- if (
470
- (name != null && reference.name === name) ||
471
- (type != null && reference.type === type)
472
- ) {
473
- nameCount += 1;
474
- if (nameCount > index) {
475
- return idx + 1;
476
- }
477
- }
478
- }
479
- }
480
- } else {
481
- let valueSums = Tags.getSums(value);
482
- if (
483
- nameCount +
484
- (name != null
485
- ? Tags.getAtName(name, valueSums.names)
486
- : Tags.getAtName(type, valueSums.types)) >
487
- index
488
- ) {
489
- node = value;
490
- continue outer;
491
- } else {
492
- nameCount +=
493
- (name != null
494
- ? Tags.getAtName(name, valueSums.names)
495
- : Tags.getAtName(type, valueSums.types)) ?? 0;
496
- idx += Tags.getSize(value);
497
- }
498
- }
499
- }
500
-
501
- return null;
502
- }
503
-
504
- return null;
505
- };
506
-
507
- const { hasOwn, freeze } = Object;
508
- const { isArray } = Array;
509
-
510
336
  export const getOriginalFirstNode = (node) => {
511
337
  if (node.type !== TreeNode) return;
512
338
 
@@ -529,7 +355,10 @@ export const getFirstNodeProperty = (node) => {
529
355
  if (!ref.flags.expression) {
530
356
  return child;
531
357
  } else {
532
- let tagsIndex = getPropertyTagsIndex(node, ref.type, ref.name, 0, -1);
358
+ let tagsIndex = Tags.getIndex(
359
+ buildFullPathSegment(ref.type, ref.name, 0, -1),
360
+ node.value.tags,
361
+ );
533
362
  return Tags.getAt(tagsIndex, getTags(node), 2)?.value;
534
363
  }
535
364
  // is it shifted?
@@ -538,22 +367,6 @@ export const getFirstNodeProperty = (node) => {
538
367
  return null;
539
368
  };
540
369
 
541
- export const referenceIsSingular = (ref) => {
542
- return (ref.name && !['#', '@'].includes(ref.type)) || ref.type === '_';
543
- };
544
-
545
- export const referencesAreEqual = (a, b) => {
546
- return (
547
- a === b ||
548
- (a.type === b.type &&
549
- a.name === b.name &&
550
- a.flags.array === b.flags.array &&
551
- a.flags.intrinsic === b.flags.intrinsic &&
552
- a.flags.hasGap === b.flags.hasGap &&
553
- a.flags.expression === b.flags.expression)
554
- );
555
- };
556
-
557
370
  export const isDefined = (obj, key) => hasOwn(obj, key) && obj[key] !== undefined;
558
371
  export const isUndefined = (obj, key) => !hasOwn(obj, key) || obj[key] === undefined;
559
372
 
@@ -570,10 +383,8 @@ export const getProperty = (pathSegment, node) => {
570
383
 
571
384
  if (pathSegment == null) throw new Error('Bad path segment');
572
385
 
573
- let { name, type, index, shiftIndex } =
574
- typeof pathSegment === 'string' ? buildPathSegment(pathSegment) : pathSegment;
575
-
576
- let propIndex = getPropertyTagsIndex(node, type, name, index, shiftIndex);
386
+ let segment = typeof pathSegment === 'string' ? buildPathSegment(pathSegment) : pathSegment;
387
+ let propIndex = Tags.getIndex(segment, node.value.tags);
577
388
 
578
389
  if (propIndex == null) return null;
579
390
 
@@ -638,7 +449,12 @@ export const getOr = (defaultValue, path, node) => {
638
449
  return isNullNode(result) ? null : result;
639
450
  };
640
451
 
452
+ export const set = (path, value, node) => {
453
+ return Path.from(node).replaceAt(path, value).node;
454
+ };
455
+
641
456
  export function* list(name, node) {
457
+ if (isArray(name)) throw new Error('not supported');
642
458
  let count = countList(name, node);
643
459
 
644
460
  for (let i = 0; i < count; i++) {
@@ -651,10 +467,10 @@ export const countList = (name, node) => {
651
467
 
652
468
  if (name == null) throw new Error('Bad path');
653
469
 
654
- return Tags.getAtName(name, Tags.getSums(getTags(node)).names);
470
+ return Tags.findStats(name, Tags.getSums(getTags(node)).names);
655
471
  };
656
472
 
657
- export function* allTagPathsFor(range, options = {}) {
473
+ export function* allTagPathsFor(range, options = freeze({})) {
658
474
  if (range == null) return;
659
475
 
660
476
  if (range[0] && !(range[0] instanceof TagPath)) throw new Error();
@@ -666,7 +482,7 @@ export function* allTagPathsFor(range, options = {}) {
666
482
  let path = startPath;
667
483
 
668
484
  while (path) {
669
- if (path.inner && path.previousSibling.tag.type === ReferenceTag) {
485
+ if (path.inner && path.previousSibling.type === ReferenceTag) {
670
486
  path = TagPath.from(path.inner, 0);
671
487
  }
672
488
 
@@ -684,7 +500,7 @@ export function* allTagPathsFor(range, options = {}) {
684
500
 
685
501
  if (
686
502
  endPath &&
687
- path.tag.type === CloseNodeTag &&
503
+ path.type === CloseNodeTag &&
688
504
  gapPath &&
689
505
  gapPath.tagsIndex === endPath.tagsIndex &&
690
506
  gapPath.path.node === endPath.path.node
@@ -695,7 +511,7 @@ export function* allTagPathsFor(range, options = {}) {
695
511
  }
696
512
  }
697
513
 
698
- export function* allTagsFor(range, options = {}) {
514
+ export function* allTagsFor(range, options = freeze({})) {
699
515
  for (let path of allTagPathsFor(range, options)) {
700
516
  yield path.tag;
701
517
  }
@@ -786,51 +602,27 @@ export const getAttributes = (node) => {
786
602
  };
787
603
 
788
604
  export const isSelfClosingTag = (tag) => {
789
- return tag.type === OpenNodeTag
790
- ? tag.value.selfClosing
791
- : [NullTag, GapTag].includes(tag.type)
792
- ? true
793
- : false;
794
- };
795
-
796
- export let pathForTagPath = (tagPath) => {
797
- let refTagPath = tagPath;
798
- let isShift = tagPath.tag.type === ShiftTag;
799
-
800
- if (isShift) {
801
- refTagPath = tagPath.siblingAt(tagPath.tagsIndex - tagPath.tag.value.index);
802
- }
803
-
804
- let { type, name } = refTagPath.tag.value;
805
-
806
- if (refTagPath.tag.type !== ReferenceTag) throw new Error();
807
-
808
- let index = getChildPropertyIndex(tagPath.node, refTagPath.tagsIndex);
809
- let shiftIndex = isShift ? (tagPath.tag.value.index ?? 0) + 1 : null;
810
-
811
- return [{ type, name, index, shiftIndex }];
812
- };
813
-
814
- let getSigilTag_ = (tag) => {
815
- switch (tag.type) {
816
- case Property:
817
- return getTags(tag.value.property.node)[1][0];
818
- case TreeNode:
819
- case NullNode:
820
- case GapNode:
821
- return getTags(tag)[1][0];
822
- case OpenNodeTag:
823
- case GapTag:
824
- case NullTag:
825
- return tag;
826
- }
605
+ if (!tag) return false;
606
+ let tagType = parseTagType(tag);
607
+ if ([NullTag, GapTag].includes(tagType)) return true;
608
+ if (tagType !== OpenNodeTag) return false;
609
+ let tag_ = isString(tag) ? parseOpenNodeTag(tag) : tag;
610
+ return tag_.value.selfClosing;
827
611
  };
828
612
 
829
613
  const parents = new WeakMap();
830
614
 
831
615
  export const Path = class AgastPath {
832
616
  static from(node) {
833
- return node && new Path(null, buildPathFrame(buildPropertyTag([null, [], node])));
617
+ return (
618
+ node &&
619
+ new Path(
620
+ null,
621
+ buildPathFrame(
622
+ buildPropertyTag(BList.fromValues([Tags.fromValues([]), Tags.fromValues([]), node], 1)),
623
+ ),
624
+ )
625
+ );
834
626
  }
835
627
 
836
628
  static wrap(frames) {
@@ -841,17 +633,17 @@ export const Path = class AgastPath {
841
633
  return Path.from(buildNode(openTag));
842
634
  }
843
635
 
844
- static set(node, path, value) {
636
+ static set(path, value, node) {
845
637
  return Path.from(node).replaceAt(path, value).node;
846
638
  }
847
639
 
848
- static get(node, path) {
640
+ static get(path, node) {
849
641
  return Path.from(node).get(path).node;
850
642
  }
851
643
 
852
644
  constructor(parent, frame) {
853
645
  if (!frame) throw new Error();
854
- let frame_ = isArray(frame) ? BTree.getAt(-1, frame) : frame;
646
+ let frame_ = isArray(frame) ? BList.getAt(-1, frame) : frame;
855
647
  let { property, parentIndex, isGap } = frame_;
856
648
  let { node } = property.value;
857
649
 
@@ -868,13 +660,13 @@ export const Path = class AgastPath {
868
660
  parents.set(this, parent);
869
661
  }
870
662
 
871
- this.depth = parent ? parent.depth + 1 : isArray(frame) ? BTree.getSize(frame) - 1 : 0;
663
+ this.depth = parent ? parent.depth + 1 : isArray(frame) ? BList.getSize(frame) - 1 : 0;
872
664
  this.frame = frame_;
873
665
  this.frames = !parent
874
666
  ? isArray(frame)
875
667
  ? frame
876
- : BTree.fromValues([frame])
877
- : BTree.push(parent.frames, frame);
668
+ : BList.fromValues([frame])
669
+ : BList.push(frame, parent.frames);
878
670
 
879
671
  buildSkips(this);
880
672
 
@@ -888,18 +680,10 @@ export const Path = class AgastPath {
888
680
  throw new Error();
889
681
  }
890
682
 
891
- if (
892
- parentIndex != null &&
893
- !parent?.node.value.flags.token &&
894
- (!this.referenceTag || ![ReferenceTag, ShiftTag].includes(this.referenceTag.type))
895
- ) {
896
- throw new Error();
897
- }
898
-
899
683
  if (
900
684
  parent_ &&
901
685
  (isGap
902
- ? BTree.getAt(0, parent_.node.value.bounds[0]).property
686
+ ? BList.getAt(0, parent_.node.value.bounds.leading).property
903
687
  : parent_.tagPathAt(parentIndex).tag) !== property
904
688
  ) {
905
689
  throw new Error('Path not reachable');
@@ -914,12 +698,12 @@ export const Path = class AgastPath {
914
698
 
915
699
  push(tagsIndex, isGap) {
916
700
  let tag = isGap
917
- ? BTree.getAt(0, this.node.value.bounds[0]).property
701
+ ? BList.getAt(0, this.node.value.bounds.leading).property
918
702
  : tagsIndex != null
919
703
  ? Tags.getAt(tagsIndex, this.tags)
920
704
  : null;
921
705
 
922
- if (!tag || tag.type !== Property) {
706
+ if (!tag || parseTagType(tag) !== Property) {
923
707
  return null;
924
708
  }
925
709
 
@@ -940,7 +724,7 @@ export const Path = class AgastPath {
940
724
  return parent;
941
725
  }
942
726
 
943
- parent = new Path(null, BTree.pop(frames));
727
+ parent = new Path(null, BList.pop(frames));
944
728
 
945
729
  parents.set(this, parent);
946
730
 
@@ -959,6 +743,10 @@ export const Path = class AgastPath {
959
743
  return this.frame.property.value.node;
960
744
  }
961
745
 
746
+ get value() {
747
+ return this.node.value;
748
+ }
749
+
962
750
  get name() {
963
751
  let { node } = this;
964
752
  return node.type === TreeNode ? node.value.name : null;
@@ -986,6 +774,7 @@ export const Path = class AgastPath {
986
774
  getChild(childrenIndex) {
987
775
  let { node } = this;
988
776
  if (isStubNode(node)) return null;
777
+
989
778
  return Tags.getAt(childrenIndex, getChildren(node)) || null;
990
779
  }
991
780
 
@@ -999,7 +788,7 @@ export const Path = class AgastPath {
999
788
 
1000
789
  getPropertyTagPath(childrenIndex) {
1001
790
  let tagPath = this.tagPathAt(childrenIndex + 1);
1002
- return tagPath && tagPath.tag.type === Property ? tagPath : null;
791
+ return tagPath && tagPath.type === Property ? tagPath : null;
1003
792
  }
1004
793
 
1005
794
  getReferenceTagPath(childrenIndex) {
@@ -1025,12 +814,12 @@ export const Path = class AgastPath {
1025
814
 
1026
815
  get openTagPath() {
1027
816
  let tagPath = TagPath.from(this, 0);
1028
- return tagPath.tag.type === OpenNodeTag ? tagPath : null;
817
+ return tagPath.type === OpenNodeTag ? tagPath : null;
1029
818
  }
1030
819
 
1031
820
  get closeTagPath() {
1032
821
  let tagPath = TagPath.from(this, -1);
1033
- return tagPath.tag.type === CloseNodeTag ? tagPath : null;
822
+ return tagPath.type === CloseNodeTag ? tagPath : null;
1034
823
  }
1035
824
 
1036
825
  get openTag() {
@@ -1038,7 +827,7 @@ export const Path = class AgastPath {
1038
827
  }
1039
828
 
1040
829
  get open() {
1041
- return this.openTag.value;
830
+ return parseOpenNodeTag(this.openTag).value;
1042
831
  }
1043
832
 
1044
833
  get closeTag() {
@@ -1050,11 +839,12 @@ export const Path = class AgastPath {
1050
839
  }
1051
840
 
1052
841
  get bindingTags() {
1053
- return this.parentPropertyPath?.tag.value.tags[1];
842
+ return this.parentPropertyPath?.tag.value.tags[1][1];
1054
843
  }
1055
844
 
1056
845
  get reference() {
1057
- return this.referenceTag?.value ?? null;
846
+ let { referenceTag } = this;
847
+ return referenceTag && parseTag(referenceTag)?.value;
1058
848
  }
1059
849
 
1060
850
  get referenceTag() {
@@ -1080,8 +870,8 @@ export const Path = class AgastPath {
1080
870
  return null;
1081
871
  }
1082
872
  let path = TagPath.from(this.parent, this.parentIndex);
1083
- if (path.tag.type === ShiftTag) {
1084
- path = TagPath.from(this.parent, this.parentIndex - path.tag.value.index);
873
+ if (path.next?.type === ShiftTag) {
874
+ path = TagPath.from(this.parent, this.parentIndex - path.value.shift.index);
1085
875
  }
1086
876
  return path;
1087
877
  }
@@ -1096,7 +886,7 @@ export const Path = class AgastPath {
1096
886
 
1097
887
  get firstPropertyTagPath() {
1098
888
  let tagPath = this.tagPathAt(0);
1099
- while (tagPath && (!tagPath.propertyPath || isNullNode(tagPath.propertyPath.tag.value.node))) {
889
+ while (tagPath && (!tagPath.propertyPath || isNullNode(tagPath.propertyPath.value.node))) {
1100
890
  tagPath = tagPath.nextSibling;
1101
891
  if (tagPath?.propertyPath) {
1102
892
  tagPath = tagPath.propertyPath;
@@ -1107,7 +897,7 @@ export const Path = class AgastPath {
1107
897
 
1108
898
  get lastPropertyTagPath() {
1109
899
  let tagPath = this.tagPathAt(-1);
1110
- while (tagPath && (!tagPath.propertyPath || isNullNode(tagPath.propertyPath.tag.value.node))) {
900
+ while (tagPath && (!tagPath.propertyPath || isNullNode(tagPath.propertyPath.value.node))) {
1111
901
  tagPath = tagPath.previousSibling;
1112
902
  if (tagPath?.propertyPath) {
1113
903
  tagPath = tagPath.propertyPath;
@@ -1140,7 +930,9 @@ export const Path = class AgastPath {
1140
930
  while (!pathInst.node.value.name && pathInst.node.value.type === Symbol.for('_')) {
1141
931
  if (pathInst.depth) throw new Error();
1142
932
  skippedRootFragment = true;
1143
- pathInst = pathInst.push(getPropertyTagsIndex(pathInst.node, '_', null, 0));
933
+ pathInst = pathInst.push(
934
+ Tags.getIndex(buildFullPathSegment('_', null, 0), pathInst.node.value.tags),
935
+ );
1144
936
  }
1145
937
  }
1146
938
 
@@ -1160,12 +952,9 @@ export const Path = class AgastPath {
1160
952
 
1161
953
  if (i === 0 && seg.type === '_' && skippedRootFragment) continue;
1162
954
 
1163
- let tagsIndex = getPropertyTagsIndex(
1164
- pathInst.node,
1165
- seg.type,
1166
- seg.name,
1167
- seg.index,
1168
- seg.shiftIndex,
955
+ let tagsIndex = Tags.getIndex(
956
+ buildFullPathSegment(seg.type, seg.name, seg.index, seg.shiftIndex),
957
+ pathInst.node.value.tags,
1169
958
  );
1170
959
 
1171
960
  if (tagsIndex == null) return null;
@@ -1177,9 +966,9 @@ export const Path = class AgastPath {
1177
966
  }
1178
967
 
1179
968
  replaceWith(node, bindingTags) {
1180
- let bindings = bindingTags?.map((tag) => tag.value); // TODO improve here
969
+ let bindings = bindingTags?.map((tag) => parseTag(tag).value); // TODO improve here
1181
970
  if (bindings != null && !isArray(bindings)) throw new Error();
1182
- if (!isPlainObject(node)) throw new Error();
971
+ if (!isNode(node)) throw new Error();
1183
972
 
1184
973
  if (!node) throw new Error();
1185
974
 
@@ -1191,7 +980,7 @@ export const Path = class AgastPath {
1191
980
  let path = this.parent;
1192
981
  let replacementPath = [];
1193
982
 
1194
- let targetShift_ = this.parent?.parentProperty?.value.tags[0];
983
+ let targetShift_ = this.parent?.parentProperty?.value.tags[1][0];
1195
984
  let targetShift = targetShift_ && (targetShift_.type === ReferenceTag ? null : targetShift_);
1196
985
  let targetReference = this.reference;
1197
986
  let targetBindings = bindings == null ? this.bindings : bindings;
@@ -1206,24 +995,29 @@ export const Path = class AgastPath {
1206
995
 
1207
996
  let { tags, shift } = Tags.getAt(targetParentIndex, getTags(path.node)).value;
1208
997
 
1209
- let firstTag = tags[0];
998
+ let firstTag = tags[1][0];
1210
999
  let newFirstTag = firstTag;
1211
1000
  if (shift) {
1212
1001
  newFirstTag = buildChild(ReferenceTag, targetReference);
1213
1002
  }
1214
1003
 
1215
- let tags_ = [
1216
- firstTag,
1217
- targetBindings.map((binding) => buildChild(BindingTag, binding)),
1218
- value,
1219
- ];
1004
+ let tags_ = BList.fromValues(
1005
+ [
1006
+ firstTag,
1007
+ Tags.fromValues(
1008
+ map((binding) => buildChild(BindingTag, binding), arrayValues(targetBindings)),
1009
+ ),
1010
+ value,
1011
+ ],
1012
+ 1,
1013
+ );
1220
1014
 
1221
1015
  if (!held && !shift) {
1222
1016
  built = buildNode(
1223
1017
  Tags.replaceAt(
1224
1018
  targetParentIndex,
1225
- built.value.tags,
1226
1019
  buildChild(Property, buildProperty(tags_)),
1020
+ built.value.tags,
1227
1021
  ),
1228
1022
  );
1229
1023
  replacementPath.push(targetParentIndex);
@@ -1234,16 +1028,18 @@ export const Path = class AgastPath {
1234
1028
  let nextShift = nextProperty?.type === Property && nextProperty.value.shift;
1235
1029
 
1236
1030
  if (nextShift) {
1237
- let { reference, bindings } = BTree.getAt(-1, nextProperty.value.node.value.bounds[0])
1238
- .property.value;
1031
+ let { reference, bindings } = BList.getAt(
1032
+ -1,
1033
+ nextProperty.value.node.value.bounds.leading,
1034
+ ).property.value;
1239
1035
 
1240
1036
  path = path.push(i);
1241
1037
 
1242
1038
  built = buildNode(
1243
1039
  Tags.replaceAt(
1244
1040
  targetParentIndex - shift.index,
1245
- built.value.tags,
1246
1041
  buildChild(Property, buildProperty(tags_)),
1042
+ built.value.tags,
1247
1043
  ),
1248
1044
  );
1249
1045
 
@@ -1252,17 +1048,14 @@ export const Path = class AgastPath {
1252
1048
  built = buildNode(Tags.removeAt(targetParentIndex + 1, built.value.tags));
1253
1049
  }
1254
1050
 
1255
- tags_ = [newFirstTag, tags_[1], tags_[2]];
1051
+ tags_ = BList.fromValues([newFirstTag, tags_[1][1], tags_[1][2]], 1);
1256
1052
 
1257
1053
  targetShift = null;
1258
1054
  targetReference = reference;
1259
1055
  targetBindings = bindings;
1260
- targetParentIndex = getPropertyTagsIndex(
1261
- nextProperty.value.node,
1262
- reference.type,
1263
- reference.name,
1264
- 0,
1265
- 0,
1056
+ targetParentIndex = Tags.getIndex(
1057
+ buildFullPathSegment(reference.type, reference.name, 0, 0),
1058
+ nextProperty.value.node.value.tags,
1266
1059
  );
1267
1060
  held = null;
1268
1061
 
@@ -1272,8 +1065,8 @@ export const Path = class AgastPath {
1272
1065
  built = buildNode(
1273
1066
  Tags.replaceAt(
1274
1067
  targetParentIndex,
1275
- getTags(built),
1276
1068
  buildChild(Property, buildProperty(tags_, shift)),
1069
+ getTags(built),
1277
1070
  ),
1278
1071
  );
1279
1072
  replacementPath.push(targetParentIndex);
@@ -1283,7 +1076,7 @@ export const Path = class AgastPath {
1283
1076
  held = targetShift ? value : null;
1284
1077
  heldCount = held ? heldCount + 1 : 0;
1285
1078
 
1286
- targetShift_ = path?.parentProperty?.value.tags[0];
1079
+ targetShift_ = path?.parentProperty?.value.tags[1][0];
1287
1080
  targetShift = targetShift_ && (targetShift_.type === ReferenceTag ? null : targetShift_);
1288
1081
  targetReference = path.reference;
1289
1082
  targetBindings = path.bindings;
@@ -1295,7 +1088,131 @@ export const Path = class AgastPath {
1295
1088
  }
1296
1089
 
1297
1090
  replaceAt(path, node, bindings) {
1298
- return this.get(path).replaceWith(node, bindings).atDepth(0);
1091
+ return this.get(path).replaceWith(node, bindings).atDepth(this.depth);
1092
+ }
1093
+
1094
+ removeAt(path) {
1095
+ if (!this.depth && !path.length) {
1096
+ return null;
1097
+ }
1098
+
1099
+ let built = null;
1100
+ let lastPath = this.get(path);
1101
+ let path_ = lastPath.parent;
1102
+ let replacementPath = [];
1103
+
1104
+ let targetShift_ = lastPath.parent?.parentProperty?.value.tags[1][0];
1105
+ let targetShift = targetShift_ && (targetShift_.type === ReferenceTag ? null : targetShift_);
1106
+ let targetReference = lastPath.reference;
1107
+ let targetBindings = lastPath.bindings;
1108
+ let targetParentIndex = lastPath.parentIndex;
1109
+ let held = null;
1110
+ let heldCount = 0;
1111
+ let value;
1112
+
1113
+ while (path_) {
1114
+ value = built;
1115
+ built = buildNode(getTags(path_.node));
1116
+
1117
+ let { tags, shift } = Tags.getAt(targetParentIndex, getTags(path_.node)).value;
1118
+
1119
+ let firstTag = tags[1][0];
1120
+ let newFirstTag = firstTag;
1121
+ if (shift) {
1122
+ newFirstTag = buildChild(ReferenceTag, targetReference);
1123
+ }
1124
+
1125
+ let tags_ =
1126
+ value &&
1127
+ BList.fromValues(
1128
+ [
1129
+ firstTag,
1130
+ Tags.fromValues(
1131
+ map((binding) => buildChild(BindingTag, binding), arrayValues(targetBindings)),
1132
+ ),
1133
+ value,
1134
+ ],
1135
+ 1,
1136
+ );
1137
+
1138
+ if (!held && !shift) {
1139
+ if (tags_) {
1140
+ built = buildNode(
1141
+ Tags.replaceAt(
1142
+ targetParentIndex,
1143
+ buildChild(Property, buildProperty(tags_)),
1144
+ built.value.tags,
1145
+ ),
1146
+ );
1147
+ } else {
1148
+ built = buildNode(Tags.removeAt(targetParentIndex, built.value.tags));
1149
+ }
1150
+ replacementPath.push(targetParentIndex);
1151
+ } else {
1152
+ let i = targetParentIndex + 1;
1153
+ let nextProperty = Tags.getAt(i, built.value.tags);
1154
+
1155
+ let nextShift = nextProperty?.type === Property && nextProperty.value.shift;
1156
+
1157
+ if (nextShift) {
1158
+ let { reference, bindings } = BList.getAt(
1159
+ -1,
1160
+ nextProperty.value.node.value.bounds.leading,
1161
+ ).property.value;
1162
+
1163
+ path_ = path_.push(i);
1164
+
1165
+ built = buildNode(
1166
+ Tags.replaceAt(
1167
+ targetParentIndex - shift.index,
1168
+ buildChild(Property, buildProperty(tags_)),
1169
+ built.value.tags,
1170
+ ),
1171
+ );
1172
+
1173
+ targetParentIndex -= shift.index;
1174
+ for (let i = shift.index - 1; i >= 0; i--) {
1175
+ built = buildNode(Tags.removeAt(targetParentIndex + 1, built.value.tags));
1176
+ }
1177
+
1178
+ tags_ = BList.fromValues([newFirstTag, tags_[1][1], tags_[1][2]], 1);
1179
+
1180
+ targetShift = null;
1181
+ targetReference = reference;
1182
+ targetBindings = bindings;
1183
+ targetParentIndex = Tags.getIndex(
1184
+ buildFullPathSegment(reference.type, reference.name, 0, 0),
1185
+ nextProperty.value.node.value.tags,
1186
+ );
1187
+ held = null;
1188
+
1189
+ replacementPath.push(targetParentIndex);
1190
+ continue;
1191
+ } else {
1192
+ built = buildNode(
1193
+ Tags.replaceAt(
1194
+ targetParentIndex,
1195
+ buildChild(Property, buildProperty(tags_, shift)),
1196
+ getTags(built),
1197
+ ),
1198
+ );
1199
+ replacementPath.push(targetParentIndex);
1200
+ }
1201
+ }
1202
+
1203
+ held = targetShift ? value : null;
1204
+ heldCount = held ? heldCount + 1 : 0;
1205
+
1206
+ targetShift_ = path_?.parentProperty?.value.tags[1][0];
1207
+ targetShift = targetShift_ && (targetShift_.type === ReferenceTag ? null : targetShift_);
1208
+ targetReference = path_.reference;
1209
+ targetBindings = path_.bindings;
1210
+ targetParentIndex = path_.parentIndex;
1211
+ lastPath = path_;
1212
+ path_ = path_.parent;
1213
+ }
1214
+
1215
+ return Path.from(built);
1299
1216
  }
1300
1217
 
1301
1218
  mapOnto(rootNode) {
@@ -1321,19 +1238,19 @@ export const Path = class AgastPath {
1321
1238
  let { parent, node } = this;
1322
1239
  let sigilTag = getSigilTag(node);
1323
1240
 
1324
- return !parent && sigilTag && !!(isSelfClosingTag(sigilTag) || getCloseTag(node));
1241
+ return !parent && sigilTag && !!(isSelfClosingTag(sigilTag) || getCloseNodeTag(node));
1325
1242
  }
1326
1243
 
1327
1244
  get held() {
1328
1245
  let tagPath = this.tagPathAt(-1);
1329
1246
 
1330
1247
  // TODO is this safe
1331
- if (tagPath.tag.type !== Property) return null;
1248
+ if (tagPath.type !== Property) return null;
1332
1249
 
1333
- let { tags } = tagPath.tag.value;
1250
+ let { tags } = tagPath.value;
1334
1251
 
1335
- if (tags[0]?.type === ShiftTag && !tags[2]) {
1336
- return TagPath.from(tagPath.path, tagPath.tagsIndex - 1).tag.value;
1252
+ if (tags[1][0]?.type === ShiftTag && !tags[1][2]) {
1253
+ return TagPath.from(tagPath.path, tagPath.tagsIndex - 1).value;
1337
1254
  }
1338
1255
 
1339
1256
  // if we're not at the left side, we aren't held
@@ -1350,8 +1267,8 @@ export const Path = class AgastPath {
1350
1267
  for (;;) {
1351
1268
  let refPath = coverParent.propertyKeyTagPath;
1352
1269
 
1353
- if (refPath?.tag.type === ShiftTag) {
1354
- return TagPath.from(refPath.path, refPath.tagsIndex - 1).tag.value;
1270
+ if (refPath?.type === ShiftTag) {
1271
+ return TagPath.from(refPath.path, refPath.tagsIndex - 1).value;
1355
1272
  }
1356
1273
 
1357
1274
  coverParent = coverParent.parent;
@@ -1364,57 +1281,24 @@ export const Path = class AgastPath {
1364
1281
  advance(tag) {
1365
1282
  if (!tag) throw new Error();
1366
1283
 
1367
- let tagPath = this.tagPathAt(-1, 2);
1284
+ let tagPath = this.tagPathAt(-1, -1);
1368
1285
 
1369
- if (tagPath?.tag.type === Property ? tagPath?.nextSibling : tagPath?.next) throw new Error();
1286
+ if (tagPath?.type === Property ? tagPath?.nextSibling : tagPath?.next) throw new Error();
1370
1287
 
1371
1288
  if (this.done) throw new Error();
1372
1289
 
1373
1290
  let resultPath = this;
1374
1291
 
1375
- switch (tag.type) {
1376
- case BindingTag: {
1377
- if (!this.lastPropertyTagPath) {
1378
- resultPath = this.advanceUnwrapped(buildReferenceTag()).path;
1379
- }
1380
- break;
1381
- }
1382
-
1383
- case TreeNode:
1384
- case NullNode:
1385
- case GapNode:
1386
- case NullTag:
1387
- case GapTag:
1388
- case OpenNodeTag: {
1389
- let sigilTag = getSigilTag_(tag);
1390
-
1391
- if (sigilTag.type === OpenNodeTag && sigilTag.value.type === Symbol.for('__')) {
1392
- break;
1393
- }
1394
-
1395
- let lastTagPath = this.lastPropertyTagPath;
1396
- let newProperty = !lastTagPath || propertyIsFull(lastTagPath.tag);
1397
- let reference = lastTagPath?.referenceTagPath.tag.value;
1292
+ let tag_ = isNode(tag) ? tag : parseTag(tag);
1398
1293
 
1399
- // TODO revisit this condition
1400
- if (!getFlags(this.node)?.token || reference?.type === '@' || tag.type === GapTag) {
1401
- if (newProperty || !lastTagPath.tag.value.tags.length) {
1402
- resultPath = this.advanceUnwrapped(buildReferenceTag()).path;
1403
- }
1404
- }
1405
-
1406
- break;
1407
- }
1408
- }
1409
-
1410
- if (tag.type === Property) {
1411
- resultPath = Path.from(buildNode(Tags.push(getTags(resultPath.node), tag)));
1294
+ if (tag_.type === Property) {
1295
+ resultPath = Path.from(buildNode(Tags.push(tag, getTags(resultPath.node))));
1412
1296
  } else {
1413
- resultPath = resultPath.advanceUnwrapped(tag).path;
1297
+ resultPath = resultPath.advanceUnwrapped(printTag(tag)).path;
1414
1298
  }
1415
1299
 
1416
1300
  if (
1417
- (tag.type === CloseNodeTag || (tag.type === OpenNodeTag && tag.value.selfClosing)) &&
1301
+ (tag_.type === CloseNodeTag || (tag_.type === OpenNodeTag && tag_.value.selfClosing)) &&
1418
1302
  resultPath.depth
1419
1303
  ) {
1420
1304
  return resultPath.parent;
@@ -1424,14 +1308,13 @@ export const Path = class AgastPath {
1424
1308
  }
1425
1309
 
1426
1310
  advanceUnwrapped(tag) {
1427
- if (!tag) throw new Error();
1311
+ if (!isString(tag) && !isNode(tag)) throw new Error();
1312
+ let tag_ = isString(tag) ? parseTag(tag) : tag;
1428
1313
 
1429
1314
  let tagPath = this.tagPathAt(-1, [-1, -1]);
1430
1315
 
1431
1316
  if (
1432
- [TreeNode, NullNode, GapNode].includes(tagPath?.tag.type)
1433
- ? tagPath?.nextSibling
1434
- : tagPath?.next
1317
+ [TreeNode, NullNode, GapNode].includes(tagPath?.type) ? tagPath?.nextSibling : tagPath?.next
1435
1318
  )
1436
1319
  throw new Error();
1437
1320
 
@@ -1439,11 +1322,11 @@ export const Path = class AgastPath {
1439
1322
 
1440
1323
  let targetPath = tagPath && endsNode(tagPath.tag) ? this.parent : this;
1441
1324
 
1442
- switch (tag.type) {
1325
+ switch (tag_.type) {
1443
1326
  case ReferenceTag: {
1444
- let { type, name, flags } = tag.value;
1327
+ let { type, name, flags } = tag_.value;
1445
1328
 
1446
- if ([ReferenceTag, ShiftTag, BindingTag].includes(targetPath.tagPathAt(-1, -1)?.tag.type))
1329
+ if ([ReferenceTag, ShiftTag, BindingTag].includes(targetPath.tagPathAt(-1, -1)?.type))
1447
1330
  throw new Error('invalid location for reference');
1448
1331
  if (!name && !type) throw new Error();
1449
1332
 
@@ -1456,22 +1339,25 @@ export const Path = class AgastPath {
1456
1339
  if (targetPath.open.type === Symbol.for('_') && !['_', '#'].includes(type))
1457
1340
  throw new Error();
1458
1341
 
1459
- if (tag.value.name) {
1460
- let property = getProperty(tag.value.name, targetPath.node);
1342
+ if (tag_.value.name) {
1343
+ let property = getProperty(tag_.value.name, targetPath.node);
1461
1344
  if (
1462
- getOr(null, tag.value.name, targetPath.node) &&
1345
+ getOr(null, tag_.value.name, targetPath.node) &&
1463
1346
  !property.value.shift &&
1464
- !referencesAreEqual(tag.value, property.value.reference)
1347
+ !Tags.referencesAreEqual(tag_.value, property.value.reference)
1465
1348
  ) {
1466
1349
  throw new Error('mismatched references');
1467
1350
  }
1468
- } else if (tag.value.type === '_') {
1469
- let rootIdx = getPropertyTagsIndex(targetPath.node, '_', null, 0);
1351
+ } else if (tag_.value.type === '_') {
1352
+ let rootIdx = Tags.getIndex(
1353
+ buildFullPathSegment('_', null, 0),
1354
+ targetPath.node.value.tags,
1355
+ );
1470
1356
 
1471
1357
  if (
1472
1358
  rootIdx != null &&
1473
- !referencesAreEqual(
1474
- tag.value,
1359
+ !Tags.referencesAreEqual(
1360
+ tag_.value,
1475
1361
  Tags.getAt(rootIdx, getTags(targetPath.node)).value.reference,
1476
1362
  )
1477
1363
  ) {
@@ -1479,43 +1365,42 @@ export const Path = class AgastPath {
1479
1365
  }
1480
1366
  }
1481
1367
 
1482
- let property = buildChild(Property, buildProperty([tag]));
1368
+ let property = buildChild(Property, buildProperty(BList.fromValues([printTag(tag)])));
1483
1369
 
1484
1370
  targetPath = targetPath.replaceWith(
1485
- buildNode(Tags.push(getTags(targetPath.node), property)),
1371
+ buildNode(Tags.push(property, getTags(targetPath.node))),
1486
1372
  );
1487
1373
  break;
1488
1374
  }
1489
1375
 
1490
1376
  case BindingTag: {
1491
- if (![ReferenceTag, ShiftTag].includes(this.tagPathAt(-1, -1).tag.type)) {
1377
+ if (![ReferenceTag, ShiftTag].includes(parseTagType(this.tagPathAt(-1, -1)))) {
1492
1378
  throw new Error('Invalid location for BindingTag');
1493
1379
  }
1494
- if (!tag.value.segments) throw new Error();
1495
1380
 
1496
1381
  let refPath = this.tagPathAt(-1, 0);
1497
1382
  let propPath = TagPath.from(refPath.path, refPath.tagsIndex);
1498
1383
 
1499
- let { shift } = propPath.tag.value;
1384
+ let { shift } = propPath.value;
1500
1385
 
1501
- if (![ReferenceTag, ShiftTag].includes(refPath.tag.type)) throw new Error();
1386
+ if (![ReferenceTag, ShiftTag].includes(refPath.type)) throw new Error();
1502
1387
 
1503
- if (refPath.tag.type === ShiftTag) {
1388
+ if (refPath.type === ShiftTag) {
1504
1389
  refPath = TagPath.from(refPath.path, refPath.tagsIndex - shift.index, 0);
1505
1390
  }
1506
1391
 
1507
- let { 0: firstTag, 1: bindingTags = [] } = propPath.tag.value.tags;
1508
- let tags = [firstTag, [...bindingTags, tag]];
1392
+ let { 0: firstTag, 1: bindingTags = [] } = propPath.value.tags[1];
1393
+ let tags = BList.fromValues([firstTag, Tags.from(...bindingTags, tag_)], 1);
1509
1394
  let property = buildChild(Property, buildProperty(tags, shift));
1510
1395
 
1511
1396
  targetPath = this.replaceWith(
1512
- buildNode(Tags.replaceAt(propPath.tagsIndex, getTags(this.node), property)),
1397
+ buildNode(Tags.replaceAt(propPath.tagsIndex, property, getTags(this.node))),
1513
1398
  );
1514
1399
  break;
1515
1400
  }
1516
1401
 
1517
1402
  case OpenNodeTag: {
1518
- let { literalValue, flags, type } = tag.value;
1403
+ let { literalValue, flags, type, name, attributes } = tag_.value;
1519
1404
 
1520
1405
  if (this.held && flags.token) throw new Error();
1521
1406
 
@@ -1524,22 +1409,36 @@ export const Path = class AgastPath {
1524
1409
  }
1525
1410
 
1526
1411
  let parentProp = this.node && Tags.getAt(-1, getChildren(this.node));
1527
- let ref = parentProp.value.shift
1412
+
1413
+ if (parentProp && propertyIsFull(parentProp)) {
1414
+ parentProp = null;
1415
+ }
1416
+
1417
+ let ref = parentProp?.value.shift
1528
1418
  ? this.node &&
1529
- Tags.getAt(-1 - parentProp.value.shift.index, getChildren(this.node)).value.tags[0]
1530
- .value
1531
- : parentProp.value.reference;
1419
+ parseTag(
1420
+ Tags.getAt(-1 - parentProp.value.shift.index, getChildren(this.node)).value
1421
+ .tags[1][0],
1422
+ ).value
1423
+ : parentProp?.value.reference;
1532
1424
 
1533
- if (this.node.value.flags.token && ref.type !== '@') throw new Error();
1425
+ let parentFlags = parentProp?.value.shift ? flags : this.node.value.flags;
1534
1426
 
1535
- let node = buildNode(tag);
1427
+ if (parentFlags.token && ref?.type !== '@') throw new Error();
1536
1428
 
1429
+ let node;
1537
1430
  if (literalValue && !flags.token) {
1538
- throw new Error();
1431
+ let newOpenTag = buildFullOpenNodeTag(flags, type, name, null, attributes);
1432
+ let literalNode = Path.fromTag(
1433
+ buildFullOpenNodeTag(tokenFlags, null, null, literalValue),
1434
+ ).node;
1435
+
1436
+ node = Path.fromTag(newOpenTag).advance(literalNode).advance('</>').node;
1437
+ } else {
1438
+ node = buildNode(tag_);
1539
1439
  }
1540
1440
 
1541
1441
  targetPath = this.advanceUnwrapped(node).path;
1542
-
1543
1442
  targetPath = targetPath.tagPathAt(-1).inner;
1544
1443
 
1545
1444
  break;
@@ -1548,11 +1447,11 @@ export const Path = class AgastPath {
1548
1447
  case GapTag:
1549
1448
  case NullTag: {
1550
1449
  // if (getSigilTag(this.node)) throw new Error();
1551
- if (this.tagPathAt(-1, 0).tag.type !== ReferenceTag) {
1450
+ if (parseTagType(this.tagPathAt(-1, 0)) !== ReferenceTag) {
1552
1451
  throw new Error('Invalid location for NullTag');
1553
1452
  }
1554
1453
 
1555
- let node = buildNode(tag);
1454
+ let node = buildNode(tag_);
1556
1455
 
1557
1456
  targetPath = this.advanceUnwrapped(node).path;
1558
1457
  // targetPath = targetPath.tagPathAt(-1).inner; // ?
@@ -1565,21 +1464,22 @@ export const Path = class AgastPath {
1565
1464
 
1566
1465
  let lastChild = targetPath.getChild(-1);
1567
1466
 
1568
- if (lastChild?.type === Property && !propertyIsFull(lastChild)) throw new Error();
1467
+ if (isObject(lastChild) && lastChild?.type === Property && !propertyIsFull(lastChild))
1468
+ throw new Error();
1569
1469
 
1570
1470
  let openTag = getOpenTag(node);
1571
1471
 
1572
1472
  if (!openTag) throw new Error();
1573
- if (openTag.value.selfClosing) throw new Error();
1473
+ if (parseTag(openTag).value.selfClosing) throw new Error();
1574
1474
 
1575
- targetPath = targetPath.replaceWith(buildNode(Tags.push(getTags(node), tag)));
1475
+ targetPath = targetPath.replaceWith(buildNode(Tags.push(tag_, getTags(node))));
1576
1476
  break;
1577
1477
  }
1578
1478
 
1579
1479
  case NullNode:
1580
1480
  case GapNode:
1581
1481
  case TreeNode: {
1582
- let node = tag;
1482
+ let node = tag_;
1583
1483
  let parentPath = targetPath;
1584
1484
  let { node: parentNode } = parentPath;
1585
1485
 
@@ -1588,12 +1488,12 @@ export const Path = class AgastPath {
1588
1488
  if (isMultiFragment(node)) {
1589
1489
  let existingProperty = tagPath;
1590
1490
 
1591
- while (existingProperty?.tag.type === AttributeDefinition) {
1592
- existingProperty = existingProperty.previousSibling.propertyPath;
1491
+ while (existingProperty?.type === AttributeDefinition) {
1492
+ existingProperty = tagPath.siblingAt(existingProperty.tagsIndex - 1);
1593
1493
  }
1594
1494
 
1595
1495
  if (
1596
- existingProperty.tag.type !== OpenNodeTag &&
1496
+ existingProperty.type !== OpenNodeTag &&
1597
1497
  existingProperty.tag &&
1598
1498
  !propertyIsFull(existingProperty.tag)
1599
1499
  ) {
@@ -1603,7 +1503,7 @@ export const Path = class AgastPath {
1603
1503
  let newPath = Path.from(buildNode(this.node.value.tags));
1604
1504
 
1605
1505
  for (let tag of Tags.traverse(node.value.children)) {
1606
- if (tag.type === Property) {
1506
+ if (parseTagType(tag) === Property) {
1607
1507
  newPath = newPath.advance(tag);
1608
1508
  } else {
1609
1509
  newPath = newPath.advanceUnwrapped(tag).path.atDepth(0);
@@ -1614,11 +1514,11 @@ export const Path = class AgastPath {
1614
1514
  break;
1615
1515
  }
1616
1516
 
1617
- let shift = tagPath.tag.type === Property ? tagPath.tag.value.shift : undefined;
1517
+ let shift = tagPath.type === Property ? tagPath.value.shift : undefined;
1618
1518
  let { held } = this;
1619
1519
 
1620
1520
  if (held) {
1621
- // let heldProperty = this.tagPathAt(-2, 2).tag.value;
1521
+ // let heldProperty = this.tagPathAt(-2, 2).value;
1622
1522
  let matchesHeld = isGapNode(node);
1623
1523
  if (getRoot(node) && nodeIsComplete(node)) {
1624
1524
  matchesHeld =
@@ -1626,8 +1526,10 @@ export const Path = class AgastPath {
1626
1526
  isGapNode(node) ||
1627
1527
  held.node === node ||
1628
1528
  held.node ===
1629
- BTree.getAt(-BTree.getSize(held.node.value.bounds[0]), node.value.bounds[0])
1630
- .property.value.node;
1529
+ BList.getAt(
1530
+ -BList.getSize(held.node.value.bounds.leading),
1531
+ node.value.bounds.leading,
1532
+ ).property.value.node;
1631
1533
  if (!matchesHeld) {
1632
1534
  // TODO We're passing by the node that shifted if it's a cover!
1633
1535
  throw new Error();
@@ -1636,17 +1538,17 @@ export const Path = class AgastPath {
1636
1538
  }
1637
1539
 
1638
1540
  if (propertyIsFull(tagPath.tag)) {
1639
- let tags = [null, [], node];
1541
+ let tags = BList.fromValues([Tags.fromValues([]), Tags.fromValues([]), node], 1);
1640
1542
  let property = buildChild(Property, buildProperty(tags, shift));
1641
1543
 
1642
- targetPath = this.replaceWith(buildNode(Tags.push(parentNode.value.tags, property)));
1544
+ targetPath = this.replaceWith(buildNode(Tags.push(property, parentNode.value.tags)));
1643
1545
  } else {
1644
- let { 0: firstTag, 1: bindingTags = [] } = tagPath.tag.value.tags;
1645
- let tags = [firstTag, bindingTags, node];
1546
+ let { 0: firstTag, 1: bindingTags = Tags.fromValues([]) } = tagPath.value.tags[1];
1547
+ let tags = BList.fromValues([firstTag, bindingTags, node], 1);
1646
1548
  let property = buildChild(Property, buildProperty(tags, shift));
1647
1549
 
1648
1550
  targetPath = this.replaceWith(
1649
- buildNode(Tags.replaceAt(tagPath.tagsIndex, parentNode.value.tags, property)),
1551
+ buildNode(Tags.replaceAt(tagPath.tagsIndex, property, parentNode.value.tags)),
1650
1552
  );
1651
1553
  }
1652
1554
 
@@ -1662,10 +1564,10 @@ export const Path = class AgastPath {
1662
1564
 
1663
1565
  // add undefined attributes from value
1664
1566
  let { node } = this;
1665
- if (tag.type !== AttributeDefinition) throw new Error();
1567
+ if (tag_.type !== AttributeDefinition) throw new Error();
1666
1568
 
1667
- let { path, value } = tag.value;
1668
- let openTag = getOpenTag(node);
1569
+ let { path, value } = tag_.value;
1570
+ let openTag = parseTag(getOpenTag(node));
1669
1571
  let { attributes } = node.value;
1670
1572
 
1671
1573
  if (!objHas(attributes, path) && objGet(attributes, path) !== undefined)
@@ -1675,10 +1577,10 @@ export const Path = class AgastPath {
1675
1577
 
1676
1578
  let { flags, name } = openTag.value;
1677
1579
  attributes = immSet(attributes, path, value);
1678
- let newOpenTag = buildOpenNodeTag(flags, name, null, attributes);
1580
+ let newOpenTag = buildOpenNodeTag(flags, name, null, printObject(attributes));
1679
1581
 
1680
1582
  targetPath = this.replaceWith(
1681
- buildNode(Tags.push(Tags.replaceAt(0, getTags(node), newOpenTag), tag)),
1583
+ buildNode(Tags.push(tag_, Tags.replaceAt(0, newOpenTag, getTags(node)))),
1682
1584
  );
1683
1585
  break;
1684
1586
  }
@@ -1691,7 +1593,7 @@ export const Path = class AgastPath {
1691
1593
  let { reference, shift: heldShift } = lastPropertyTag.value;
1692
1594
 
1693
1595
  if (!reference) {
1694
- ({ reference } = this.tagPathAt(-1 - (heldShift?.index ?? 0)).tag.value);
1596
+ ({ reference } = this.tagPathAt(-1 - (heldShift?.index ?? 0)).value);
1695
1597
  }
1696
1598
 
1697
1599
  if (!reference.flags.expression && reference.type !== '_') throw new Error();
@@ -1700,55 +1602,114 @@ export const Path = class AgastPath {
1700
1602
  ? buildShift(heldShift.index + 1, heldShift.height + 1)
1701
1603
  : buildShift(1, 3);
1702
1604
 
1703
- let property = buildChild(Property, buildProperty([tag], shift));
1605
+ let property = buildChild(Property, buildProperty(BList.fromValues([tag]), shift));
1704
1606
 
1705
- targetPath = this.replaceWith(buildNode(Tags.push(this.node.value.tags, property)));
1607
+ targetPath = this.replaceWith(buildNode(Tags.push(property, this.node.value.tags)));
1706
1608
  break;
1707
1609
  }
1708
1610
 
1709
1611
  case LiteralTag:
1710
- if (typeof tag.value !== 'string') throw new Error();
1612
+ if (typeof tag_.value !== 'string') throw new Error();
1711
1613
 
1712
- targetPath = this.replaceWith(buildNode(Tags.push(this.node.value.tags, tag)));
1614
+ targetPath = this.replaceWith(buildNode(Tags.push(tag_, this.node.value.tags)));
1713
1615
  break;
1714
1616
 
1715
1617
  default:
1716
1618
  throw new Error();
1717
1619
  }
1718
1620
 
1719
- return targetPath && TagPath.from(targetPath, -1, isNodeTag(tag) ? -1 : offsetForTag(tag));
1621
+ return targetPath && TagPath.from(targetPath, -1, offsetForTag(tag_));
1720
1622
  }
1721
1623
  };
1722
1624
 
1723
- Object.freeze(Path.prototype);
1625
+ freezeClass(Path);
1724
1626
 
1725
1627
  export const tagPathsAreEqual = (a, b) => {
1726
1628
  if (a == null || b == null) return b == a;
1727
1629
  return a.path.node === b.path.node && a.tagsIndex === b.tagsIndex;
1728
1630
  };
1729
1631
 
1632
+ let resolveIdx = (idx, tree) => {
1633
+ let path = [];
1634
+ let node = tree;
1635
+
1636
+ if (isArray(idx)) {
1637
+ for (let seg of arrayValues(idx)) {
1638
+ let index = typeof seg !== 'object' ? seg : seg.index;
1639
+ if (typeof index === 'string') throw new Error();
1640
+ if (!Tags.isNode(node)) return null;
1641
+ let index_ = index < 0 ? Tags.getSize(node) + index : index;
1642
+ path.push(index_);
1643
+ node = Tags.getValues(node)[index_];
1644
+ if (node && !Tags.isNode(node)) {
1645
+ return path;
1646
+ }
1647
+ if (!node) return null;
1648
+ }
1649
+
1650
+ return path;
1651
+ }
1652
+ };
1653
+
1730
1654
  export class TagPath {
1731
1655
  static from(path, tagsIndex, propertyIndex) {
1732
1656
  let tags = getTags(path.node);
1733
1657
  let size = Tags.getSize(tags);
1734
1658
  let index = Number.isFinite(tagsIndex) && tagsIndex < 0 ? size + tagsIndex : tagsIndex;
1735
-
1736
- let propertyIndex_ = typeof propertyIndex === 'number' ? [propertyIndex] : propertyIndex;
1737
-
1738
- let propTag = Tags.getAt(index, tags);
1659
+ let propertyIndex_ = propertyIndex;
1660
+ let propTag_ = Tags.getAt(index, tags);
1661
+ let propTag = propTag_ && parseTag(propTag_);
1739
1662
 
1740
1663
  if (
1741
1664
  [OpenNodeTag, CloseNodeTag, LiteralTag, AttributeDefinition, GapTag, NullTag].includes(
1742
1665
  propTag?.type,
1743
1666
  )
1744
1667
  ) {
1745
- propertyIndex_ = [];
1668
+ propertyIndex_ = null;
1746
1669
  }
1747
1670
 
1748
- let resolvedPropertyIndex =
1749
- propTag && (propertyIndex_?.length ? Tags.findPath(propertyIndex_, propTag.value.tags) : []);
1671
+ let idx = null;
1672
+ if (propTag?.type === Property) {
1673
+ if (isArray(propertyIndex_)) {
1674
+ if (propertyIndex_.length) {
1675
+ idx = resolveIdx(propertyIndex_, propTag.value.tags);
1676
+ }
1677
+ } else if (propertyIndex_ != null) {
1678
+ let { tags } = propTag.value;
1679
+ let bindingsSize = Tags.getSize(tags[1][1]);
1680
+ let valuesSize = Tags.getValues(tags).length;
1681
+ let tagsSize = valuesSize - (tags[1][1] ? 1 : 0) + bindingsSize;
1682
+ let absoluteIndex = propertyIndex_ < 0 ? propertyIndex_ + tagsSize : propertyIndex_;
1683
+
1684
+ let bindingsStop = 1 + bindingsSize;
1685
+
1686
+ if (absoluteIndex === 0) {
1687
+ idx = [0];
1688
+ } else if (absoluteIndex > bindingsStop) {
1689
+ idx = [2];
1690
+ } else if (tags[1][1]) {
1691
+ let result = Tags.findPath(absoluteIndex - 1, tags[1][1]).map(({ index }) => index);
1692
+ if (Number.isFinite(arrayLast(result))) {
1693
+ idx = [1].concat(result);
1694
+ }
1695
+ }
1696
+ }
1697
+ }
1698
+
1699
+ idx ||= [];
1700
+
1701
+ if (idx?.length) {
1702
+ let tag = Tags.getAt(idx, propTag.value.tags);
1703
+ if (isArray(tag)) {
1704
+ idx = [];
1705
+ }
1706
+ }
1707
+
1708
+ freezeRecord(idx);
1750
1709
 
1751
- return resolvedPropertyIndex && new TagPath(path, index, resolvedPropertyIndex);
1710
+ let pathExists = isArray(propertyIndex_) && propertyIndex_?.length ? idx.length : propTag;
1711
+
1712
+ return pathExists ? new TagPath(path, index, idx) : null;
1752
1713
  }
1753
1714
 
1754
1715
  static fromNode(node, tagsIndex, propertyIndex) {
@@ -1773,28 +1734,26 @@ export class TagPath {
1773
1734
  if (!Number.isFinite(tagsIndex) || tagsIndex < 0) throw new Error();
1774
1735
  if (!isArray(propertyIndex)) throw new Error();
1775
1736
 
1776
- let propertyIndex_ = Object.freeze([...propertyIndex]);
1737
+ for (let index of recordValues(propertyIndex)) {
1738
+ if (!(index >= 0) || !Number.isFinite(index)) throw new Error();
1739
+ }
1777
1740
 
1778
1741
  let tag = Tags.getAt(tagsIndex, getTags(path.node));
1779
1742
 
1780
- if (propertyIndex_.length) {
1781
- tag = Tags.getAt(propertyIndex_, tag.value.tags);
1743
+ if (propertyIndex.length) {
1744
+ tag = Tags.getAt(propertyIndex, tag.value.tags);
1782
1745
  }
1783
1746
 
1784
1747
  if (tag == null || isArray(tag)) throw new Error();
1785
1748
 
1786
- for (let segment of propertyIndex_) {
1787
- if (!isPlainObject(segment) || !(segment.index === 0 || segment.index > 0)) throw new Error();
1788
- }
1789
-
1790
1749
  this.path = path;
1791
1750
  this.tagsIndex = tagsIndex;
1792
- this.propertyIndex = propertyIndex_;
1751
+ this.propertyIndex = propertyIndex;
1793
1752
 
1794
1753
  this.node = path.node;
1795
1754
  this.tag = tag;
1796
1755
 
1797
- if (tag.type === Property && propertyIndex_.length) throw new Error();
1756
+ if (isObject(tag) && tag.type === Property && propertyIndex.length) throw new Error();
1798
1757
 
1799
1758
  freeze(this);
1800
1759
  }
@@ -1802,13 +1761,21 @@ export class TagPath {
1802
1761
  asPrimitive() {
1803
1762
  let { path, tagsIndex, propertyIndex } = this;
1804
1763
 
1805
- return Object.freeze({ path: path.asPrimitive(), tagsIndex, propertyIndex });
1764
+ return freeze({ path: path.asPrimitive(), tagsIndex, propertyIndex });
1806
1765
  }
1807
1766
 
1808
1767
  get child() {
1809
1768
  return this.tag;
1810
1769
  }
1811
1770
 
1771
+ get type() {
1772
+ return parseTagType(this.tag);
1773
+ }
1774
+
1775
+ get value() {
1776
+ return parseTag(this.tag).value;
1777
+ }
1778
+
1812
1779
  get parentNode() {
1813
1780
  return this.path.parentNode;
1814
1781
  }
@@ -1830,12 +1797,12 @@ export class TagPath {
1830
1797
  get referenceTagPath() {
1831
1798
  let path = TagPath.from(this.path, this.tagsIndex, 0);
1832
1799
  if (path && path.propertyPath) {
1833
- let { shift } = path.propertyPath.tag.value;
1800
+ let { shift } = path.propertyPath.value;
1834
1801
  if (shift) {
1835
1802
  path = TagPath.from(this.path, this.tagsIndex - shift.index, 0);
1836
1803
  }
1837
1804
  }
1838
- return path?.tag.type === ReferenceTag ? path : null;
1805
+ return path?.type === ReferenceTag ? path : null;
1839
1806
  }
1840
1807
 
1841
1808
  get nextProperty() {
@@ -1843,33 +1810,33 @@ export class TagPath {
1843
1810
 
1844
1811
  let nextChild = TagPath.from(path, tagsIndex + 1);
1845
1812
 
1846
- return nextChild?.tag.type === Property ? nextChild : null;
1813
+ return nextChild?.type === Property ? nextChild : null;
1847
1814
  }
1848
1815
 
1849
1816
  get nextSibling() {
1850
1817
  let { path, tagsIndex, propertyIndex } = this;
1851
1818
 
1852
- let propertyIndex0 = propertyIndex[0]?.index;
1819
+ let propertyIndex0 = propertyIndex[0];
1853
1820
 
1854
- let nextChildIndex = tagsIndex === 0 ? 0 : tagsIndex - 1 + 1;
1821
+ let nextChildIndex = tagsIndex;
1855
1822
  let nextChild = path.getChild(nextChildIndex);
1856
1823
 
1857
1824
  if (propertyIndex0 == null && !nextChild) {
1858
- return this.tag.type === CloseNodeTag ? null : path.closeTagPath;
1825
+ return this.type === CloseNodeTag ? null : path.closeTagPath;
1859
1826
  }
1860
1827
 
1861
1828
  switch (propertyIndex0) {
1862
1829
  case 0:
1863
1830
  case 1: {
1864
1831
  let childIndex = tagsIndex - 1;
1865
- let bindingIndex = propertyIndex[1]?.index ?? -1;
1832
+ let bindingIndex = propertyIndex[1] ?? -1;
1866
1833
  let binding = path.getBindingTagPath(childIndex, bindingIndex + 1);
1867
1834
  if (binding) return binding;
1868
1835
  return path.getNodeTagPath(childIndex);
1869
1836
  }
1870
1837
  case 2:
1871
1838
  default: {
1872
- if (!nextChild || nextChild.type !== Property) {
1839
+ if (!nextChild || nextChild.type !== Property || !propertyIndex.length) {
1873
1840
  return path.tagPathAt(tagsIndex + 1);
1874
1841
  } else {
1875
1842
  let ref = path.getReferenceTagPath(nextChildIndex);
@@ -1916,38 +1883,34 @@ export class TagPath {
1916
1883
  get next() {
1917
1884
  let { path, tagsIndex, propertyIndex } = this;
1918
1885
 
1919
- propertyIndex = [...propertyIndex];
1886
+ propertyIndex = [...recordValues(propertyIndex)];
1920
1887
 
1921
1888
  let leaving = false;
1922
1889
 
1923
1890
  for (;;) {
1924
- let tag = Tags.getAt(tagsIndex, getTags(path.node));
1891
+ let tag = parseTag(Tags.getAt(tagsIndex, getTags(path.node)));
1925
1892
  let propTag = tag;
1926
1893
  let lastRef = null;
1894
+ let wasLeaving = leaving;
1927
1895
  leaving = false;
1928
1896
 
1929
1897
  if (!tag) return null;
1930
1898
 
1931
- if (tag.type === Property) {
1932
- if (propertyIndex.length === 0 && !leaving) {
1933
- propertyIndex.push({ index: 0, node: tag.value.tags });
1934
- }
1899
+ if (isObject(tag) && tag.type === Property && !wasLeaving) {
1900
+ if (propertyIndex.length === 0) {
1901
+ let { 0: open, 1: bindings } = tag.value.tags[1];
1935
1902
 
1936
- if (propertyIndex != null) {
1937
- propTag = Tags.getAt(propertyIndex, tag.value.tags);
1938
- lastRef = Tags.getAt(0, tag.value.tags);
1903
+ let index = !isArray(open) ? 0 : !isArray(bindings) ? 1 : 2;
1904
+
1905
+ propertyIndex.push({ index, node: tag.value.tags });
1939
1906
  }
1940
- }
1941
1907
 
1942
- if (isArray(propTag)) {
1943
- if (propTag.length) {
1944
- // enter bindings array
1945
- propertyIndex.push({ index: 0, node: propTag });
1946
- propTag = propTag[propertyIndex[1].index];
1947
- } else {
1948
- propertyIndex = [{ index: propertyIndex[0].index + 1, node: tag }];
1908
+ if (propertyIndex != null) {
1909
+ let tag_ = Tags.getAt(propertyIndex, tag.value.tags);
1910
+ propTag = isNode(tag_) ? tag_ : parseTag(tag_);
1911
+ let lastRef_ = Tags.getAt([0], tag.value.tags);
1912
+ lastRef = isArray(lastRef_) ? null : parseTag(lastRef_);
1949
1913
  }
1950
- continue;
1951
1914
  }
1952
1915
 
1953
1916
  let isInitialTag =
@@ -1956,21 +1919,21 @@ export class TagPath {
1956
1919
  arraysEqual(propertyIndex, this.propertyIndex);
1957
1920
 
1958
1921
  // done
1959
- if (!isInitialTag && !isNodeTag(propTag)) {
1922
+ if (!isInitialTag && !isNodeTag(propTag) && propTag.type !== Property) {
1960
1923
  return TagPath.from(path, tagsIndex, propertyIndex);
1961
1924
  }
1962
1925
 
1963
1926
  // shift
1964
- if (propTag.type === BindingTag && lastRef.type === ShiftTag) {
1927
+ if (propTag.type === BindingTag && lastRef?.type === ShiftTag) {
1965
1928
  let lastProp = Tags.getAt(tagsIndex, path.tags);
1966
1929
  let { shift } = lastProp.value;
1967
1930
  let refIndex = tagsIndex - shift.index;
1968
1931
  if (refIndex < 0) throw new Error();
1969
- let refTag = Tags.getAt(refIndex, path.tags, 0);
1932
+ let refTag = parseTag(Tags.getAt(refIndex, path.tags, 0));
1970
1933
 
1971
1934
  if (refTag.type !== ReferenceTag) throw new Error();
1972
1935
 
1973
- let { node } = tag.value;
1936
+ let { node } = parseTag(tag).value;
1974
1937
  if (!node) return null;
1975
1938
 
1976
1939
  path = new Path(path, tagsIndex);
@@ -1982,66 +1945,47 @@ export class TagPath {
1982
1945
  continue;
1983
1946
  }
1984
1947
 
1985
- let { nextSibling } = this;
1948
+ // in
1949
+ if (isNodeTag(propTag)) {
1950
+ let shiftPath = path;
1951
+
1952
+ while (
1953
+ shiftPath.parent &&
1954
+ !Tags.getAt(shiftPath.parentIndex - 1, shiftPath.parent.node.value.children).value.shift
1955
+ ) {
1956
+ shiftPath = shiftPath.parent;
1957
+ }
1958
+
1959
+ let isGap =
1960
+ path.parent &&
1961
+ tagsIndex === 1 &&
1962
+ shiftPath.parent &&
1963
+ Tags.getAt(shiftPath.parentIndex - 1, shiftPath.parent.node.value.children).value.shift &&
1964
+ tag.value.node ===
1965
+ Tags.getAt(shiftPath.parentIndex - 2, shiftPath.parent.node.value.children).value.node;
1966
+
1967
+ path = path.push(tagsIndex, isGap);
1968
+ tagsIndex = 0;
1969
+ propertyIndex = [];
1970
+ continue;
1971
+ }
1986
1972
 
1987
1973
  // over
1974
+ let { nextSibling } = TagPath.from(path, tagsIndex, freeze([...arrayValues(propertyIndex)]));
1975
+
1988
1976
  if (nextSibling) {
1989
1977
  ({ tagsIndex, propertyIndex } = nextSibling);
1990
- // in
1991
- if (isNodeTag(nextSibling.tag)) {
1992
- let shiftPath = path;
1993
-
1994
- while (
1995
- shiftPath.parent &&
1996
- !Tags.getAt(shiftPath.parentIndex - 1, shiftPath.parent.node.value.children).value.shift
1997
- ) {
1998
- shiftPath = shiftPath.parent;
1999
- }
2000
-
2001
- let isGap =
2002
- path.parent &&
2003
- tagsIndex === 1 &&
2004
- shiftPath.parent &&
2005
- Tags.getAt(shiftPath.parentIndex - 1, shiftPath.parent.node.value.children).value
2006
- .shift &&
2007
- tag.value.node ===
2008
- Tags.getAt(shiftPath.parentIndex - 2, shiftPath.parent.node.value.children).value
2009
- .node;
2010
-
2011
- path = path.push(tagsIndex, isGap);
2012
- tagsIndex = 0;
2013
- propertyIndex = [];
2014
- continue;
2015
- } else {
2016
- return nextSibling;
2017
- }
1978
+ propertyIndex = [...arrayValues(propertyIndex)];
1979
+ continue;
2018
1980
  }
2019
1981
 
2020
1982
  // out
2021
1983
  if (path.parentIndex != null && path.parent) {
2022
- do {
2023
- if (
2024
- path.coverBoundary.parent &&
2025
- Tags.getAt(
2026
- [path.coverBoundary.parentIndex, 0],
2027
- path.coverBoundary.parent.node.value.tags,
2028
- )?.type === ShiftTag
2029
- ) {
2030
- // while
2031
- tagsIndex =
2032
- Tags.getSize(path.parent.node.value.tags) > path.parentIndex + 1
2033
- ? path.parentIndex + 1
2034
- : null;
2035
- } else {
2036
- tagsIndex = path.parentIndex + 1;
2037
- }
2038
-
2039
- propertyIndex = [];
2040
- path = path.parent;
2041
- leaving = true;
1984
+ tagsIndex = path.parentIndex;
1985
+ propertyIndex = [];
1986
+ path = path.parent;
2042
1987
 
2043
- if (!path) return null;
2044
- } while (tagsIndex == null);
1988
+ if (!path) return null;
2045
1989
 
2046
1990
  leaving = true;
2047
1991
  continue;
@@ -2054,21 +1998,23 @@ export class TagPath {
2054
1998
  get nextUnshifted() {
2055
1999
  let { path, tagsIndex, propertyIndex } = this;
2056
2000
 
2057
- propertyIndex = [...propertyIndex];
2001
+ propertyIndex = [...arrayValues(propertyIndex)];
2058
2002
 
2059
2003
  let leaving = false;
2060
2004
 
2061
2005
  for (;;) {
2062
- let tag = Tags.getAt(tagsIndex, getTags(path.node));
2006
+ let tag = parseTag(Tags.getAt(tagsIndex, getTags(path.node)));
2063
2007
  let propTag = tag;
2064
2008
  let wasLeaving = leaving;
2065
2009
  leaving = false;
2066
2010
 
2067
2011
  if (!tag) return null;
2068
2012
 
2069
- if (tag.type === Property) {
2013
+ if (isObject(tag) && tag.type === Property) {
2070
2014
  if (propertyIndex.length === 0 && !leaving) {
2071
- propertyIndex.push({ index: 0, node: tag.value.tags });
2015
+ let { 0: open, 1: bindings } = tag.value.tags[1];
2016
+ let index = !isArray(open) ? 0 : !isArray(bindings) ? 1 : 2;
2017
+ propertyIndex.push({ index, node: tag.value.tags });
2072
2018
  }
2073
2019
 
2074
2020
  if (propertyIndex != null) {
@@ -2077,7 +2023,7 @@ export class TagPath {
2077
2023
  }
2078
2024
 
2079
2025
  if (isArray(propTag)) {
2080
- if (propTag.length) {
2026
+ if (Tags.getSize(propTag)) {
2081
2027
  // enter bindings array
2082
2028
  propertyIndex.push({ index: 0, node: propTag });
2083
2029
  propTag = propTag[propertyIndex[1].index];
@@ -2097,7 +2043,7 @@ export class TagPath {
2097
2043
  tagsIndex === this.tagsIndex &&
2098
2044
  arraysEqual(propertyIndex, this.propertyIndex);
2099
2045
 
2100
- if (!wasLeaving && propTag.type === ReferenceTag && propTag.value.flags.expression) {
2046
+ if (!wasLeaving && parseTagType(propTag) === ReferenceTag) {
2101
2047
  // move past shifts
2102
2048
 
2103
2049
  let shifts = 0;
@@ -2125,6 +2071,7 @@ export class TagPath {
2125
2071
  // over
2126
2072
  if (nextSibling) {
2127
2073
  ({ tagsIndex, propertyIndex } = nextSibling);
2074
+ propertyIndex = [...arrayValues(propertyIndex)];
2128
2075
  continue;
2129
2076
  }
2130
2077
 
@@ -2159,7 +2106,7 @@ export class TagPath {
2159
2106
 
2160
2107
  let tag = Tags.getAt(tagsIndex, getTags(path.node));
2161
2108
 
2162
- return tag.type === Property ? path.tagPathAt(tagsIndex) : null;
2109
+ return isObject(tag) && tag.type === Property ? path.tagPathAt(tagsIndex) : null;
2163
2110
  }
2164
2111
 
2165
2112
  get innerNode() {
@@ -2167,20 +2114,22 @@ export class TagPath {
2167
2114
  }
2168
2115
 
2169
2116
  get inner() {
2170
- let { tagsIndex, tag } = this;
2117
+ let { tagsIndex, type } = this;
2171
2118
 
2172
2119
  let node;
2173
- switch (tag.type) {
2120
+ switch (type) {
2174
2121
  case Property: {
2175
- ({ node } = tag.value);
2122
+ ({ node } = this.tag.value);
2176
2123
  break;
2177
2124
  }
2125
+ case GapNode:
2126
+ case NullNode:
2178
2127
  case TreeNode: {
2179
- node = tag;
2128
+ node = this.tag;
2180
2129
  break;
2181
2130
  }
2182
2131
  default:
2183
- throw new Error();
2132
+ node = null;
2184
2133
  }
2185
2134
  return node && this.path.push(tagsIndex);
2186
2135
  }
@@ -2194,4 +2143,4 @@ export class TagPath {
2194
2143
  }
2195
2144
  }
2196
2145
 
2197
- Object.freeze(TagPath.prototype);
2146
+ freezeClass(TagPath);