@bablr/agast-helpers 0.8.0 → 0.9.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,19 +1,42 @@
1
1
  import { WeakStackFrame } from '@bablr/weak-stack';
2
- import * as btree from '@bablr/agast-helpers/btree';
3
- import * as Children from './children.js';
2
+ import * as BTree from '@bablr/agast-helpers/btree';
3
+ import * as Tags from './tags.js';
4
4
  import {
5
5
  ReferenceTag,
6
6
  InitializerTag,
7
7
  DoctypeTag,
8
- OpenNodeTag,
9
8
  CloseNodeTag,
10
9
  GapTag,
11
10
  NullTag,
12
11
  ShiftTag,
13
12
  BindingTag,
13
+ PropertyWrapper,
14
14
  Property,
15
15
  } from './symbols.js';
16
- import { isString } from './object.js';
16
+ import { isPlainObject, isString } from './object.js';
17
+ import { buildBinding, buildChild, buildProperty, buildPropertyWrapper } from './builders.js';
18
+ import { buildReferenceTag, finalizeNode } from './tree.js';
19
+
20
+ export const wrapperIsFull = (wrapper) => {
21
+ let { tags } = wrapper.value;
22
+
23
+ if (tags[1]?.type === InitializerTag) {
24
+ return true;
25
+ } else {
26
+ return tags.length === 3;
27
+ }
28
+ };
29
+
30
+ export const offsetForTag = (tag) => {
31
+ switch (tag.type) {
32
+ case ReferenceTag:
33
+ case ShiftTag:
34
+ return 0;
35
+ case BindingTag:
36
+ case InitializerTag:
37
+ return 1;
38
+ }
39
+ };
17
40
 
