@bablr/agast-helpers 0.10.10 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/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
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, closeStack);
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;
397
- };
398
-
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;
333
+ return [GapTag, NullTag].includes(parseTagType(tag));
505
334
  };
506
335
 
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,6 +449,10 @@ 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) {
642
457
  if (isArray(name)) throw new Error('not supported');
643
458
  let count = countList(name, node);
@@ -652,10 +467,10 @@ export const countList = (name, node) => {
652
467
 
653
468
  if (name == null) throw new Error('Bad path');
654
469
 
655
- return Tags.getAtName(name, Tags.getSums(getTags(node)).names);
470
+ return Tags.findStats(name, Tags.getSums(getTags(node)).names);
656
471
  };
657
472
 
658
- export function* allTagPathsFor(range, options = {}) {
473
+ export function* allTagPathsFor(range, options = freeze({})) {
659
474
  if (range == null) return;
660
475
 
661
476
  if (range[0] && !(range[0] instanceof TagPath)) throw new Error();
@@ -667,7 +482,7 @@ export function* allTagPathsFor(range, options = {}) {
667
482
  let path = startPath;
668
483
 
669
484
  while (path) {
670
- if (path.inner && path.previousSibling.tag.type === ReferenceTag) {
485
+ if (path.inner && path.previousSibling.type === ReferenceTag) {
671
486
  path = TagPath.from(path.inner, 0);
672
487
  }
673
488
 
@@ -685,7 +500,7 @@ export function* allTagPathsFor(range, options = {}) {
685
500
 
686
501
  if (
687
502
  endPath &&
688
- path.tag.type === CloseNodeTag &&
503
+ path.type === CloseNodeTag &&
689
504
  gapPath &&
690
505
  gapPath.tagsIndex === endPath.tagsIndex &&
691
506
  gapPath.path.node === endPath.path.node
@@ -696,7 +511,7 @@ export function* allTagPathsFor(range, options = {}) {
696
511
  }
697
512
  }
698
513
 
699
- export function* allTagsFor(range, options = {}) {
514
+ export function* allTagsFor(range, options = freeze({})) {
700
515
  for (let path of allTagPathsFor(range, options)) {
701
516
  yield path.tag;
702
517
  }
@@ -787,51 +602,27 @@ export const getAttributes = (node) => {
787
602
  };
788
603
 
789
604
  export const isSelfClosingTag = (tag) => {
790
- return tag.type === OpenNodeTag
791
- ? tag.value.selfClosing
792
- : [NullTag, GapTag].includes(tag.type)
793
- ? true
794
- : false;
795
- };
796
-
797
- export let pathForTagPath = (tagPath) => {
798
- let refTagPath = tagPath;
799
- let isShift = tagPath.tag.type === ShiftTag;
800
-
801
- if (isShift) {
802
- refTagPath = tagPath.siblingAt(tagPath.tagsIndex - tagPath.tag.value.index);
803
- }
804
-
805
- let { type, name } = refTagPath.tag.value;
806
-
807
- if (refTagPath.tag.type !== ReferenceTag) throw new Error();
808
-
809
- let index = getChildPropertyIndex(tagPath.node, refTagPath.tagsIndex);
810
- let shiftIndex = isShift ? (tagPath.tag.value.index ?? 0) + 1 : null;
811
-
812
- return [{ type, name, index, shiftIndex }];
813
- };
814
-
815
- let getSigilTag_ = (tag) => {
816
- switch (tag.type) {
817
- case Property:
818
- return getTags(tag.value.property.node)[1][0];
819
- case TreeNode:
820
- case NullNode:
821
- case GapNode:
822
- return getTags(tag)[1][0];
823
- case OpenNodeTag:
824
- case GapTag:
825
- case NullTag:
826
- return tag;
827
- }
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;
828
611
  };
829
612
 
830
613
  const parents = new WeakMap();
831
614
 
832
615
  export const Path = class AgastPath {
833
616
  static from(node) {
834
- 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
+ );
835
626
  }
836
627
 
837
628
  static wrap(frames) {
@@ -842,17 +633,17 @@ export const Path = class AgastPath {
842
633
  return Path.from(buildNode(openTag));
843
634
  }
844
635
 
845
- static set(node, path, value) {
636
+ static set(path, value, node) {
846
637
  return Path.from(node).replaceAt(path, value).node;
847
638
  }
848
639
 
849
- static get(node, path) {
640
+ static get(path, node) {
850
641
  return Path.from(node).get(path).node;
851
642
  }
852
643
 
853
644
  constructor(parent, frame) {
854
645
  if (!frame) throw new Error();
855
- let frame_ = isArray(frame) ? BTree.getAt(-1, frame) : frame;
646
+ let frame_ = isArray(frame) ? BList.getAt(-1, frame) : frame;
856
647
  let { property, parentIndex, isGap } = frame_;
857
648
  let { node } = property.value;
858
649
 
@@ -869,13 +660,13 @@ export const Path = class AgastPath {
869
660
  parents.set(this, parent);
870
661
  }
871
662
 
872
- 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;
873
664
  this.frame = frame_;
874
665
  this.frames = !parent
875
666
  ? isArray(frame)
876
667
  ? frame
877
- : BTree.fromValues([frame])
878
- : BTree.push(parent.frames, frame);
668
+ : BList.fromValues([frame])
669
+ : BList.push(frame, parent.frames);
879
670
 
880
671
  buildSkips(this);
881
672
 
@@ -889,18 +680,10 @@ export const Path = class AgastPath {
889
680
  throw new Error();
890
681
  }
891
682
 
892
- if (
893
- parentIndex != null &&
894
- !parent?.node.value.flags.token &&
895
- (!this.referenceTag || ![ReferenceTag, ShiftTag].includes(this.referenceTag.type))
896
- ) {
897
- throw new Error();
898
- }
899
-
900
683
  if (
901
684
  parent_ &&
902
685
  (isGap
903
- ? BTree.getAt(0, parent_.node.value.bounds[0]).property
686
+ ? BList.getAt(0, parent_.node.value.bounds.leading).property
904
687
  : parent_.tagPathAt(parentIndex).tag) !== property
905
688
  ) {
906
689
  throw new Error('Path not reachable');
@@ -915,12 +698,12 @@ export const Path = class AgastPath {
915
698
 
916
699
  push(tagsIndex, isGap) {
917
700
  let tag = isGap
918
- ? BTree.getAt(0, this.node.value.bounds[0]).property
701
+ ? BList.getAt(0, this.node.value.bounds.leading).property
919
702
  : tagsIndex != null
920
703
  ? Tags.getAt(tagsIndex, this.tags)
921
704
  : null;
922
705
 
923
- if (!tag || tag.type !== Property) {
706
+ if (!tag || parseTagType(tag) !== Property) {
924
707
  return null;
925
708
  }
926
709
 
@@ -941,7 +724,7 @@ export const Path = class AgastPath {
941
724
  return parent;
942
725
  }
943
726
 
944
- parent = new Path(null, BTree.pop(frames));
727
+ parent = new Path(null, BList.pop(frames));
945
728
 
946
729
  parents.set(this, parent);
947
730
 
@@ -960,6 +743,10 @@ export const Path = class AgastPath {
960
743
  return this.frame.property.value.node;
961
744
  }
962
745
 
746
+ get value() {
747
+ return this.node.value;
748
+ }
749
+
963
750
  get name() {
964
751
  let { node } = this;
965
752
  return node.type === TreeNode ? node.value.name : null;
@@ -987,6 +774,7 @@ export const Path = class AgastPath {
987
774
  getChild(childrenIndex) {
988
775
  let { node } = this;
989
776
  if (isStubNode(node)) return null;
777
+
990
778
  return Tags.getAt(childrenIndex, getChildren(node)) || null;
991
779
  }
992
780
 
@@ -1000,7 +788,7 @@ export const Path = class AgastPath {
1000
788
 
1001
789
  getPropertyTagPath(childrenIndex) {
1002
790
  let tagPath = this.tagPathAt(childrenIndex + 1);
1003
- return tagPath && tagPath.tag.type === Property ? tagPath : null;
791
+ return tagPath && tagPath.type === Property ? tagPath : null;
1004
792
  }
1005
793
 
1006
794
  getReferenceTagPath(childrenIndex) {
@@ -1026,12 +814,12 @@ export const Path = class AgastPath {
1026
814
 
1027
815
  get openTagPath() {
1028
816
  let tagPath = TagPath.from(this, 0);
1029
- return tagPath.tag.type === OpenNodeTag ? tagPath : null;
817
+ return tagPath.type === OpenNodeTag ? tagPath : null;
1030
818
  }
1031
819
 
1032
820
  get closeTagPath() {
1033
821
  let tagPath = TagPath.from(this, -1);
1034
- return tagPath.tag.type === CloseNodeTag ? tagPath : null;
822
+ return tagPath.type === CloseNodeTag ? tagPath : null;
1035
823
  }
1036
824
 
1037
825
  get openTag() {
@@ -1039,7 +827,7 @@ export const Path = class AgastPath {
1039
827
  }
1040
828
 
1041
829
  get open() {
1042
- return this.openTag.value;
830
+ return parseOpenNodeTag(this.openTag).value;
1043
831
  }
1044
832
 
1045
833
  get closeTag() {
@@ -1051,11 +839,12 @@ export const Path = class AgastPath {
1051
839
  }
1052
840
 
1053
841
  get bindingTags() {
1054
- return this.parentPropertyPath?.tag.value.tags[1];
842
+ return this.parentPropertyPath?.tag.value.tags[1][1];
1055
843
  }
1056
844
 
1057
845
  get reference() {
1058
- return this.referenceTag?.value ?? null;
846
+ let { referenceTag } = this;
847
+ return referenceTag && parseTag(referenceTag)?.value;
1059
848
  }
1060
849
 
1061
850
  get referenceTag() {
@@ -1081,8 +870,8 @@ export const Path = class AgastPath {
1081
870
  return null;
1082
871
  }
1083
872
  let path = TagPath.from(this.parent, this.parentIndex);
1084
- if (path.tag.type === ShiftTag) {
1085
- 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);
1086
875
  }
1087
876
  return path;
1088
877
  }
@@ -1097,7 +886,7 @@ export const Path = class AgastPath {
1097
886
 
1098
887
  get firstPropertyTagPath() {
1099
888
  let tagPath = this.tagPathAt(0);
1100
- while (tagPath && (!tagPath.propertyPath || isNullNode(tagPath.propertyPath.tag.value.node))) {
889
+ while (tagPath && (!tagPath.propertyPath || isNullNode(tagPath.propertyPath.value.node))) {
1101
890
  tagPath = tagPath.nextSibling;
1102
891
  if (tagPath?.propertyPath) {
1103
892
  tagPath = tagPath.propertyPath;
@@ -1108,7 +897,7 @@ export const Path = class AgastPath {
1108
897
 
1109
898
  get lastPropertyTagPath() {
1110
899
  let tagPath = this.tagPathAt(-1);
1111
- while (tagPath && (!tagPath.propertyPath || isNullNode(tagPath.propertyPath.tag.value.node))) {
900
+ while (tagPath && (!tagPath.propertyPath || isNullNode(tagPath.propertyPath.value.node))) {
1112
901
  tagPath = tagPath.previousSibling;
1113
902
  if (tagPath?.propertyPath) {
1114
903
  tagPath = tagPath.propertyPath;
@@ -1141,7 +930,9 @@ export const Path = class AgastPath {
1141
930
  while (!pathInst.node.value.name && pathInst.node.value.type === Symbol.for('_')) {
1142
931
  if (pathInst.depth) throw new Error();
1143
932
  skippedRootFragment = true;
1144
- pathInst = pathInst.push(getPropertyTagsIndex(pathInst.node, '_', null, 0));
933
+ pathInst = pathInst.push(
934
+ Tags.getIndex(buildFullPathSegment('_', null, 0), pathInst.node.value.tags),
935
+ );
1145
936
  }
1146
937
  }
1147
938
 
@@ -1161,12 +952,9 @@ export const Path = class AgastPath {
1161
952
 
1162
953
  if (i === 0 && seg.type === '_' && skippedRootFragment) continue;
1163
954
 
1164
- let tagsIndex = getPropertyTagsIndex(
1165
- pathInst.node,
1166
- seg.type,
1167
- seg.name,
1168
- seg.index,
1169
- seg.shiftIndex,
955
+ let tagsIndex = Tags.getIndex(
956
+ buildFullPathSegment(seg.type, seg.name, seg.index, seg.shiftIndex),
957
+ pathInst.node.value.tags,
1170
958
  );
1171
959
 
1172
960
  if (tagsIndex == null) return null;
@@ -1178,9 +966,9 @@ export const Path = class AgastPath {
1178
966
  }
1179
967
 
1180
968
  replaceWith(node, bindingTags) {
1181
- let bindings = bindingTags?.map((tag) => tag.value); // TODO improve here
969
+ let bindings = bindingTags?.map((tag) => parseTag(tag).value); // TODO improve here
1182
970
  if (bindings != null && !isArray(bindings)) throw new Error();
1183
- if (!isPlainObject(node)) throw new Error();
971
+ if (!isNode(node)) throw new Error();
1184
972
 
1185
973
  if (!node) throw new Error();
1186
974
 
@@ -1192,7 +980,7 @@ export const Path = class AgastPath {
1192
980
  let path = this.parent;
1193
981
  let replacementPath = [];
1194
982
 
1195
- let targetShift_ = this.parent?.parentProperty?.value.tags[0];
983
+ let targetShift_ = this.parent?.parentProperty?.value.tags[1][0];
1196
984
  let targetShift = targetShift_ && (targetShift_.type === ReferenceTag ? null : targetShift_);
1197
985
  let targetReference = this.reference;
1198
986
  let targetBindings = bindings == null ? this.bindings : bindings;
@@ -1207,24 +995,29 @@ export const Path = class AgastPath {
1207
995
 
1208
996
  let { tags, shift } = Tags.getAt(targetParentIndex, getTags(path.node)).value;
1209
997
 
1210
- let firstTag = tags[0];
998
+ let firstTag = tags[1][0];
1211
999
  let newFirstTag = firstTag;
1212
1000
  if (shift) {
1213
1001
  newFirstTag = buildChild(ReferenceTag, targetReference);
1214
1002
  }
1215
1003
 
1216
- let tags_ = [
1217
- firstTag,
1218
- targetBindings.map((binding) => buildChild(BindingTag, binding)),
1219
- value,
1220
- ];
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
+ );
1221
1014
 
1222
1015
  if (!held && !shift) {
1223
1016
  built = buildNode(
1224
1017
  Tags.replaceAt(
1225
1018
  targetParentIndex,
1226
- built.value.tags,
1227
1019
  buildChild(Property, buildProperty(tags_)),
1020
+ built.value.tags,
1228
1021
  ),
1229
1022
  );
1230
1023
  replacementPath.push(targetParentIndex);
@@ -1235,16 +1028,18 @@ export const Path = class AgastPath {
1235
1028
  let nextShift = nextProperty?.type === Property && nextProperty.value.shift;
1236
1029
 
1237
1030
  if (nextShift) {
1238
- let { reference, bindings } = BTree.getAt(-1, nextProperty.value.node.value.bounds[0])
1239
- .property.value;
1031
+ let { reference, bindings } = BList.getAt(
1032
+ -1,
1033
+ nextProperty.value.node.value.bounds.leading,
1034
+ ).property.value;
1240
1035
 
1241
1036
  path = path.push(i);
1242
1037
 
1243
1038
  built = buildNode(
1244
1039
  Tags.replaceAt(
1245
1040
  targetParentIndex - shift.index,
1246
- built.value.tags,
1247
1041
  buildChild(Property, buildProperty(tags_)),
1042
+ built.value.tags,
1248
1043
  ),
1249
1044
  );
1250
1045
 
@@ -1253,17 +1048,14 @@ export const Path = class AgastPath {
1253
1048
  built = buildNode(Tags.removeAt(targetParentIndex + 1, built.value.tags));
1254
1049
  }
1255
1050
 
1256
- tags_ = [newFirstTag, tags_[1], tags_[2]];
1051
+ tags_ = BList.fromValues([newFirstTag, tags_[1][1], tags_[1][2]], 1);
1257
1052
 
1258
1053
  targetShift = null;
1259
1054
  targetReference = reference;
1260
1055
  targetBindings = bindings;
1261
- targetParentIndex = getPropertyTagsIndex(
1262
- nextProperty.value.node,
1263
- reference.type,
1264
- reference.name,
1265
- 0,
1266
- 0,
1056
+ targetParentIndex = Tags.getIndex(
1057
+ buildFullPathSegment(reference.type, reference.name, 0, 0),
1058
+ nextProperty.value.node.value.tags,
1267
1059
  );
1268
1060
  held = null;
1269
1061
 
@@ -1273,8 +1065,8 @@ export const Path = class AgastPath {
1273
1065
  built = buildNode(
1274
1066
  Tags.replaceAt(
1275
1067
  targetParentIndex,
1276
- getTags(built),
1277
1068
  buildChild(Property, buildProperty(tags_, shift)),
1069
+ getTags(built),
1278
1070
  ),
1279
1071
  );
1280
1072
  replacementPath.push(targetParentIndex);
@@ -1284,7 +1076,7 @@ export const Path = class AgastPath {
1284
1076
  held = targetShift ? value : null;
1285
1077
  heldCount = held ? heldCount + 1 : 0;
1286
1078
 
1287
- targetShift_ = path?.parentProperty?.value.tags[0];
1079
+ targetShift_ = path?.parentProperty?.value.tags[1][0];
1288
1080
  targetShift = targetShift_ && (targetShift_.type === ReferenceTag ? null : targetShift_);
1289
1081
  targetReference = path.reference;
1290
1082
  targetBindings = path.bindings;
@@ -1296,7 +1088,131 @@ export const Path = class AgastPath {
1296
1088
  }
1297
1089
 
1298
1090
  replaceAt(path, node, bindings) {
1299
- 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);
1300
1216
  }
1301
1217
 
1302
1218
  mapOnto(rootNode) {
@@ -1322,19 +1238,19 @@ export const Path = class AgastPath {
1322
1238
  let { parent, node } = this;
1323
1239
  let sigilTag = getSigilTag(node);
1324
1240
 
1325
- return !parent && sigilTag && !!(isSelfClosingTag(sigilTag) || getCloseTag(node));
1241
+ return !parent && sigilTag && !!(isSelfClosingTag(sigilTag) || getCloseNodeTag(node));
1326
1242
  }
1327
1243
 
1328
1244
  get held() {
1329
1245
  let tagPath = this.tagPathAt(-1);
1330
1246
 
1331
1247
  // TODO is this safe
1332
- if (tagPath.tag.type !== Property) return null;
1248
+ if (tagPath.type !== Property) return null;
1333
1249
 
1334
- let { tags } = tagPath.tag.value;
1250
+ let { tags } = tagPath.value;
1335
1251
 
1336
- if (tags[0]?.type === ShiftTag && !tags[2]) {
1337
- 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;
1338
1254
  }
1339
1255
 
1340
1256
  // if we're not at the left side, we aren't held
@@ -1351,8 +1267,8 @@ export const Path = class AgastPath {
1351
1267
  for (;;) {
1352
1268
  let refPath = coverParent.propertyKeyTagPath;
1353
1269
 
1354
- if (refPath?.tag.type === ShiftTag) {
1355
- 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;
1356
1272
  }
1357
1273
 
1358
1274
  coverParent = coverParent.parent;
@@ -1365,57 +1281,24 @@ export const Path = class AgastPath {
1365
1281
  advance(tag) {
1366
1282
  if (!tag) throw new Error();
1367
1283
 
1368
- let tagPath = this.tagPathAt(-1, 2);
1284
+ let tagPath = this.tagPathAt(-1, -1);
1369
1285
 
1370
- if (tagPath?.tag.type === Property ? tagPath?.nextSibling : tagPath?.next) throw new Error();
1286
+ if (tagPath?.type === Property ? tagPath?.nextSibling : tagPath?.next) throw new Error();
1371
1287
 
1372
1288
  if (this.done) throw new Error();
1373
1289
 
1374
1290
  let resultPath = this;
1375
1291
 
1376
- switch (tag.type) {
1377
- case BindingTag: {
1378
- if (!this.lastPropertyTagPath) {
1379
- resultPath = this.advanceUnwrapped(buildReferenceTag()).path;
1380
- }
1381
- break;
1382
- }
1292
+ let tag_ = isNode(tag) ? tag : parseTag(tag);
1383
1293
 
1384
- case TreeNode:
1385
- case NullNode:
1386
- case GapNode:
1387
- case NullTag:
1388
- case GapTag:
1389
- case OpenNodeTag: {
1390
- let sigilTag = getSigilTag_(tag);
1391
-
1392
- if (sigilTag.type === OpenNodeTag && sigilTag.value.type === Symbol.for('__')) {
1393
- break;
1394
- }
1395
-
1396
- let lastTagPath = this.lastPropertyTagPath;
1397
- let newProperty = !lastTagPath || propertyIsFull(lastTagPath.tag);
1398
- let reference = lastTagPath?.referenceTagPath.tag.value;
1399
-
1400
- // TODO revisit this condition
1401
- if (!getFlags(this.node)?.token || reference?.type === '@' || tag.type === GapTag) {
1402
- if (newProperty || !lastTagPath.tag.value.tags.length) {
1403
- resultPath = this.advanceUnwrapped(buildReferenceTag()).path;
1404
- }
1405
- }
1406
-
1407
- break;
1408
- }
1409
- }
1410
-
1411
- if (tag.type === Property) {
1412
- 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))));
1413
1296
  } else {
1414
- resultPath = resultPath.advanceUnwrapped(tag).path;
1297
+ resultPath = resultPath.advanceUnwrapped(printTag(tag)).path;
1415
1298
  }
1416
1299
 
1417
1300
  if (
1418
- (tag.type === CloseNodeTag || (tag.type === OpenNodeTag && tag.value.selfClosing)) &&
1301
+ (tag_.type === CloseNodeTag || (tag_.type === OpenNodeTag && tag_.value.selfClosing)) &&
1419
1302
  resultPath.depth
1420
1303
  ) {
1421
1304
  return resultPath.parent;
@@ -1425,14 +1308,13 @@ export const Path = class AgastPath {
1425
1308
  }
1426
1309
 
1427
1310
  advanceUnwrapped(tag) {
1428
- if (!tag) throw new Error();
1311
+ if (!isString(tag) && !isNode(tag)) throw new Error();
1312
+ let tag_ = isString(tag) ? parseTag(tag) : tag;
1429
1313
 
1430
1314
  let tagPath = this.tagPathAt(-1, [-1, -1]);
1431
1315
 
1432
1316
  if (
1433
- [TreeNode, NullNode, GapNode].includes(tagPath?.tag.type)
1434
- ? tagPath?.nextSibling
1435
- : tagPath?.next
1317
+ [TreeNode, NullNode, GapNode].includes(tagPath?.type) ? tagPath?.nextSibling : tagPath?.next
1436
1318
  )
1437
1319
  throw new Error();
1438
1320
 
@@ -1440,11 +1322,11 @@ export const Path = class AgastPath {
1440
1322
 
1441
1323
  let targetPath = tagPath && endsNode(tagPath.tag) ? this.parent : this;
1442
1324
 
1443
- switch (tag.type) {
1325
+ switch (tag_.type) {
1444
1326
  case ReferenceTag: {
1445
- let { type, name, flags } = tag.value;
1327
+ let { type, name, flags } = tag_.value;
1446
1328
 
1447
- if ([ReferenceTag, ShiftTag, BindingTag].includes(targetPath.tagPathAt(-1, -1)?.tag.type))
1329
+ if ([ReferenceTag, ShiftTag, BindingTag].includes(targetPath.tagPathAt(-1, -1)?.type))
1448
1330
  throw new Error('invalid location for reference');
1449
1331
  if (!name && !type) throw new Error();
1450
1332
 
@@ -1457,22 +1339,25 @@ export const Path = class AgastPath {
1457
1339
  if (targetPath.open.type === Symbol.for('_') && !['_', '#'].includes(type))
1458
1340
  throw new Error();
1459
1341
 
1460
- if (tag.value.name) {
1461
- let property = getProperty(tag.value.name, targetPath.node);
1342
+ if (tag_.value.name) {
1343
+ let property = getProperty(tag_.value.name, targetPath.node);
1462
1344
  if (
1463
- getOr(null, tag.value.name, targetPath.node) &&
1345
+ getOr(null, tag_.value.name, targetPath.node) &&
1464
1346
  !property.value.shift &&
1465
- !referencesAreEqual(tag.value, property.value.reference)
1347
+ !Tags.referencesAreEqual(tag_.value, property.value.reference)
1466
1348
  ) {
1467
1349
  throw new Error('mismatched references');
1468
1350
  }
1469
- } else if (tag.value.type === '_') {
1470
- 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
+ );
1471
1356
 
1472
1357
  if (
1473
1358
  rootIdx != null &&
1474
- !referencesAreEqual(
1475
- tag.value,
1359
+ !Tags.referencesAreEqual(
1360
+ tag_.value,
1476
1361
  Tags.getAt(rootIdx, getTags(targetPath.node)).value.reference,
1477
1362
  )
1478
1363
  ) {
@@ -1480,43 +1365,42 @@ export const Path = class AgastPath {
1480
1365
  }
1481
1366
  }
1482
1367
 
1483
- let property = buildChild(Property, buildProperty([tag]));
1368
+ let property = buildChild(Property, buildProperty(BList.fromValues([printTag(tag)])));
1484
1369
 
1485
1370
  targetPath = targetPath.replaceWith(
1486
- buildNode(Tags.push(getTags(targetPath.node), property)),
1371
+ buildNode(Tags.push(property, getTags(targetPath.node))),
1487
1372
  );
1488
1373
  break;
1489
1374
  }
1490
1375
 
1491
1376
  case BindingTag: {
1492
- if (![ReferenceTag, ShiftTag].includes(this.tagPathAt(-1, -1).tag.type)) {
1377
+ if (![ReferenceTag, ShiftTag].includes(parseTagType(this.tagPathAt(-1, -1)))) {
1493
1378
  throw new Error('Invalid location for BindingTag');
1494
1379
  }
1495
- if (!tag.value.segments) throw new Error();
1496
1380
 
1497
1381
  let refPath = this.tagPathAt(-1, 0);
1498
1382
  let propPath = TagPath.from(refPath.path, refPath.tagsIndex);
1499
1383
 
1500
- let { shift } = propPath.tag.value;
1384
+ let { shift } = propPath.value;
1501
1385
 
1502
- if (![ReferenceTag, ShiftTag].includes(refPath.tag.type)) throw new Error();
1386
+ if (![ReferenceTag, ShiftTag].includes(refPath.type)) throw new Error();
1503
1387
 
1504
- if (refPath.tag.type === ShiftTag) {
1388
+ if (refPath.type === ShiftTag) {
1505
1389
  refPath = TagPath.from(refPath.path, refPath.tagsIndex - shift.index, 0);
1506
1390
  }
1507
1391
 
1508
- let { 0: firstTag, 1: bindingTags = [] } = propPath.tag.value.tags;
1509
- 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);
1510
1394
  let property = buildChild(Property, buildProperty(tags, shift));
1511
1395
 
1512
1396
  targetPath = this.replaceWith(
1513
- buildNode(Tags.replaceAt(propPath.tagsIndex, getTags(this.node), property)),
1397
+ buildNode(Tags.replaceAt(propPath.tagsIndex, property, getTags(this.node))),
1514
1398
  );
1515
1399
  break;
1516
1400
  }
1517
1401
 
1518
1402
  case OpenNodeTag: {
1519
- let { literalValue, flags, type } = tag.value;
1403
+ let { literalValue, flags, type, name, attributes } = tag_.value;
1520
1404
 
1521
1405
  if (this.held && flags.token) throw new Error();
1522
1406
 
@@ -1525,22 +1409,36 @@ export const Path = class AgastPath {
1525
1409
  }
1526
1410
 
1527
1411
  let parentProp = this.node && Tags.getAt(-1, getChildren(this.node));
1528
- let ref = parentProp.value.shift
1412
+
1413
+ if (parentProp && propertyIsFull(parentProp)) {
1414
+ parentProp = null;
1415
+ }
1416
+
1417
+ let ref = parentProp?.value.shift
1529
1418
  ? this.node &&
1530
- Tags.getAt(-1 - parentProp.value.shift.index, getChildren(this.node)).value.tags[0]
1531
- .value
1532
- : 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;
1533
1424
 
1534
- if (this.node.value.flags.token && ref.type !== '@') throw new Error();
1425
+ let parentFlags = parentProp?.value.shift ? flags : this.node.value.flags;
1535
1426
 
1536
- let node = buildNode(tag);
1427
+ if (parentFlags.token && ref?.type !== '@') throw new Error();
1537
1428
 
1429
+ let node;
1538
1430
  if (literalValue && !flags.token) {
1539
- 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_);
1540
1439
  }
1541
1440
 
1542
1441
  targetPath = this.advanceUnwrapped(node).path;
1543
-
1544
1442
  targetPath = targetPath.tagPathAt(-1).inner;
1545
1443
 
1546
1444
  break;
@@ -1549,11 +1447,11 @@ export const Path = class AgastPath {
1549
1447
  case GapTag:
1550
1448
  case NullTag: {
1551
1449
  // if (getSigilTag(this.node)) throw new Error();
1552
- if (this.tagPathAt(-1, 0).tag.type !== ReferenceTag) {
1450
+ if (parseTagType(this.tagPathAt(-1, 0)) !== ReferenceTag) {
1553
1451
  throw new Error('Invalid location for NullTag');
1554
1452
  }
1555
1453
 
1556
- let node = buildNode(tag);
1454
+ let node = buildNode(tag_);
1557
1455
 
1558
1456
  targetPath = this.advanceUnwrapped(node).path;
1559
1457
  // targetPath = targetPath.tagPathAt(-1).inner; // ?
@@ -1566,21 +1464,22 @@ export const Path = class AgastPath {
1566
1464
 
1567
1465
  let lastChild = targetPath.getChild(-1);
1568
1466
 
1569
- if (lastChild?.type === Property && !propertyIsFull(lastChild)) throw new Error();
1467
+ if (isObject(lastChild) && lastChild?.type === Property && !propertyIsFull(lastChild))
1468
+ throw new Error();
1570
1469
 
1571
1470
  let openTag = getOpenTag(node);
1572
1471
 
1573
1472
  if (!openTag) throw new Error();
1574
- if (openTag.value.selfClosing) throw new Error();
1473
+ if (parseTag(openTag).value.selfClosing) throw new Error();
1575
1474
 
1576
- targetPath = targetPath.replaceWith(buildNode(Tags.push(getTags(node), tag)));
1475
+ targetPath = targetPath.replaceWith(buildNode(Tags.push(tag_, getTags(node))));
1577
1476
  break;
1578
1477
  }
1579
1478
 
1580
1479
  case NullNode:
1581
1480
  case GapNode:
1582
1481
  case TreeNode: {
1583
- let node = tag;
1482
+ let node = tag_;
1584
1483
  let parentPath = targetPath;
1585
1484
  let { node: parentNode } = parentPath;
1586
1485
 
@@ -1589,12 +1488,12 @@ export const Path = class AgastPath {
1589
1488
  if (isMultiFragment(node)) {
1590
1489
  let existingProperty = tagPath;
1591
1490
 
1592
- while (existingProperty?.tag.type === AttributeDefinition) {
1593
- existingProperty = existingProperty.previousSibling.propertyPath;
1491
+ while (existingProperty?.type === AttributeDefinition) {
1492
+ existingProperty = tagPath.siblingAt(existingProperty.tagsIndex - 1);
1594
1493
  }
1595
1494
 
1596
1495
  if (
1597
- existingProperty.tag.type !== OpenNodeTag &&
1496
+ existingProperty.type !== OpenNodeTag &&
1598
1497
  existingProperty.tag &&
1599
1498
  !propertyIsFull(existingProperty.tag)
1600
1499
  ) {
@@ -1604,7 +1503,7 @@ export const Path = class AgastPath {
1604
1503
  let newPath = Path.from(buildNode(this.node.value.tags));
1605
1504
 
1606
1505
  for (let tag of Tags.traverse(node.value.children)) {
1607
- if (tag.type === Property) {
1506
+ if (parseTagType(tag) === Property) {
1608
1507
  newPath = newPath.advance(tag);
1609
1508
  } else {
1610
1509
  newPath = newPath.advanceUnwrapped(tag).path.atDepth(0);
@@ -1615,11 +1514,11 @@ export const Path = class AgastPath {
1615
1514
  break;
1616
1515
  }
1617
1516
 
1618
- let shift = tagPath.tag.type === Property ? tagPath.tag.value.shift : undefined;
1517
+ let shift = tagPath.type === Property ? tagPath.value.shift : undefined;
1619
1518
  let { held } = this;
1620
1519
 
1621
1520
  if (held) {
1622
- // let heldProperty = this.tagPathAt(-2, 2).tag.value;
1521
+ // let heldProperty = this.tagPathAt(-2, 2).value;
1623
1522
  let matchesHeld = isGapNode(node);
1624
1523
  if (getRoot(node) && nodeIsComplete(node)) {
1625
1524
  matchesHeld =
@@ -1627,8 +1526,10 @@ export const Path = class AgastPath {
1627
1526
  isGapNode(node) ||
1628
1527
  held.node === node ||
1629
1528
  held.node ===
1630
- BTree.getAt(-BTree.getSize(held.node.value.bounds[0]), node.value.bounds[0])
1631
- .property.value.node;
1529
+ BList.getAt(
1530
+ -BList.getSize(held.node.value.bounds.leading),
1531
+ node.value.bounds.leading,
1532
+ ).property.value.node;
1632
1533
  if (!matchesHeld) {
1633
1534
  // TODO We're passing by the node that shifted if it's a cover!
1634
1535
  throw new Error();
@@ -1637,17 +1538,17 @@ export const Path = class AgastPath {
1637
1538
  }
1638
1539
 
1639
1540
  if (propertyIsFull(tagPath.tag)) {
1640
- let tags = [null, [], node];
1541
+ let tags = BList.fromValues([Tags.fromValues([]), Tags.fromValues([]), node], 1);
1641
1542
  let property = buildChild(Property, buildProperty(tags, shift));
1642
1543
 
1643
- targetPath = this.replaceWith(buildNode(Tags.push(parentNode.value.tags, property)));
1544
+ targetPath = this.replaceWith(buildNode(Tags.push(property, parentNode.value.tags)));
1644
1545
  } else {
1645
- let { 0: firstTag, 1: bindingTags = [] } = tagPath.tag.value.tags;
1646
- 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);
1647
1548
  let property = buildChild(Property, buildProperty(tags, shift));
1648
1549
 
1649
1550
  targetPath = this.replaceWith(
1650
- buildNode(Tags.replaceAt(tagPath.tagsIndex, parentNode.value.tags, property)),
1551
+ buildNode(Tags.replaceAt(tagPath.tagsIndex, property, parentNode.value.tags)),
1651
1552
  );
1652
1553
  }
1653
1554
 
@@ -1663,10 +1564,10 @@ export const Path = class AgastPath {
1663
1564
 
1664
1565
  // add undefined attributes from value
1665
1566
  let { node } = this;
1666
- if (tag.type !== AttributeDefinition) throw new Error();
1567
+ if (tag_.type !== AttributeDefinition) throw new Error();
1667
1568
 
1668
- let { path, value } = tag.value;
1669
- let openTag = getOpenTag(node);
1569
+ let { path, value } = tag_.value;
1570
+ let openTag = parseTag(getOpenTag(node));
1670
1571
  let { attributes } = node.value;
1671
1572
 
1672
1573
  if (!objHas(attributes, path) && objGet(attributes, path) !== undefined)
@@ -1676,10 +1577,10 @@ export const Path = class AgastPath {
1676
1577
 
1677
1578
  let { flags, name } = openTag.value;
1678
1579
  attributes = immSet(attributes, path, value);
1679
- let newOpenTag = buildOpenNodeTag(flags, name, null, attributes);
1580
+ let newOpenTag = buildOpenNodeTag(flags, name, null, printObject(attributes));
1680
1581
 
1681
1582
  targetPath = this.replaceWith(
1682
- buildNode(Tags.push(Tags.replaceAt(0, getTags(node), newOpenTag), tag)),
1583
+ buildNode(Tags.push(tag_, Tags.replaceAt(0, newOpenTag, getTags(node)))),
1683
1584
  );
1684
1585
  break;
1685
1586
  }
@@ -1692,7 +1593,7 @@ export const Path = class AgastPath {
1692
1593
  let { reference, shift: heldShift } = lastPropertyTag.value;
1693
1594
 
1694
1595
  if (!reference) {
1695
- ({ reference } = this.tagPathAt(-1 - (heldShift?.index ?? 0)).tag.value);
1596
+ ({ reference } = this.tagPathAt(-1 - (heldShift?.index ?? 0)).value);
1696
1597
  }
1697
1598
 
1698
1599
  if (!reference.flags.expression && reference.type !== '_') throw new Error();
@@ -1701,55 +1602,114 @@ export const Path = class AgastPath {
1701
1602
  ? buildShift(heldShift.index + 1, heldShift.height + 1)
1702
1603
  : buildShift(1, 3);
1703
1604
 
1704
- let property = buildChild(Property, buildProperty([tag], shift));
1605
+ let property = buildChild(Property, buildProperty(BList.fromValues([tag]), shift));
1705
1606
 
1706
- targetPath = this.replaceWith(buildNode(Tags.push(this.node.value.tags, property)));
1607
+ targetPath = this.replaceWith(buildNode(Tags.push(property, this.node.value.tags)));
1707
1608
  break;
1708
1609
  }
1709
1610
 
1710
1611
  case LiteralTag:
1711
- if (typeof tag.value !== 'string') throw new Error();
1612
+ if (typeof tag_.value !== 'string') throw new Error();
1712
1613
 
1713
- targetPath = this.replaceWith(buildNode(Tags.push(this.node.value.tags, tag)));
1614
+ targetPath = this.replaceWith(buildNode(Tags.push(tag_, this.node.value.tags)));
1714
1615
  break;
1715
1616
 
1716
1617
  default:
1717
1618
  throw new Error();
1718
1619
  }
1719
1620
 
1720
- return targetPath && TagPath.from(targetPath, -1, isNodeTag(tag) ? -1 : offsetForTag(tag));
1621
+ return targetPath && TagPath.from(targetPath, -1, offsetForTag(tag_));
1721
1622
  }
1722
1623
  };
1723
1624
 
1724
- Object.freeze(Path.prototype);
1625
+ freezeClass(Path);
1725
1626
 
1726
1627
  export const tagPathsAreEqual = (a, b) => {
1727
1628
  if (a == null || b == null) return b == a;
1728
1629
  return a.path.node === b.path.node && a.tagsIndex === b.tagsIndex;
1729
1630
  };
1730
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
+
1731
1654
  export class TagPath {
1732
1655
  static from(path, tagsIndex, propertyIndex) {
1733
1656
  let tags = getTags(path.node);
1734
1657
  let size = Tags.getSize(tags);
1735
1658
  let index = Number.isFinite(tagsIndex) && tagsIndex < 0 ? size + tagsIndex : tagsIndex;
1736
-
1737
- let propertyIndex_ = typeof propertyIndex === 'number' ? [propertyIndex] : propertyIndex;
1738
-
1739
- let propTag = Tags.getAt(index, tags);
1659
+ let propertyIndex_ = propertyIndex;
1660
+ let propTag_ = Tags.getAt(index, tags);
1661
+ let propTag = propTag_ && parseTag(propTag_);
1740
1662
 
1741
1663
  if (
1742
1664
  [OpenNodeTag, CloseNodeTag, LiteralTag, AttributeDefinition, GapTag, NullTag].includes(
1743
1665
  propTag?.type,
1744
1666
  )
1745
1667
  ) {
1746
- propertyIndex_ = [];
1668
+ propertyIndex_ = null;
1747
1669
  }
1748
1670
 
1749
- let resolvedPropertyIndex =
1750
- 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
+ }
1751
1698
 
1752
- return resolvedPropertyIndex && new TagPath(path, index, resolvedPropertyIndex);
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);
1709
+
1710
+ let pathExists = isArray(propertyIndex_) && propertyIndex_?.length ? idx.length : propTag;
1711
+
1712
+ return pathExists ? new TagPath(path, index, idx) : null;
1753
1713
  }
1754
1714
 
1755
1715
  static fromNode(node, tagsIndex, propertyIndex) {
@@ -1774,28 +1734,26 @@ export class TagPath {
1774
1734
  if (!Number.isFinite(tagsIndex) || tagsIndex < 0) throw new Error();
1775
1735
  if (!isArray(propertyIndex)) throw new Error();
1776
1736
 
1777
- let propertyIndex_ = Object.freeze([...propertyIndex]);
1737
+ for (let index of recordValues(propertyIndex)) {
1738
+ if (!(index >= 0) || !Number.isFinite(index)) throw new Error();
1739
+ }
1778
1740
 
1779
1741
  let tag = Tags.getAt(tagsIndex, getTags(path.node));
1780
1742
 
1781
- if (propertyIndex_.length) {
1782
- tag = Tags.getAt(propertyIndex_, tag.value.tags);
1743
+ if (propertyIndex.length) {
1744
+ tag = Tags.getAt(propertyIndex, tag.value.tags);
1783
1745
  }
1784
1746
 
1785
1747
  if (tag == null || isArray(tag)) throw new Error();
1786
1748
 
1787
- for (let segment of propertyIndex_) {
1788
- if (!isPlainObject(segment) || !(segment.index === 0 || segment.index > 0)) throw new Error();
1789
- }
1790
-
1791
1749
  this.path = path;
1792
1750
  this.tagsIndex = tagsIndex;
1793
- this.propertyIndex = propertyIndex_;
1751
+ this.propertyIndex = propertyIndex;
1794
1752
 
1795
1753
  this.node = path.node;
1796
1754
  this.tag = tag;
1797
1755
 
1798
- if (tag.type === Property && propertyIndex_.length) throw new Error();
1756
+ if (isObject(tag) && tag.type === Property && propertyIndex.length) throw new Error();
1799
1757
 
1800
1758
  freeze(this);
1801
1759
  }
@@ -1803,13 +1761,21 @@ export class TagPath {
1803
1761
  asPrimitive() {
1804
1762
  let { path, tagsIndex, propertyIndex } = this;
1805
1763
 
1806
- return Object.freeze({ path: path.asPrimitive(), tagsIndex, propertyIndex });
1764
+ return freeze({ path: path.asPrimitive(), tagsIndex, propertyIndex });
1807
1765
  }
1808
1766
 
1809
1767
  get child() {
1810
1768
  return this.tag;
1811
1769
  }
1812
1770
 
1771
+ get type() {
1772
+ return parseTagType(this.tag);
1773
+ }
1774
+
1775
+ get value() {
1776
+ return parseTag(this.tag).value;
1777
+ }
1778
+
1813
1779
  get parentNode() {
1814
1780
  return this.path.parentNode;
1815
1781
  }
@@ -1831,12 +1797,12 @@ export class TagPath {
1831
1797
  get referenceTagPath() {
1832
1798
  let path = TagPath.from(this.path, this.tagsIndex, 0);
1833
1799
  if (path && path.propertyPath) {
1834
- let { shift } = path.propertyPath.tag.value;
1800
+ let { shift } = path.propertyPath.value;
1835
1801
  if (shift) {
1836
1802
  path = TagPath.from(this.path, this.tagsIndex - shift.index, 0);
1837
1803
  }
1838
1804
  }
1839
- return path?.tag.type === ReferenceTag ? path : null;
1805
+ return path?.type === ReferenceTag ? path : null;
1840
1806
  }
1841
1807
 
1842
1808
  get nextProperty() {
@@ -1844,33 +1810,33 @@ export class TagPath {
1844
1810
 
1845
1811
  let nextChild = TagPath.from(path, tagsIndex + 1);
1846
1812
 
1847
- return nextChild?.tag.type === Property ? nextChild : null;
1813
+ return nextChild?.type === Property ? nextChild : null;
1848
1814
  }
1849
1815
 
1850
1816
  get nextSibling() {
1851
1817
  let { path, tagsIndex, propertyIndex } = this;
1852
1818
 
1853
- let propertyIndex0 = propertyIndex[0]?.index;
1819
+ let propertyIndex0 = propertyIndex[0];
1854
1820
 
1855
- let nextChildIndex = tagsIndex === 0 ? 0 : tagsIndex - 1 + 1;
1821
+ let nextChildIndex = tagsIndex;
1856
1822
  let nextChild = path.getChild(nextChildIndex);
1857
1823
 
1858
1824
  if (propertyIndex0 == null && !nextChild) {
1859
- return this.tag.type === CloseNodeTag ? null : path.closeTagPath;
1825
+ return this.type === CloseNodeTag ? null : path.closeTagPath;
1860
1826
  }
1861
1827
 
1862
1828
  switch (propertyIndex0) {
1863
1829
  case 0:
1864
1830
  case 1: {
1865
1831
  let childIndex = tagsIndex - 1;
1866
- let bindingIndex = propertyIndex[1]?.index ?? -1;
1832
+ let bindingIndex = propertyIndex[1] ?? -1;
1867
1833
  let binding = path.getBindingTagPath(childIndex, bindingIndex + 1);
1868
1834
  if (binding) return binding;
1869
1835
  return path.getNodeTagPath(childIndex);
1870
1836
  }
1871
1837
  case 2:
1872
1838
  default: {
1873
- if (!nextChild || nextChild.type !== Property) {
1839
+ if (!nextChild || nextChild.type !== Property || !propertyIndex.length) {
1874
1840
  return path.tagPathAt(tagsIndex + 1);
1875
1841
  } else {
1876
1842
  let ref = path.getReferenceTagPath(nextChildIndex);
@@ -1917,38 +1883,34 @@ export class TagPath {
1917
1883
  get next() {
1918
1884
  let { path, tagsIndex, propertyIndex } = this;
1919
1885
 
1920
- propertyIndex = [...propertyIndex];
1886
+ propertyIndex = [...recordValues(propertyIndex)];
1921
1887
 
1922
1888
  let leaving = false;
1923
1889
 
1924
1890
  for (;;) {
1925
- let tag = Tags.getAt(tagsIndex, getTags(path.node));
1891
+ let tag = parseTag(Tags.getAt(tagsIndex, getTags(path.node)));
1926
1892
  let propTag = tag;
1927
1893
  let lastRef = null;
1894
+ let wasLeaving = leaving;
1928
1895
  leaving = false;
1929
1896
 
1930
1897
  if (!tag) return null;
1931
1898
 
1932
- if (tag.type === Property) {
1933
- if (propertyIndex.length === 0 && !leaving) {
1934
- propertyIndex.push({ index: 0, node: tag.value.tags });
1935
- }
1899
+ if (isObject(tag) && tag.type === Property && !wasLeaving) {
1900
+ if (propertyIndex.length === 0) {
1901
+ let { 0: open, 1: bindings } = tag.value.tags[1];
1936
1902
 
1937
- if (propertyIndex != null) {
1938
- propTag = Tags.getAt(propertyIndex, tag.value.tags);
1939
- 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 });
1940
1906
  }
1941
- }
1942
1907
 
1943
- if (isArray(propTag)) {
1944
- if (propTag.length) {
1945
- // enter bindings array
1946
- propertyIndex.push({ index: 0, node: propTag });
1947
- propTag = propTag[propertyIndex[1].index];
1948
- } else {
1949
- 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_);
1950
1913
  }
1951
- continue;
1952
1914
  }
1953
1915
 
1954
1916
  let isInitialTag =
@@ -1957,21 +1919,21 @@ export class TagPath {
1957
1919
  arraysEqual(propertyIndex, this.propertyIndex);
1958
1920
 
1959
1921
  // done
1960
- if (!isInitialTag && !isNodeTag(propTag)) {
1922
+ if (!isInitialTag && !isNodeTag(propTag) && propTag.type !== Property) {
1961
1923
  return TagPath.from(path, tagsIndex, propertyIndex);
1962
1924
  }
1963
1925
 
1964
1926
  // shift
1965
- if (propTag.type === BindingTag && lastRef.type === ShiftTag) {
1927
+ if (propTag.type === BindingTag && lastRef?.type === ShiftTag) {
1966
1928
  let lastProp = Tags.getAt(tagsIndex, path.tags);
1967
1929
  let { shift } = lastProp.value;
1968
1930
  let refIndex = tagsIndex - shift.index;
1969
1931
  if (refIndex < 0) throw new Error();
1970
- let refTag = Tags.getAt(refIndex, path.tags, 0);
1932
+ let refTag = parseTag(Tags.getAt(refIndex, path.tags, 0));
1971
1933
 
1972
1934
  if (refTag.type !== ReferenceTag) throw new Error();
1973
1935
 
1974
- let { node } = tag.value;
1936
+ let { node } = parseTag(tag).value;
1975
1937
  if (!node) return null;
1976
1938
 
1977
1939
  path = new Path(path, tagsIndex);
@@ -1983,66 +1945,47 @@ export class TagPath {
1983
1945
  continue;
1984
1946
  }
1985
1947
 
1986
- 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
+ }
1987
1972
 
1988
1973
  // over
1974
+ let { nextSibling } = TagPath.from(path, tagsIndex, freeze([...arrayValues(propertyIndex)]));
1975
+
1989
1976
  if (nextSibling) {
1990
1977
  ({ tagsIndex, propertyIndex } = nextSibling);
1991
- // in
1992
- if (isNodeTag(nextSibling.tag)) {
1993
- let shiftPath = path;
1994
-
1995
- while (
1996
- shiftPath.parent &&
1997
- !Tags.getAt(shiftPath.parentIndex - 1, shiftPath.parent.node.value.children).value.shift
1998
- ) {
1999
- shiftPath = shiftPath.parent;
2000
- }
2001
-
2002
- let isGap =
2003
- path.parent &&
2004
- tagsIndex === 1 &&
2005
- shiftPath.parent &&
2006
- Tags.getAt(shiftPath.parentIndex - 1, shiftPath.parent.node.value.children).value
2007
- .shift &&
2008
- tag.value.node ===
2009
- Tags.getAt(shiftPath.parentIndex - 2, shiftPath.parent.node.value.children).value
2010
- .node;
2011
-
2012
- path = path.push(tagsIndex, isGap);
2013
- tagsIndex = 0;
2014
- propertyIndex = [];
2015
- continue;
2016
- } else {
2017
- return nextSibling;
2018
- }
1978
+ propertyIndex = [...arrayValues(propertyIndex)];
1979
+ continue;
2019
1980
  }
2020
1981
 
2021
1982
  // out
2022
1983
  if (path.parentIndex != null && path.parent) {
2023
- do {
2024
- if (
2025
- path.coverBoundary.parent &&
2026
- Tags.getAt(
2027
- [path.coverBoundary.parentIndex, 0],
2028
- path.coverBoundary.parent.node.value.tags,
2029
- )?.type === ShiftTag
2030
- ) {
2031
- // while
2032
- tagsIndex =
2033
- Tags.getSize(path.parent.node.value.tags) > path.parentIndex + 1
2034
- ? path.parentIndex + 1
2035
- : null;
2036
- } else {
2037
- tagsIndex = path.parentIndex + 1;
2038
- }
2039
-
2040
- propertyIndex = [];
2041
- path = path.parent;
2042
- leaving = true;
1984
+ tagsIndex = path.parentIndex;
1985
+ propertyIndex = [];
1986
+ path = path.parent;
2043
1987
 
2044
- if (!path) return null;
2045
- } while (tagsIndex == null);
1988
+ if (!path) return null;
2046
1989
 
2047
1990
  leaving = true;
2048
1991
  continue;
@@ -2055,21 +1998,23 @@ export class TagPath {
2055
1998
  get nextUnshifted() {
2056
1999
  let { path, tagsIndex, propertyIndex } = this;
2057
2000
 
2058
- propertyIndex = [...propertyIndex];
2001
+ propertyIndex = [...arrayValues(propertyIndex)];
2059
2002
 
2060
2003
  let leaving = false;
2061
2004
 
2062
2005
  for (;;) {
2063
- let tag = Tags.getAt(tagsIndex, getTags(path.node));
2006
+ let tag = parseTag(Tags.getAt(tagsIndex, getTags(path.node)));
2064
2007
  let propTag = tag;
2065
2008
  let wasLeaving = leaving;
2066
2009
  leaving = false;
2067
2010
 
2068
2011
  if (!tag) return null;
2069
2012
 
2070
- if (tag.type === Property) {
2013
+ if (isObject(tag) && tag.type === Property) {
2071
2014
  if (propertyIndex.length === 0 && !leaving) {
2072
- 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 });
2073
2018
  }
2074
2019
 
2075
2020
  if (propertyIndex != null) {
@@ -2078,7 +2023,7 @@ export class TagPath {
2078
2023
  }
2079
2024
 
2080
2025
  if (isArray(propTag)) {
2081
- if (propTag.length) {
2026
+ if (Tags.getSize(propTag)) {
2082
2027
  // enter bindings array
2083
2028
  propertyIndex.push({ index: 0, node: propTag });
2084
2029
  propTag = propTag[propertyIndex[1].index];
@@ -2098,7 +2043,7 @@ export class TagPath {
2098
2043
  tagsIndex === this.tagsIndex &&
2099
2044
  arraysEqual(propertyIndex, this.propertyIndex);
2100
2045
 
2101
- if (!wasLeaving && propTag.type === ReferenceTag && propTag.value.flags.expression) {
2046
+ if (!wasLeaving && parseTagType(propTag) === ReferenceTag) {
2102
2047
  // move past shifts
2103
2048
 
2104
2049
  let shifts = 0;
@@ -2126,6 +2071,7 @@ export class TagPath {
2126
2071
  // over
2127
2072
  if (nextSibling) {
2128
2073
  ({ tagsIndex, propertyIndex } = nextSibling);
2074
+ propertyIndex = [...arrayValues(propertyIndex)];
2129
2075
  continue;
2130
2076
  }
2131
2077
 
@@ -2160,7 +2106,7 @@ export class TagPath {
2160
2106
 
2161
2107
  let tag = Tags.getAt(tagsIndex, getTags(path.node));
2162
2108
 
2163
- return tag.type === Property ? path.tagPathAt(tagsIndex) : null;
2109
+ return isObject(tag) && tag.type === Property ? path.tagPathAt(tagsIndex) : null;
2164
2110
  }
2165
2111
 
2166
2112
  get innerNode() {
@@ -2168,22 +2114,22 @@ export class TagPath {
2168
2114
  }
2169
2115
 
2170
2116
  get inner() {
2171
- let { tagsIndex, tag } = this;
2117
+ let { tagsIndex, type } = this;
2172
2118
 
2173
2119
  let node;
2174
- switch (tag.type) {
2120
+ switch (type) {
2175
2121
  case Property: {
2176
- ({ node } = tag.value);
2122
+ ({ node } = this.tag.value);
2177
2123
  break;
2178
2124
  }
2179
2125
  case GapNode:
2180
2126
  case NullNode:
2181
2127
  case TreeNode: {
2182
- node = tag;
2128
+ node = this.tag;
2183
2129
  break;
2184
2130
  }
2185
2131
  default:
2186
- throw new Error();
2132
+ node = null;
2187
2133
  }
2188
2134
  return node && this.path.push(tagsIndex);
2189
2135
  }
@@ -2197,4 +2143,4 @@ export class TagPath {
2197
2143
  }
2198
2144
  }
2199
2145
 
2200
- Object.freeze(TagPath.prototype);
2146
+ freezeClass(TagPath);