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