18
41
  export const buildPathSegment = (name, index = null, shiftIndex = null) => {
19
42
  if (!name) throw new Error();
@@ -29,18 +52,26 @@ export const buildFullPathSegment = (type, name, index = null, shiftIndex = null
29
52
  return { type, name, index, shiftIndex };
30
53
  };
31
54
 
32
- export const getRoot = (node, index = 0) => {
55
+ export const getRootProperty = (node, index = 0) => {
33
56
  if (node == null || !isFragmentNode(node)) {
34
- return node;
57
+ return null;
35
58
  }
36
- let idx = getPropertyChildrenIndex(node, '.', null, index);
59
+ let idx = getPropertyTagsIndex(node, '.', null, index);
37
60
 
38
61
  if (idx == null) return null;
39
62
 
40
- let tag = Children.getAt(idx + 2, node.children);
63
+ let tag = Tags.getAt(idx, node.tags);
64
+
65
+ if (tag.type !== PropertyWrapper) throw new Error();
66
+ return tag.value.property;
67
+ };
68
+
69
+ export const getRoot = (node, index = 0) => {
70
+ if (!isFragmentNode(node)) {
71
+ return node;
72
+ }
41
73
 
42
- if (tag.type !== Property) throw new Error();
43
- return tag.value.node;
74
+ return getRootProperty(node, index)?.node;
44
75
  };
45
76
 
46
77
  export const getRootArray = (node) => {
@@ -57,62 +88,67 @@ export const getRootArray = (node) => {
57
88
  };
58
89
 
59
90
  export const getOpenTag = (node) => {
60
- if (!node.children.length) return null;
61
- let tag = Children.getAt(0, node.children);
62
- if (tag.type === NullTag || tag.type === GapTag) return null;
63
- if (tag.type === DoctypeTag) {
64
- tag = Children.getAt(1, node.children);
65
- }
66
- if (tag && tag.type !== OpenNodeTag) throw new Error();
67
- return tag;
91
+ return Tags.getValues(node.tags)[0];
68
92
  };
69
93
 
70
94
  export const getCloseTag = (node) => {
71
- const { children } = node;
72
- const tag = Children.getAt(-1, children);
73
- if (tag.type !== CloseNodeTag) return null;
74
- return tag;
95
+ return Tags.getValues(node.tags)[2];
75
96
  };
76
97
 
77
98
  export const isNullNode = (node) => {
78
- return node && node.type === null && Children.getAt(0, node.children).type === NullTag;
99
+ return node && node.type === null && Tags.getAt(0, node.tags).type === NullTag;
79
100
  };
80
101
 
81
102
  export const isFragmentNode = (node) => {
82
- return node && node.type === null && getOpenTag(node)?.value.type === null;
103
+ return node && node.type === null && getOpenTag(node)?.value?.type === null;
83
104
  };
84
105
 
85
106
  export const isGapNode = (node) => {
86
- return node && node.type === null && Children.getAt(0, node.children).type === GapTag;
107
+ return node && node.type === null && Tags.getAt(0, node.tags).type === GapTag;
87
108
  };
88
109
 
89
110
  export const isStubNode = (node) => {
90
- return (
91
- node && node.type === null && [GapTag, NullTag].includes(Children.getAt(0, node.children).type)
92
- );
111
+ return node && node.type === null && [GapTag, NullTag].includes(Tags.getAt(0, node.tags).type);
112
+ };
113
+
114
+ export const isStubTag = (tag) => {
115
+ return [GapTag, NullTag].includes(tag.type);
93
116
  };
94
117
 
95
- export const getChildPropertyIndex = (agAstNode, childrenIndex) => {
96
- let child = Children.getAt(childrenIndex, agAstNode.children);
118
+ export const getChildPropertyIndex = (agAstNode, tagsIndex) => {
119
+ let child = Tags.getAt(tagsIndex, agAstNode.tags);
120
+
121
+ if (child.type !== PropertyWrapper) return null;
97
122
 
98
- let refIndex = child.type === ShiftTag ? childrenIndex - child.value.index * 3 : childrenIndex;
123
+ let tag = child.value.tags[0];
99
124
 
100
- let stack = Children.findPath(refIndex, agAstNode.children);
125
+ let refIndex = tag.type === ShiftTag ? tagsIndex - tag.value.index : tagsIndex;
126
+
127
+ let stack = Tags.findPath(refIndex, agAstNode.tags);
101
128
  let { node, index: leafIdx } = stack.value;
102
- let leaf = Children.getAt(leafIdx, node);
129
+ let leaf = Tags.getAt(leafIdx, node);
130
+
131
+ if (leaf.type !== PropertyWrapper) {
132
+ return null;
133
+ }
103
134
 
104
- if (leaf.type !== ReferenceTag) return null;
135
+ let leafRefTag = leaf.value.tags[0];
105
136
 
106
- let { name, isArray } = leaf.value;
137
+ if (leafRefTag.type !== ReferenceTag) return null;
138
+
139
+ let { name, isArray } = leafRefTag.value;
107
140
  let count = -1;
108
141
 
109
142
  if (!isArray) return null;
110
143
 
111
144
  for (let i = leafIdx; i >= 0; i--) {
112
- let value = Children.getAt(i, node);
145
+ let value = Tags.getAt(i, node);
146
+ if (value.type === PropertyWrapper) {
147
+ let firstTag = value.value.tags[0];
113
148
 
114
- if (value.type === ReferenceTag && value.value.name === name) {
115
- count++;
149
+ if (firstTag.type === ReferenceTag && firstTag.value.name === name) {
150
+ count++;
151
+ }
116
152
  }
117
153
  }
118
154
  stack = stack.pop();
@@ -123,11 +159,17 @@ export const getChildPropertyIndex = (agAstNode, childrenIndex) => {
123
159
 
124
160
  do {
125
161
  for (let i = leafIdx - 1; i >= 0; i--) {
126
- let childNode = Children.getValues(node)[i];
127
- let { references } = Children.getSums(childNode);
162
+ let value = Tags.getValues(node)[i];
128
163
 
129
- if (hasOwn(references, name)) {
130
- count += references[name];
164
+ if (Array.isArray(value)) {
165
+ let childNode = value;
166
+ let { references } = Tags.getSums(childNode);
167
+
168
+ let res;
169
+ if ((res = Tags.getAtName(name, references))) {
170
+ count += res;
171
+ }
172
+ } else {
131
173
  }
132
174
  }
133
175
  stack = stack.pop();
@@ -141,69 +183,129 @@ export const getChildPropertyIndex = (agAstNode, childrenIndex) => {
141
183
  return count - 1;
142
184
  };
143
185
 
144
- export const getPropertyChildrenIndex = (agAstNode, type, name, index) => {
145
- let firstRefIndex = __getPropertyChildrenIndex(agAstNode, type, name, 0);
146
- let nextTag = Children.getAt(firstRefIndex + 1, agAstNode.children);
147
- let initializerOffset = nextTag.type === InitializerTag ? 1 : 0;
148
-
149
- if (isArray) {
150
- if (index == null) {
151
- if (type) {
152
- index = btree.getSums(agAstNode.children).specialTypes[type] - 1 - initializerOffset;
186
+ export const getPropertyTagsIndex = (agAstNode, type, name, index, shiftIndex) => {
187
+ let firstPropsIndex = __getPropertyTagsIndex(agAstNode, type, name, 0);
188
+ let prop = firstPropsIndex == null ? null : Tags.getAt(firstPropsIndex, agAstNode.tags);
189
+ let initializerOffset = prop?.value.tags[1]?.type === InitializerTag ? 1 : 0;
190
+ let ref = prop?.value.property.reference;
191
+ let sums = Tags.getSums(agAstNode.tags);
192
+
193
+ if (ref?.isArray) {
194
+ if (index < 0) {
195
+ if (name) {
196
+ index = Tags.getAtName(name, sums.references) - initializerOffset + index;
153
197
  } else {
154
- index = btree.getSums(agAstNode.children).references[name] - 1 - initializerOffset;
198
+ index = Tags.getAtName(type, sums.specialTypes) - initializerOffset + index;
155
199
  }
200
+ } else if (index == null) {
201
+ if (name) {
202
+ index = Tags.getAtName(name, sums.references) - 1 - initializerOffset;
203
+ } else {
204
+ index = Tags.getAtName(type, sums.specialTypes) - 1 - initializerOffset;
205
+ }
206
+
207
+ if (index < 0) return null;
208
+ }
209
+ }
210
+
211
+ let parentIndex = __getPropertyTagsIndex(agAstNode, type, name, (index ?? 0) + initializerOffset);
212
+
213
+ if (ref?.flags.expression) {
214
+ let shiftIndex_ = shiftIndex == null ? -1 : shiftIndex;
215
+ if (shiftIndex_ < 0) {
216
+ let shifts = 0;
217
+ // TODO speed this up for deeply nested shifts
218
+ // algorithm: make big jump forward, then look at shift index to see if we overshot completely
219
+ // works because we know the size of the thing and a bigger thing doesn't fit in a smaller one
220
+ while (Tags.getAt(parentIndex + shifts + 1, agAstNode.tags, 0)?.type === ShiftTag) {
221
+ shifts++;
222
+ }
223
+
224
+ if (-shiftIndex_ > shifts + 1) return null;
225
+
226
+ return parentIndex + shifts + shiftIndex_ + 1;
227
+ } else {
228
+ if (parentIndex + shiftIndex_ >= Tags.getSize(agAstNode.tags)) {
229
+ return null;
230
+ }
231
+
232
+ return parentIndex + shiftIndex_;
156
233
  }
157
234
  }
158
235
 
159
- return __getPropertyChildrenIndex(agAstNode, type, name, index + initializerOffset);
236
+ return parentIndex;
160
237
  };
161
238
 
162
- export const getInitializerChildrenIndex = (agAstNode, reference) => {
163
- let { type, name } = reference.value;
164
- return __getPropertyChildrenIndex(agAstNode, type, name, 0);
239
+ export const getInitializerTagsIndex = (agAstNode, referenceTag) => {
240
+ let { type, name } = referenceTag.value;
241
+
242
+ if (type === '#') return null;
243
+
244
+ let index = __getPropertyTagsIndex(agAstNode, type, name, 0);
245
+
246
+ if (index == null) return null;
247
+
248
+ let nextTag = Tags.getAt(index, agAstNode.tags, 1);
249
+
250
+ return nextTag?.type === InitializerTag ? index : null;
165
251
  };
166
252
 
167
- const __getPropertyChildrenIndex = (agAstNode, type, name, index) => {
253
+ const __getPropertyTagsIndex = (agAstNode, type, name, index) => {
168
254
  let nameCount = 0;
169
- let node = agAstNode.children;
255
+ let node = Tags.getValues(agAstNode.tags)[1];
170
256
  let idx = -1;
171
257
 
172
258
  // drill into subtrees, passing over subtrees with too few references of the desired name
173
259
  outer: while (node) {
174
- let isLeaf = Children.isLeafNode(node);
175
- let sums = Children.getSums(node);
176
- let valueNameCount = type == null ? sums.references[name] : sums.specialTypes[type];
260
+ let sums = Tags.getSums(node);
261
+
262
+ if (!sums) return null;
263
+
264
+ let valueNameCount =
265
+ name != null
266
+ ? Tags.getAtName(name, sums.references)
267
+ : Tags.getAtName(type, sums.specialTypes);
177
268
 
178
269
  if (nameCount + valueNameCount < index) {
179
270
  return null;
180
271
  }
181
272
 
182
- for (const value of Children.getValues(node)) {
183
- if (isLeaf) {
273
+ for (const value of Tags.getValues(node)) {
274
+ if (!isArray(value)) {
184
275
  idx++;
185
276
  let tag = value;
186
- if (
187
- tag.type === ReferenceTag &&
188
- ((name != null && tag.value.name === name) || (type != null && tag.value.type === type))
189
- ) {
190
- nameCount += 1;
191
- if (nameCount > index) {
192
- return idx;
277
+ if (tag.type === PropertyWrapper) {
278
+ let { tags, property } = tag.value;
279
+ let { reference } = property;
280
+ if (tags[0].type === ReferenceTag) {
281
+ if (
282
+ (name != null && reference.name === name) ||
283
+ (type != null && reference.type === type)
284
+ ) {
285
+ nameCount += 1;
286
+ if (nameCount > index) {
287
+ return idx + 1;
288
+ }
289
+ }
193
290
  }
194
291
  }
195
292
  } else {
196
- let valueSums = Children.getSums(value);
293
+ let valueSums = Tags.getSums(value);
197
294
  if (
198
- nameCount + (type == null ? valueSums.references[name] : valueSums.specialTypes[type]) >
295
+ nameCount +
296
+ (name != null
297
+ ? Tags.getAtName(name, valueSums.references)
298
+ : Tags.getAtName(type, valueSums.specialTypes)) >
199
299
  index
200
300
  ) {
201
301
  node = value;
202
302
  continue outer;
203
303
  } else {
204
304
  nameCount +=
205
- (type == null ? valueSums.references[name] : valueSums.specialTypes[type]) ?? 0;
206
- idx += Children.getSize(value);
305
+ (name != null
306
+ ? Tags.getAtName(name, valueSums.references)
307
+ : Tags.getAtName(type, valueSums.specialTypes)) ?? 0;
308
+ idx += Tags.getSize(value);
207
309
  }
208
310
  }
209
311
  }
@@ -218,10 +320,9 @@ const { hasOwn, freeze } = Object;
218
320
  const { isArray } = Array;
219
321
 
220
322
  export const getOriginalFirstNode = (node, height = 1) => {
221
- for (let child of Children.traverse(node.children)) {
222
- if (child.type === Property) {
223
- return getProperties(buildPathSegment(child.value.reference.name, 0, height), node.properties)
224
- .node;
323
+ for (let child of Tags.traverse(node.tags)) {
324
+ if (child.type === PropertyWrapper) {
325
+ return child.value.property.node;
225
326
  }
226
327
  }
227
328
  return null;
@@ -232,26 +333,16 @@ export const getFirstNode = (node) => {
232
333
  };
233
334
 
234
335
  export const getFirstNodeProperty = (node) => {
235
- for (let child of Children.traverse(node.children)) {
236
- if (child.type === Property) {
237
- return child.value;
238
- }
239
- }
240
- return null;
241
- };
242
-
243
- export const getFirstNodeShiftStack = (node) => {
244
- for (let child of Children.traverse(node.children)) {
245
- if (child.type === Property) {
246
- let { reference } = child.value;
247
-
248
- if (!reference.flags.expression) return null;
249
-
250
- let property = node.properties[reference.name];
251
- if (reference.isArray) {
252
- property = btree.getAt(-1, property.node);
336
+ for (let child of Tags.traverse(node.tags)) {
337
+ if (child.type === PropertyWrapper) {
338
+ let ref = child.value.reference;
339
+ if (!ref.flags.expression) {
340
+ return child.value;
341
+ } else {
342
+ let tagsIndex = getPropertyTagsIndex(node, ref.type, ref.name, 0, -1);
343
+ return Tags.getAt(tagsIndex, node.tags + 2)?.value.node;
253
344
  }
254
- return property.node;
345
+ // is it shifted?
255
346
  }
256
347
  }
257
348
  return null;
@@ -260,40 +351,59 @@ export const getFirstNodeShiftStack = (node) => {
260
351
  export const referencesAreEqual = (a, b) => {
261
352
  return (
262
353
  a === b ||
263
- (a.value.name === b.value.name &&
264
- a.value.isArray === b.value.isArray &&
265
- a.value.flags.hasGap === b.value.flags.hasGap &&
266
- a.value.flags.expression === b.value.flags.expression)
354
+ (a.type === b.type &&
355
+ a.name === b.name &&
356
+ a.isArray === b.isArray &&
357
+ a.flags.hasGap === b.flags.hasGap &&
358
+ a.flags.expression === b.flags.expression)
267
359
  );
268
360
  };
269
361
 
270
- export const getProperties = (pathSegment, properties) => {
271
- let pathSegment_ = typeof pathSegment === 'string' ? buildPathSegment(pathSegment) : pathSegment;
272
- if (!hasOwn(pathSegment_, 'shiftIndex')) throw new Error();
273
- let { name, index, shiftIndex } = pathSegment_;
274
-
275
- if (hasOwn(properties, name)) {
276
- let prop = properties[name];
362
+ export const isDefined = (obj, key) => hasOwn(obj, key) && obj[key] !== undefined;
363
+ export const isUndefined = (obj, key) => !hasOwn(obj, key) || obj[key] === undefined;
277
364
 
278
- if (prop.reference.isArray) {
279
- prop = btree.getAt(index ?? -1, prop.node);
365
+ export function* relatedNodes(node) {
366
+ for (const child of Tags.traverse(node.tags)) {
367
+ if (child.type === PropertyWrapper) {
368
+ yield child.node;
280
369
  }
370
+ }
371
+ }
281
372
 
282
- if (!prop) return null;
373
+ export const getProperty = (pathSegment, node) => {
374
+ if (!node) throw new Error();
283
375
 
284
- if (prop.reference.flags.expression) {
285
- prop = btree.getAt(shiftIndex ?? -1, prop.node);
286
- }
376
+ if (pathSegment == null) throw new Error('Bad path segment');
377
+
378
+ let { name, type, index, shiftIndex } =
379
+ typeof pathSegment === 'string' ? buildPathSegment(pathSegment) : pathSegment;
380
+
381
+ let propIndex = getPropertyTagsIndex(node, type, name, index, shiftIndex);
382
+
383
+ if (propIndex == null) return null;
384
+
385
+ return Tags.getAt(propIndex, node.tags)?.value.property;
386
+ };
287
387
 
288
- return prop;
388
+ export const has = (path, node) => {
389
+ if (!isArray(path)) {
390
+ path = [path];
289
391
  }
290
- return null;
392
+
393
+ let pathArr = [...path];
394
+ let node_ = get(pathArr.slice(0, -1), node);
395
+ let seg = pathArr[pathArr.length - 1];
396
+ let name = typeof seg === 'string' ? seg : seg.name;
397
+
398
+ return (
399
+ !!getProperty(seg, node_) ||
400
+ getInitializerTagsIndex(node_, buildReferenceTag(null, name)) != null
401
+ );
291
402
  };
292
403
 
293
- export const isDefined = (obj, key) => hasOwn(obj, key) && obj[key] !== undefined;
294
- export const isUndefined = (obj, key) => !hasOwn(obj, key) || obj[key] === undefined;
404
+ export const get = (path, node) => getOr(null, path, node);
295
405
 
296
- export const get = (path, node) => {
406
+ export const getOr = (defaultValue, path, node) => {
297
407
  if (!node) throw new Error();
298
408
 
299
409
  if (path == null) throw new Error('Bad path');
@@ -302,42 +412,57 @@ export const get = (path, node) => {
302
412
  path = [path];
303
413
  }
304
414
 
305
- let node_ = node;
306
- for (let i = 0; i < path.length; i++) {
415
+ let root = getRoot(node);
416
+ let result = root || node;
417
+
418
+ let start = 0;
419
+ if (path[0]?.type === '.') {
420
+ if (!root) return null;
421
+ start = 1;
422
+ }
423
+
424
+ for (let i = start; i < path.length; i++) {
307
425
  let nameOrSeg = path[i];
308
426
  let isName = typeof nameOrSeg === 'string';
309
427
  let property;
428
+ let index = path[i + 1];
310
429
 
311
- if (!isName && (!nameOrSeg || !hasOwn(nameOrSeg, 'shiftIndex'))) throw new Error('Bad path');
430
+ if (typeof index === 'number') {
431
+ i++;
432
+ } else {
433
+ index = undefined;
434
+ }
312
435
 
313
- let name = isName ? nameOrSeg : nameOrSeg.name;
436
+ if (!isName && (!nameOrSeg || !hasOwn(nameOrSeg, 'shiftIndex'))) throw new Error('Bad path');
314
437
 
315
- if (name) {
316
- let { index } = nameOrSeg;
438
+ let seg = isName ? buildPathSegment(nameOrSeg, index) : nameOrSeg;
317
439
 
318
- property = getProperties(
319
- isName ? buildPathSegment(name, index) : nameOrSeg,
320
- node_.properties,
321
- );
440
+ property = getProperty(seg, result);
322
441
 
323
- ({ node: node_ } = property || {});
324
- } else if (!isName) {
325
- let { type } = nameOrSeg;
326
- if (type !== '.') throw new Error('not implemented');
442
+ if (!property) return defaultValue;
327
443
 
328
- let refIndex = getPropertyChildrenIndex(node_, '.');
444
+ result = property.node;
445
+ }
329
446
 
330
- if (refIndex == null) return null;
447
+ return result;
448
+ };
331
449
 
332
- node_ = Children.getAt(refIndex + 2, node_.children).value.node;
333
- } else {
334
- throw new Error();
335
- }
450
+ export function* list(name, node) {
451
+ let count = countList(name, node);
336
452
 
337
- if (!node_) return null;
453
+ for (let i = 0; i < count; i++) {
454
+ yield get(buildPathSegment(name, i, -1), node);
338
455
  }
456
+ }
339
457
 
340
- return node_;
458
+ export const countList = (name, node) => {
459
+ if (!node) throw new Error();
460
+
461
+ if (name == null) throw new Error('Bad path');
462
+
463
+ let hasInitializer = getInitializerTagsIndex(node, buildReferenceTag(null, name)) != null;
464
+
465
+ return Tags.getAtName(name, Tags.getSums(node.tags).references) - (hasInitializer ? 1 : 0);
341
466
  };
342
467
 
343
468
  export function* allTagPathsFor(range, options = {}) {
@@ -362,21 +487,17 @@ export function* allTagPathsFor(range, options = {}) {
362
487
 
363
488
  yield path;
364
489
 
365
- if (
366
- endPath &&
367
- path.childrenIndex === endPath.childrenIndex &&
368
- path.path.node === endPath.path.node
369
- ) {
490
+ if (endPath && path.tagsIndex === endPath.tagsIndex && path.path.node === endPath.path.node) {
370
491
  return;
371
492
  }
372
493
 
373
- let gapPath = path.path.parent && TagPath.from(path.path.parent, path.path.referenceIndex + 1);
494
+ let gapPath = path.path.parent && TagPath.from(path.path.parent, path.path.parentIndex, 1);
374
495
 
375
496
  if (
376
497
  endPath &&
377
498
  path.tag.type === CloseNodeTag &&
378
499
  gapPath &&
379
- gapPath.childrenIndex === endPath.childrenIndex &&
500
+ gapPath.tagsIndex === endPath.tagsIndex &&
380
501
  gapPath.path.node === endPath.path.node
381
502
  ) {
382
503
  return;
@@ -392,7 +513,7 @@ export function* allTagsFor(range, options = {}) {
392
513
  }
393
514
 
394
515
  export const buildFullRange = (node) => {
395
- let sum = Children.getSize(node.children);
516
+ let sum = Tags.getSize(node.tags);
396
517
  return sum ? [0, sum - 1] : null;
397
518
  };
398
519
 
@@ -404,10 +525,10 @@ export function* ownTagPathsFor(range) {
404
525
 
405
526
  if (startPath.parent.node !== endPath.parent.node) throw new Error();
406
527
 
407
- let { children } = startPath.parent.node;
528
+ let { tags } = startPath.parent.node;
408
529
 
409
- for (let i = startPath.childrenIndex; i < endPath.childrenIndex; i++) {
410
- yield children[i];
530
+ for (let i = startPath.tagsIndex; i < endPath.tagsIndex; i++) {
531
+ yield tags[i];
411
532
  }
412
533
  }
413
534
 
@@ -462,14 +583,14 @@ export let pathForTagPath = (tagPath) => {
462
583
  let isShift = tagPath.tag.type === ShiftTag;
463
584
 
464
585
  if (isShift) {
465
- refTagPath = tagPath.siblingAt(tagPath.childrenIndex - tagPath.tag.value.index * 3);
586
+ refTagPath = tagPath.siblingAt(tagPath.tagsIndex - tagPath.tag.value.index);
466
587
  }
467
588
 
468
589
  let { type, name } = refTagPath.tag.value;
469
590
 
470
591
  if (refTagPath.tag.type !== ReferenceTag) throw new Error();
471
592
 
472
- let index = getChildPropertyIndex(tagPath.node, refTagPath.childrenIndex);
593
+ let index = getChildPropertyIndex(tagPath.node, refTagPath.tagsIndex);
473
594
  let shiftIndex = isShift ? (tagPath.tag.value.index ?? 0) + 1 : null;
474
595
 
475
596
  return [{ type, name, index, shiftIndex }];
@@ -477,37 +598,42 @@ export let pathForTagPath = (tagPath) => {
477
598
 
478
599
  export const Path = class AgastPath extends WeakStackFrame {
479
600
  static from(node) {
480
- return node && this.create(node);
601
+ let node_ = isPlainObject(node) ? node : node.node;
602
+ return node_ && this.create(node_);
481
603
  }
482
604
 
483
- constructor(parent, node, referenceIndex = null) {
605
+ constructor(parent, node, parentIndex = null) {
484
606
  super(parent);
485
607
 
486
- if (!(hasOwn(node, 'type') && hasOwn(node, 'properties'))) throw new Error();
608
+ if (!node || !(hasOwn(node, 'type') && hasOwn(node, 'tags'))) throw new Error();
487
609
 
488
- if (parent && referenceIndex == null) throw new Error();
610
+ if (parent && parentIndex == null) throw new Error();
489
611
  if (!node) throw new Error();
490
612
  if (isArray(node)) throw new Error();
491
613
 
492
614
  this.node = node;
493
- this.referenceIndex = referenceIndex; // in the parent
615
+ this.parentIndex = parentIndex; // in the parent
494
616
 
495
- let referenceTag =
496
- referenceIndex != null && Children.getAt(referenceIndex, parent.node.children);
617
+ let parentTag = parent && Tags.getAt(parentIndex, parent.node.tags);
497
618
 
498
- if (referenceTag && ![ReferenceTag, ShiftTag].includes(referenceTag.type)) {
619
+ if (parentTag && parentTag.type !== PropertyWrapper) {
499
620
  throw new Error();
500
621
  }
501
622
 
502
- if (parent && (!this.reference || ![ReferenceTag, ShiftTag].includes(this.reference.type))) {
623
+ parentIndex != null && parentTag?.value.tags[0];
624
+
625
+ if (
626
+ parent &&
627
+ (!this.referenceTag || ![ReferenceTag, ShiftTag].includes(this.referenceTag.type))
628
+ ) {
503
629
  throw new Error();
504
630
  }
505
631
 
506
632
  // if (
507
633
  // parent &&
508
- // (['#', '@'].includes(this.reference.value.type)
509
- // ? parent.tagPathAt(referenceIndex + 1).tag.value
510
- // : get(pathForTagPath(parent.tagPathAt(referenceIndex)), parent.node)) !== node
634
+ // (['#', '@'].includes(this.referenceTag.value.type)
635
+ // ? parent.tagPathAt(parentIndex, 1).tag.value
636
+ // : get(pathForTagPath(parent.tagPathAt(parentIndex)), parent.node)) !== node
511
637
  // ) {
512
638
  // throw new Error('Path not reachable');
513
639
  // }
@@ -541,18 +667,54 @@ export const Path = class AgastPath extends WeakStackFrame {
541
667
  return this.closeTagPath.tag;
542
668
  }
543
669
 
544
- tagPathAt(idx) {
545
- return TagPath.from(this, idx);
670
+ tagPathAt(idx, offset) {
671
+ return TagPath.from(this, idx, offset);
672
+ }
673
+
674
+ get binding() {
675
+ return this.bindingTag?.value ?? null;
676
+ }
677
+
678
+ get bindingTag() {
679
+ return this.bindingTagPath?.tag ?? null;
680
+ }
681
+
682
+ get bindingTagPath() {
683
+ return (this.parent?.node ?? null) && new TagPath(this.parent, this.parentIndex, 1);
546
684
  }
547
685
 
548
686
  get reference() {
549
- return (
550
- (this.parent?.node ?? null) && Children.getAt(this.referenceIndex, this.parent.node.children)
551
- );
687
+ return this.referenceTag?.value ?? null;
688
+ }
689
+
690
+ get referenceTag() {
691
+ return this.referenceTagPath?.tag ?? null;
552
692
  }
553
693
 
554
694
  get referenceTagPath() {
555
- return (this.parent?.node ?? null) && new TagPath(this.parent, this.referenceIndex);
695
+ if (!this.parent?.node) {
696
+ return null;
697
+ }
698
+ let path = new TagPath(this.parent, this.parentIndex, 0);
699
+ if (path.tag.type === ShiftTag) {
700
+ path = new TagPath(this.parent, this.parentIndex - path.tag.value.index, 0);
701
+ }
702
+ return path;
703
+ }
704
+
705
+ get parentPropertyPath() {
706
+ if (!this.parent?.node) {
707
+ return null;
708
+ }
709
+ let path = new TagPath(this.parent, this.parentIndex);
710
+ if (path.tag.type === ShiftTag) {
711
+ path = new TagPath(this.parent, this.parentIndex - path.tag.value.index);
712
+ }
713
+ return path;
714
+ }
715
+
716
+ get parentProperty() {
717
+ return this.parentPropertyPath?.tag ?? null;
556
718
  }
557
719
 
558
720
  get(path) {
@@ -563,37 +725,167 @@ export const Path = class AgastPath extends WeakStackFrame {
563
725
  if (!isArray(path_)) throw new Error();
564
726
 
565
727
  let pathInst = this;
728
+
729
+ if (!pathInst.node.type && pathInst.node.flags.cover) {
730
+ if (pathInst.depth) throw new Error();
731
+ pathInst = pathInst.push(
732
+ getRoot(pathInst.node),
733
+ getPropertyTagsIndex(pathInst.node, '.', null, 0),
734
+ );
735
+ }
736
+
566
737
  for (let i = 0; i < path_.length; i++) {
567
738
  let nameOrSeg = path_[i];
568
739
 
740
+ let seg = nameOrSeg;
569
741
  if (isString(nameOrSeg)) {
570
- nameOrSeg = { type: null, name: nameOrSeg, index: null, shiftIndex: null };
742
+ let index = path_[i + 1];
743
+ if (typeof index === 'number') {
744
+ i++;
745
+ } else {
746
+ index = undefined;
747
+ }
748
+ seg = buildPathSegment(nameOrSeg, index);
571
749
  }
572
750
 
573
- let property = getProperties(nameOrSeg, pathInst.node.properties);
751
+ let tagsIndex = getPropertyTagsIndex(
752
+ pathInst.node,
753
+ seg.type,
754
+ seg.name,
755
+ seg.index,
756
+ seg.shiftIndex,
757
+ );
758
+
759
+ let { property } = Tags.getAt(tagsIndex, pathInst.node.tags).value;
574
760
 
575
761
  if (!property) return null;
576
762
 
577
- let { node, reference: ref } = property;
763
+ let { node } = property;
578
764
 
579
- let shiftIndex_ = nameOrSeg.shiftIndex;
765
+ pathInst = pathInst.push(node, tagsIndex);
766
+ }
580
767
 
581
- if (shiftIndex_ && shiftIndex_ < 0) {
582
- shiftIndex_ = btree.getSize(node) + shiftIndex_;
583
- } else if (shiftIndex_ == null && ref.flags.expression) {
584
- shiftIndex_ = 0;
585
- }
768
+ return pathInst;
769
+ }
770
+
771
+ replaceWith(node, binding = buildBinding()) {
772
+ return this.replaceAt([], node, binding);
773
+ }
774
+
775
+ replaceAt(path, node, binding = buildBinding()) {
776
+ throw new Error('not working yet');
777
+
778
+ let built;
779
+ let path_ = path;
780
+ if (typeof path_ === 'string') {
781
+ path_ = [path_];
782
+ }
783
+ if (!isArray(path_)) throw new Error();
586
784
 
587
- let shiftOffset = shiftIndex_ ? (shiftIndex_ - 1) * 3 : 0;
785
+ let pathInst = this;
588
786
 
787
+ if (!pathInst.node.type && pathInst.node.flags.cover) {
788
+ if (pathInst.depth) throw new Error();
589
789
  pathInst = pathInst.push(
590
- node,
591
- getPropertyChildrenIndex(pathInst.node, nameOrSeg.type, nameOrSeg.name, nameOrSeg.index) +
592
- shiftOffset,
790
+ getRoot(pathInst.node),
791
+ getPropertyTagsIndex(pathInst.node, '.', null, 0),
593
792
  );
594
793
  }
595
794
 
596
- return pathInst;
795
+ for (let i = 0; i < path_.length; i++) {
796
+ let nameOrSeg = path_[i];
797
+
798
+ built = { ...pathInst.node };
799
+
800
+ let seg = nameOrSeg;
801
+ if (isString(nameOrSeg)) {
802
+ let index = path_[i + 1];
803
+ if (typeof index === 'number') {
804
+ i++;
805
+ } else {
806
+ index = undefined;
807
+ }
808
+ seg = buildPathSegment(nameOrSeg, index);
809
+ }
810
+
811
+ let tagsIndex = getPropertyTagsIndex(
812
+ pathInst.node,
813
+ seg.type,
814
+ seg.name,
815
+ seg.index,
816
+ seg.shiftIndex,
817
+ );
818
+
819
+ let tagPath = TagPath.from(pathInst, tagsIndex);
820
+
821
+ let {
822
+ property,
823
+ tags: { 0: refTag },
824
+ } = tagPath.tag.value;
825
+
826
+ if (refTag.type === ShiftTag) {
827
+ let { index } = refTag.value;
828
+
829
+ for (let i = 0; i < index - 1; i++) {
830
+ // update bounds!
831
+ }
832
+ tagsIndex -= index;
833
+ pathInst = pathInst.replace(built, tagsIndex);
834
+ }
835
+
836
+ if (!property) return null;
837
+
838
+ let { node } = property;
839
+
840
+ pathInst = pathInst.push(node, tagsIndex);
841
+ }
842
+
843
+ let node_ = isPlainObject(node) ? node : node.node;
844
+
845
+ if (!node_) throw new Error();
846
+
847
+ path = pathInst.parent;
848
+ let replacementPath = [];
849
+
850
+ let targetReference = pathInst.reference;
851
+ let targetBinding = binding;
852
+ let targetParentIndex = pathInst.parentIndex;
853
+
854
+ while (path) {
855
+ let value = built;
856
+ built = { ...path.node };
857
+
858
+ let { tags } = Tags.getAt(targetParentIndex, path.node.tags).value;
859
+ let property = buildProperty(targetReference, targetBinding, value);
860
+
861
+ let tags_ = [tags[0], buildChild(BindingTag, targetBinding), buildChild(Property, property)];
862
+
863
+ built.tags = Tags.replaceAt(
864
+ targetParentIndex,
865
+ built.tags,
866
+ buildChild(PropertyWrapper, buildPropertyWrapper(tags_, property)),
867
+ );
868
+
869
+ finalizeNode(built);
870
+
871
+ if (targetReference.name) {
872
+ replacementPath.push(
873
+ buildPathSegment(
874
+ targetReference.name,
875
+ getChildPropertyIndex(path.node, targetParentIndex),
876
+ ),
877
+ );
878
+ }
879
+
880
+ targetReference = path.reference;
881
+ targetBinding = path.binding;
882
+ targetParentIndex = path.parentIndex;
883
+ path = path.parent;
884
+ }
885
+
886
+ replacementPath.reverse();
887
+
888
+ return Path.from(built).get(replacementPath).atDepth(this.depth);
597
889
  }
598
890
 
599
891
  atDepth(depth) {
@@ -603,34 +895,50 @@ export const Path = class AgastPath extends WeakStackFrame {
603
895
 
604
896
  export const tagPathsAreEqual = (a, b) => {
605
897
  if (a == null || b == null) return b == a;
606
- return a.path.node === b.path.node && a.childrenIndex === b.childrenIndex;
898
+ return a.path.node === b.path.node && a.tagsIndex === b.tagsIndex;
607
899
  };
608
900
 
609
901
  export class TagPath {
610
- constructor(path, childrenIndex) {
611
- if (path == null || childrenIndex == null) throw new Error();
902
+ constructor(path, tagsIndex, wrapperIndex) {
903
+ if (path == null || tagsIndex == null) throw new Error();
612
904
 
613
- let tag = Children.getAt(childrenIndex, path.node.children);
905
+ let tag = Tags.getAt(tagsIndex, path.node.tags);
614
906
 
615
907
  if (tag == null) throw new Error();
616
908
 
909
+ if (tag.type === PropertyWrapper && wrapperIndex != null) {
910
+ tag = tag.value.tags[wrapperIndex];
911
+ }
912
+
617
913
  this.path = path;
618
914
  this.node = path.node;
619
915
  this.tag = tag;
620
- this.childrenIndex = childrenIndex;
916
+ this.tagsIndex = tagsIndex;
917
+ this.wrapperIndex = wrapperIndex;
621
918
 
622
919
  freeze(this);
623
920
  }
624
921
 
625
- static from(path, childrenIndex) {
626
- let size = Children.getSize(path.node.children);
627
- let index = childrenIndex < 0 ? size + childrenIndex : childrenIndex;
922
+ static from(path, tagsIndex, wrapperIndex) {
923
+ let size = Tags.getSize(path.node.tags);
924
+ let index = tagsIndex < 0 ? size + tagsIndex : tagsIndex;
925
+
926
+ let tag = Tags.getAt(index, path.node.tags);
628
927
 
629
- return index >= 0 && index < size ? new TagPath(path, index) : null;
928
+ if (!tag) return null;
929
+
930
+ let offset =
931
+ wrapperIndex == null || tag.type !== PropertyWrapper
932
+ ? null
933
+ : wrapperIndex < 0
934
+ ? tag.value.tags.length + wrapperIndex
935
+ : wrapperIndex;
936
+
937
+ return index >= 0 && index < size ? new TagPath(path, index, offset) : null;
630
938
  }
631
939
 
632
- static fromNode(node, childrenIndex) {
633
- return TagPath.from(Path.from(node), childrenIndex);
940
+ static fromNode(node, tagsIndex, wrapperIndex) {
941
+ return TagPath.from(Path.from(node), tagsIndex, wrapperIndex);
634
942
  }
635
943
 
636
944
  get child() {
@@ -642,117 +950,129 @@ export class TagPath {
642
950
  }
643
951
 
644
952
  get nextSibling() {
645
- const { path, childrenIndex } = this;
953
+ const { path, tagsIndex, wrapperIndex } = this;
646
954
 
647
- const child =
648
- childrenIndex + 1 >= Children.getSize(path.node.children)
649
- ? null
650
- : Children.getAt(childrenIndex + 1, path.node.children);
955
+ let propPath = new TagPath(path, tagsIndex);
651
956
 
652
- return child && new TagPath(path, childrenIndex + 1);
957
+ if (wrapperIndex != null && wrapperIndex + 1 < Tags.getSize(propPath.tag.value.tags)) {
958
+ return new TagPath(path, tagsIndex, wrapperIndex + 1);
959
+ } else if (tagsIndex + 1 < Tags.getSize(path.node.tags)) {
960
+ return new TagPath(path, tagsIndex + 1, 0);
961
+ }
962
+
963
+ return TagPath.from(path, tagsIndex + 1, 0);
653
964
  }
654
965
 
655
966
  get next() {
656
- let { path, childrenIndex } = this;
967
+ let { path, tagsIndex, wrapperIndex } = this;
657
968
 
658
969
  let leaving = false;
659
970
 
660
971
  for (;;) {
661
- let prevTag = Children.getAt(childrenIndex - 1, path.node.children);
662
- let tag = Children.getAt(childrenIndex, path.node.children);
663
- let isInitialTag = path.node === this.path.node && childrenIndex === this.childrenIndex;
972
+ let lastRef = Tags.getAt(tagsIndex, path.node.tags, 0);
973
+ let tag = Tags.getAt(tagsIndex, path.node.tags);
974
+ let wrapperTag = tag;
975
+ let isInitialTag =
976
+ path.node === this.path.node &&
977
+ tagsIndex === this.tagsIndex &&
978
+ wrapperIndex === this.wrapperIndex;
664
979
  let wasLeaving = leaving;
665
980
  leaving = false;
666
981
 
667
982
  if (!tag) return null;
668
983
 
984
+ wrapperIndex =
985
+ tag.type === PropertyWrapper ? (wrapperIndex == null ? 0 : wrapperIndex) : null;
986
+
987
+ if (wrapperIndex != null) {
988
+ wrapperTag = tag.value.tags[wrapperIndex];
989
+ }
990
+
669
991
  // done
670
- if (!isInitialTag && tag.type !== Property) {
671
- return new TagPath(path, childrenIndex);
992
+ if (!isInitialTag && wrapperTag.type !== Property) {
993
+ return new TagPath(path, tagsIndex, wrapperIndex);
672
994
  }
673
995
 
674
996
  // in
675
- if (tag.type === Property && !wasLeaving) {
676
- // jump to gap tag?
677
- if (
678
- path.parent &&
679
- tag.value.reference.flags.expression &&
680
- (childrenIndex === 3 ||
681
- (childrenIndex === 5 &&
682
- Children.getAt(2, path.node.children).type === InitializerTag)) &&
683
- Children.getAt(path.referenceIndex, path.parent.node.children)?.type === ShiftTag
684
- ) {
685
- let { reference: ref } = tag.value;
686
- let shiftStack = path.node.properties[ref.name].node;
997
+ if (wrapperTag.type === Property && !wasLeaving) {
998
+ if (tag.value.tags[1]?.type === InitializerTag || !tag.value.property.node) {
999
+ } else {
1000
+ // jump to gap tag?
1001
+ let tag1;
1002
+ if (
1003
+ path.parent &&
1004
+ tag.value.property.reference.flags.expression &&
1005
+ (tagsIndex === 1 ||
1006
+ (tagsIndex === 2 &&
1007
+ (tag1 = Tags.getAt(1, path.node.tags)).type === PropertyWrapper &&
1008
+ tag1.value.tags[1].type === InitializerTag)) &&
1009
+ Tags.getAt(path.parentIndex, path.parent.node.tags, 0)?.type === ShiftTag
1010
+ ) {
1011
+ let shiftStack = tag.value.property.node.bounds[0];
1012
+
1013
+ let gapNode = BTree.getAt(-1, shiftStack).node;
687
1014
 
688
- if (ref.isArray) {
689
- shiftStack = btree.getAt(-1, shiftStack).node;
1015
+ path = new Path(path, gapNode, tagsIndex);
1016
+ tagsIndex = 0;
1017
+ continue;
690
1018
  }
691
- let gapNode = btree.getAt(0, shiftStack).node;
692
1019
 
693
- path = new Path(path, gapNode, childrenIndex - 2);
694
- childrenIndex = 0;
1020
+ path = new Path(path, tag.value.property.node, tagsIndex);
1021
+ tagsIndex = 0;
695
1022
  continue;
696
1023
  }
697
-
698
- path = path.push(tag.value.node, childrenIndex - 2);
699
- childrenIndex = 0;
700
- continue;
701
1024
  }
702
1025
 
1026
+ tag;
703
1027
  // shift
704
- if (tag.type === BindingTag && prevTag.type === ShiftTag) {
705
- let refIndex = childrenIndex - 1 - prevTag.value.index * 3;
1028
+ if (wrapperTag.type === BindingTag && lastRef.type === ShiftTag) {
1029
+ let refIndex = tagsIndex - lastRef.value.index;
706
1030
  if (refIndex < 0) throw new Error();
707
- let refTag = Children.getAt(refIndex, path.node.children);
708
-
709
- const { name, isArray } = refTag.value;
710
- let pathDesc = buildPathSegment(name);
711
- let index;
712
- if (isArray) {
713
- index = getChildPropertyIndex(path.node, refIndex);
714
- pathDesc = buildPathSegment(name, index);
715
- }
1031
+ let refTag = Tags.getAt(refIndex, path.node.tags, 0);
716
1032
 
717
- // HMM
718
- pathDesc.shiftIndex = prevTag.value.height;
1033
+ if (refTag.type !== ReferenceTag) throw new Error();
719
1034
 
720
- if (index !== -1) {
721
- path = path.get([pathDesc]);
1035
+ if (Tags.getAt(refIndex, path.node.tags, 1).type !== InitializerTag) {
1036
+ path = new Path(path, tag.value.property.node, tagsIndex);
722
1037
 
723
1038
  if (!path) return null;
724
1039
 
725
- childrenIndex = 0;
1040
+ tagsIndex = 0;
726
1041
  continue;
727
1042
  }
728
1043
  }
729
1044
 
730
1045
  // over
731
- if (path.node && childrenIndex + 1 < Children.getSize(path.node.children)) {
732
- childrenIndex++;
1046
+ if (tag.type === PropertyWrapper && wrapperIndex + 1 < tag.value.tags.length) {
1047
+ wrapperIndex++;
1048
+ continue;
1049
+ } else if (path.node && tagsIndex + 1 < Tags.getSize(path.node.tags)) {
1050
+ tagsIndex++;
1051
+ wrapperIndex = 0;
733
1052
  continue;
734
1053
  }
735
1054
 
736
1055
  // out
737
- if (path.referenceIndex != null && path.parent) {
1056
+ if (path.parentIndex != null && path.parent) {
738
1057
  do {
739
1058
  if (
740
1059
  path.parent &&
741
- Children.getAt(path.referenceIndex, path.parent.node.children)?.type === ShiftTag
1060
+ Tags.getAt(path.parentIndex, path.parent.node.tags, 0)?.type === ShiftTag
742
1061
  ) {
743
- childrenIndex =
744
- Children.getSize(path.parent.node.children) > path.referenceIndex + 2
745
- ? path.referenceIndex + 2
1062
+ tagsIndex =
1063
+ Tags.getSize(path.parent.node.tags) > path.parentIndex + 1
1064
+ ? path.parentIndex + 1
746
1065
  : null;
747
1066
  } else {
748
- childrenIndex = path.referenceIndex + 2;
1067
+ tagsIndex = path.parentIndex + 1;
749
1068
  }
750
1069
 
1070
+ wrapperIndex = 0;
751
1071
  path = path.parent;
752
1072
  leaving = true;
753
1073
 
754
1074
  if (!path) return null;
755
- } while (childrenIndex == null);
1075
+ } while (tagsIndex == null);
756
1076
 
757
1077
  leaving = true;
758
1078
  continue;
@@ -763,64 +1083,77 @@ export class TagPath {
763
1083
  }
764
1084
 
765
1085
  get nextUnshifted() {
766
- let { path, childrenIndex } = this;
1086
+ let { path, tagsIndex, wrapperIndex } = this;
767
1087
 
768
1088
  let leaving = false;
769
1089
 
770
1090
  for (;;) {
771
- let tag = Children.getAt(childrenIndex, path.node.children);
772
- let isInitialTag = path.node === this.path.node && childrenIndex === this.childrenIndex;
1091
+ let tag = Tags.getAt(tagsIndex, path.node.tags);
1092
+ let wrapperTag = tag;
1093
+ let isInitialTag =
1094
+ path.node === this.path.node &&
1095
+ tagsIndex === this.tagsIndex &&
1096
+ wrapperIndex === this.wrapperIndex;
773
1097
  let wasLeaving = leaving;
774
1098
  leaving = false;
775
1099
 
776
1100
  if (!tag) return null;
777
1101
 
778
- if (tag.type === ReferenceTag && tag.value.flags.expression) {
779
- // look forward for another reference
780
- let offset = 1;
781
- let tag_ = tag;
782
- do {
783
- tag_ = Children.getAt(childrenIndex + offset, path.node.children);
784
- offset++;
1102
+ wrapperIndex =
1103
+ tag.type === PropertyWrapper ? (wrapperIndex == null ? 0 : wrapperIndex) : null;
1104
+
1105
+ if (wrapperIndex != null) {
1106
+ wrapperTag = tag.value.tags[wrapperIndex];
1107
+ }
1108
+
1109
+ if (wrapperTag.type === ReferenceTag && wrapperTag.value.flags.expression) {
1110
+ // move past shifts
785
1111
 
786
- if (tag_?.type === InitializerTag) break;
787
- } while (tag_ && [BindingTag, ShiftTag].includes(tag_.type));
788
- if (!tag_) {
789
- return null;
1112
+ let shifts = 0;
1113
+ let tag = Tags.getAt(tagsIndex + 1, path.node.tags);
1114
+
1115
+ while (tag?.type === PropertyWrapper && tag.value.tags[0].type === ShiftTag) {
1116
+ shifts++;
1117
+ wrapperIndex = 1;
1118
+ tag = Tags.getAt(tagsIndex + shifts + 1, path.node.tags);
1119
+ }
1120
+ if (shifts) {
1121
+ tagsIndex += shifts;
1122
+ continue;
790
1123
  }
791
1124
  }
792
1125
 
793
1126
  // done
794
- if (!isInitialTag && tag.type !== Property) {
795
- return new TagPath(path, childrenIndex);
1127
+ if (!isInitialTag && wrapperTag.type !== Property) {
1128
+ return new TagPath(path, tagsIndex, wrapperIndex);
796
1129
  }
797
1130
 
798
1131
  // in
799
- if (
800
- tag.type === Property &&
801
- !wasLeaving &&
802
- !(Children.getAt(childrenIndex + 1, path.node.children)?.type === ShiftTag)
803
- ) {
804
- // TODO use properties to skip over a lot of tag walking
805
- path = path.push(tag.value.node, childrenIndex - 2);
806
- childrenIndex = 0;
807
- continue;
1132
+ if (tag.type === PropertyWrapper) {
1133
+ if (wrapperIndex + 1 < tag.value.tags.length) {
1134
+ wrapperIndex++;
1135
+ continue;
1136
+ } else if (!wasLeaving && tag.value.tags[1] && tag.value.tags[1].type !== InitializerTag) {
1137
+ path = path.push(tag.value.property.node, tagsIndex);
1138
+ tagsIndex = 0;
1139
+ continue;
1140
+ }
808
1141
  }
809
1142
 
810
1143
  // over
811
- if (path.node && childrenIndex + 1 < Children.getSize(path.node.children)) {
812
- childrenIndex++;
1144
+ if (path.node && tagsIndex + 1 < Tags.getSize(path.node.tags)) {
1145
+ tagsIndex++;
813
1146
  continue;
814
1147
  }
815
1148
 
816
1149
  // out
817
- if (path.referenceIndex != null) {
1150
+ if (path.parentIndex != null) {
818
1151
  do {
819
- childrenIndex = path.referenceIndex + 2;
1152
+ tagsIndex = path.parentIndex + 1;
820
1153
 
821
1154
  path = path.parent;
822
1155
  leaving = true;
823
- } while (childrenIndex == null);
1156
+ } while (tagsIndex == null);
824
1157
 
825
1158
  leaving = true;
826
1159
  continue;
@@ -831,63 +1164,15 @@ export class TagPath {
831
1164
  }
832
1165
 
833
1166
  get previousSibling() {
834
- const { path, childrenIndex } = this;
835
-
836
- const child = childrenIndex - 1 < 0 ? null : btree.getAt(childrenIndex - 1, path.node.children);
837
-
838
- return child && new TagPath(path, childrenIndex - 1);
839
- }
840
-
841
- get previous() {
842
- throw new Error('not implemented');
843
- }
1167
+ const { path, tagsIndex, wrapperIndex } = this;
844
1168
 
845
- get previousUnshifted() {
846
- let { path, childrenIndex } = this;
847
-
848
- let leaving = false;
849
-
850
- for (;;) {
851
- let tag = Children.getAt(childrenIndex, path.node.children);
852
- let isInitialTag = path.node === this.path.node && childrenIndex === this.childrenIndex;
853
- let wasLeaving = leaving;
854
- leaving = false;
855
-
856
- if (!tag) return null;
857
-
858
- // done
859
- if (!isInitialTag && tag.type !== ShiftTag && tag.type !== Property) {
860
- return new TagPath(path, childrenIndex);
861
- }
862
-
863
- // in
864
- if (tag.type === Property && !wasLeaving) {
865
- path = path.push(tag.value.node, childrenIndex - 1);
866
- childrenIndex = Children.getSize(tag.value.children) - 1;
867
- continue;
868
- }
869
-
870
- // over
871
- if (path.node && childrenIndex + 1 < Children.getSize(path.node.children)) {
872
- childrenIndex--;
873
- continue;
874
- }
875
-
876
- // out
877
- if (path.referenceIndex != null) {
878
- do {
879
- childrenIndex = path.referenceIndex;
880
-
881
- path = path.parent;
882
- leaving = true;
883
- } while (childrenIndex == null);
1169
+ if (wrapperIndex > 0) {
1170
+ return new TagPath(path, tagsIndex, wrapperIndex - 1);
1171
+ }
884
1172
 
885
- leaving = true;
886
- continue;
887
- }
1173
+ const child = tagsIndex - 1 < 0 ? null : BTree.getAt(tagsIndex - 1, path.node.tags);
888
1174
 
889
- return null;
890
- }
1175
+ return child && TagPath.from(path, tagsIndex - 1, -1);
891
1176
  }
892
1177
 
893
1178
  get innerNode() {
@@ -895,18 +1180,19 @@ export class TagPath {
895
1180
  }
896
1181
 
897
1182
  get inner() {
898
- let { tag, previousSibling } = this;
1183
+ let { path, tagsIndex, tag, previousSibling } = this;
899
1184
 
900
- let refPath = previousSibling?.previousSibling;
1185
+ let refPath = TagPath.from(path, tagsIndex, 0);
901
1186
 
902
- if (tag.type !== Property) {
1187
+ if (tag.type !== PropertyWrapper) {
903
1188
  return null;
904
1189
  }
905
1190
 
906
1191
  let shiftPath = null;
907
1192
  if (refPath.tag.type === ShiftTag) {
908
1193
  shiftPath = refPath;
909
- refPath = TagPath.from(this.path, this.childrenIndex - refPath.tag.value.index * 3 - 2);
1194
+ throw new Error('not implemented');
1195
+ refPath = TagPath.from(this.path, this.tagsIndex - refPath.tag.value.index - 2);
910
1196
  }
911
1197
 
912
1198
  if (refPath.tag.type !== ReferenceTag) throw new Error();
@@ -917,13 +1203,13 @@ export class TagPath {
917
1203
  buildFullPathSegment(
918
1204
  type,
919
1205
  name,
920
- getChildPropertyIndex(refPath.path.node, refPath.childrenIndex),
1206
+ getChildPropertyIndex(refPath.path.node, refPath.tagsIndex),
921
1207
  shiftPath?.tag.value.index,
922
1208
  ),
923
1209
  ]);
924
1210
  }
925
1211
 
926
1212
  equalTo(tagPath) {
927
- return this.node === tagPath.node && this.childrenIndex === tagPath.childrenIndex;
1213
+ return this.node === tagPath.node && this.tagsIndex === tagPath.tagsIndex;
928
1214
  }
929
1215
  }