@bablr/agast-helpers 0.7.1 → 0.8.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/tree.js CHANGED
@@ -7,6 +7,12 @@ import {
7
7
  buildLiteralTag,
8
8
  buildCloseNodeTag,
9
9
  tokenFlags,
10
+ buildInitializerTag,
11
+ buildBindingTag,
12
+ buildProperty,
13
+ buildChild,
14
+ buildShiftTag,
15
+ buildGapTag,
10
16
  } from './builders.js';
11
17
  import {
12
18
  printPrettyCSTML as printPrettyCSTMLFromStream,
@@ -23,14 +29,16 @@ import {
23
29
  NullTag,
24
30
  InitializerTag,
25
31
  LiteralTag,
26
- EmbeddedNode,
32
+ AttributeDefinition,
33
+ BindingTag,
34
+ Property,
35
+ ShiftTag,
27
36
  } from './symbols.js';
28
37
  import * as btree from './btree.js';
29
- import * as sumtree from './sumtree.js';
38
+ import * as Children from './children.js';
30
39
  export * from './builders.js';
31
40
  export * from './print.js';
32
41
  import {
33
- add,
34
42
  get,
35
43
  TagPath,
36
44
  Path,
@@ -39,14 +47,26 @@ import {
39
47
  isGapNode,
40
48
  getOpenTag,
41
49
  getCloseTag,
42
- getShifted,
50
+ getRoot,
51
+ getRootArray,
52
+ getFirstNodeShiftStack,
43
53
  } from './path.js';
54
+ import { isPlainObject } from './object.js';
44
55
 
45
- export { add, get, isFragmentNode, isNullNode, isGapNode, getOpenTag, getCloseTag };
56
+ export {
57
+ get,
58
+ isFragmentNode,
59
+ isNullNode,
60
+ isGapNode,
61
+ getOpenTag,
62
+ getCloseTag,
63
+ getRoot,
64
+ getRootArray,
65
+ };
46
66
 
47
- export const buildToken = (language, type, value, attributes = {}) => {
67
+ export const buildToken = (type, value, attributes = {}) => {
48
68
  return treeFromStreamSync([
49
- buildOpenNodeTag(tokenFlags, language, type, attributes),
69
+ buildOpenNodeTag(tokenFlags, type, attributes),
50
70
  buildLiteralTag(value),
51
71
  buildCloseNodeTag(),
52
72
  ]);
@@ -55,72 +75,36 @@ export const buildToken = (language, type, value, attributes = {}) => {
55
75
  const isString = (str) => typeof str === 'string';
56
76
 
57
77
  const { isArray } = Array;
58
- const { freeze } = Object;
59
-
60
- export const parseReference = (str) => {
61
- let {
62
- 1: name,
63
- 2: isArray,
64
- 3: index,
65
- 4: expressionToken,
66
- 5: hasGapToken,
67
- } = /^\s*([.#@]|[a-zA-Z]+)\s*(\[\s*(\d+\s*)?\])?\s*(\+)?(\$)?\s*$/.exec(str);
68
-
69
- let flags = {
70
- expression: !!expressionToken,
71
- hasGap: !!hasGapToken,
72
- };
73
-
74
- index = index ? parseInt(index, 10) : null;
75
- isArray = !!isArray;
76
- name = name || null;
77
-
78
- return buildReferenceTag(name, isArray, flags, index);
79
- };
78
+ const { freeze, hasOwn } = Object;
80
79
 
81
80
  export const mergeReferences = (outer, inner) => {
82
- let {
83
- name,
84
- isArray,
85
- index,
86
- flags: { expression, hasGap },
87
- } = outer.value;
81
+ let { type, name, isArray, index, flags: { expression, hasGap } = {} } = outer;
88
82
 
89
83
  if (
90
- name != null &&
91
- name !== '.' &&
92
- inner.value.name != null &&
93
- inner.value.name !== '.' &&
94
- name !== inner.value.name
84
+ (name != null && inner.name != null && name !== inner.name) ||
85
+ (type != null && inner.type != null && type !== inner.type)
95
86
  ) {
96
87
  return inner;
97
88
  }
98
89
 
99
- isArray = isArray || inner.value.isArray;
100
- expression = !!(expression || inner.value.flags.expression);
101
- hasGap = !!(hasGap || inner.value.flags.hasGap);
102
- name = name === '.' ? inner.value.name : name;
103
-
104
- return buildReferenceTag(name, isArray, { expression, hasGap }, index);
105
- };
90
+ isArray = isArray || inner.isArray;
91
+ expression = !!(expression || inner.flags.expression);
92
+ hasGap = !!(hasGap || inner.flags.hasGap);
93
+ name = type === '.' ? inner.name : name;
94
+ type = type === '.' ? inner.type : type;
106
95
 
107
- export const isEmptyReference = (ref) => {
108
- let { name, isArray, flags } = ref.value;
109
- return name === '.' && !isArray && !(flags.expression || flags.hasGap);
96
+ return buildReferenceTag(type, name, isArray, { expression, hasGap }, index).value;
110
97
  };
111
98
 
112
- export const defineAttribute = (node, key, value) => {
113
- const openTag = getOpenTag(node);
99
+ export const mergeReferenceTags = (outer, inner) => {
100
+ let value = mergeReferences(outer.value, inner.value);
114
101
 
115
- if (value != null) {
116
- const { flags, language, type } = openTag.value;
117
- const attributes = { ...openTag.value.attributes, [key]: value };
118
- const newOpenTag = buildOpenNodeTag(flags, language, type, attributes);
119
-
120
- node.attributes = attributes;
102
+ return buildChild(ReferenceTag, value);
103
+ };
121
104
 
122
- node.children = sumtree.replaceAt(0, node.children, newOpenTag);
123
- }
105
+ export const isEmptyReference = (ref) => {
106
+ let { type, isArray, flags } = ref.value;
107
+ return type === '.' && !isArray && !(flags.expression || flags.hasGap);
124
108
  };
125
109
 
126
110
  function* __treeFromStream(tags, options) {
@@ -130,7 +114,8 @@ function* __treeFromStream(tags, options) {
130
114
  let doctype = null;
131
115
  const co = new Coroutine(getStreamIterator(tags));
132
116
  const expressionsCo = new Coroutine(getStreamIterator(options.expressions || []));
133
- let reference = null;
117
+ let referenceTag = null;
118
+ let bindingTag = null;
134
119
 
135
120
  for (;;) {
136
121
  co.advance();
@@ -165,15 +150,21 @@ function* __treeFromStream(tags, options) {
165
150
  }
166
151
 
167
152
  case ReferenceTag: {
168
- reference = tag;
153
+ referenceTag = tag;
154
+ suppressTag = true;
155
+ break;
156
+ }
157
+
158
+ case BindingTag: {
159
+ bindingTag = tag;
169
160
  suppressTag = true;
170
161
  break;
171
162
  }
172
163
 
173
164
  case InitializerTag: {
174
- add(path.node, reference, []);
165
+ add(path.node, referenceTag, []);
175
166
  suppressTag = true;
176
- reference = null;
167
+ referenceTag = null;
177
168
  break;
178
169
  }
179
170
 
@@ -185,19 +176,19 @@ function* __treeFromStream(tags, options) {
185
176
 
186
177
  const isGap = tag.type === GapTag;
187
178
 
188
- if (path.parent && reference.type !== ReferenceTag) throw new Error();
179
+ if (path.parent && referenceTag.type !== ReferenceTag) throw new Error();
189
180
 
190
181
  let node = createNode(tag);
191
182
 
192
183
  if (isGap) {
193
184
  if (held) {
194
185
  node = held;
195
- add(path.node, reference, node);
186
+ add(path.node, referenceTag, node, bindingTag);
196
187
  suppressTag = true;
197
188
  } else if (!expressionsCo.done) {
198
189
  expressionsCo.advance();
199
190
 
200
- let outerReference = reference;
191
+ let outerReference = referenceTag;
201
192
 
202
193
  if (!expressionsCo.done) {
203
194
  node =
@@ -211,15 +202,17 @@ function* __treeFromStream(tags, options) {
211
202
  if (isFragmentNode(node)) {
212
203
  const parentNode = path.node;
213
204
 
214
- let reference;
205
+ let referenceTag;
206
+ let bindingTag;
215
207
 
216
- for (const tag of sumtree.traverse(node.children)) {
208
+ for (const tag of Children.traverse(node.children)) {
217
209
  switch (tag.type) {
218
210
  case DoctypeTag: {
219
211
  break;
220
212
  }
221
- case OpenNodeTag:
222
- case CloseNodeTag: {
213
+
214
+ case OpenNodeTag: {
215
+ referenceTag = bindingTag = null;
223
216
  if (!tag.value.type) {
224
217
  break;
225
218
  } else {
@@ -228,29 +221,28 @@ function* __treeFromStream(tags, options) {
228
221
  }
229
222
 
230
223
  case ReferenceTag:
231
- // combine tags for .
232
-
233
- reference = tag;
224
+ referenceTag = tag;
234
225
  break;
235
226
 
236
227
  case InitializerTag: {
237
- add(parentNode, mergeReferences(outerReference, reference), []);
228
+ add(parentNode, mergeReferenceTags(outerReference, referenceTag), []);
238
229
  break;
239
230
  }
240
231
 
241
- case EmbeddedNode: {
242
- add(parentNode, mergeReferences(outerReference, reference), tag.value);
243
- break;
244
- }
245
-
246
- case GapTag: {
247
- const resolvedNode = getShifted(shift, reference, node);
248
- add(parentNode, mergeReferences(outerReference, reference), resolvedNode);
232
+ case BindingTag: {
233
+ bindingTag = tag;
249
234
  break;
250
235
  }
251
236
 
237
+ case GapTag:
252
238
  case NullTag: {
253
- add(parentNode, mergeReferences(outerReference, reference), null);
239
+ add(
240
+ parentNode,
241
+ mergeReferenceTags(outerReference, referenceTag),
242
+ buildStubNode(tag),
243
+ bindingTag,
244
+ );
245
+ referenceTag = bindingTag = null;
254
246
  break;
255
247
  }
256
248
 
@@ -262,21 +254,21 @@ function* __treeFromStream(tags, options) {
262
254
  if (path.node.flags.token) {
263
255
  throw new Error('not implemented');
264
256
  }
265
- add(path.node, reference, node);
257
+ add(path.node, referenceTag, node, bindingTag ?? buildBindingTag());
266
258
  }
267
259
  } else {
268
260
  if (!path.node.flags.token) {
269
- add(path.node, reference, node);
261
+ add(path.node, referenceTag, node, bindingTag ?? buildBindingTag());
270
262
  }
271
263
  }
272
264
  }
273
265
  }
274
266
 
275
- reference = null;
267
+ referenceTag = null;
276
268
  held = isGap ? null : held;
277
269
 
278
270
  if (!path.node.flags.token) {
279
- path = { parent: path, node, depth: (path.depth || -1) + 1, arrays: new Set() };
271
+ path = { parent: path, node, depth: (path.depth ?? -1) + 1, arrays: new Set() };
280
272
  }
281
273
 
282
274
  break;
@@ -305,20 +297,18 @@ function* __treeFromStream(tags, options) {
305
297
  const node = createNode(tag);
306
298
 
307
299
  if (path) {
308
- add(path.node, reference, node);
309
- reference = null;
300
+ add(path.node, referenceTag, node, bindingTag ?? buildBindingTag());
301
+ referenceTag = null;
310
302
  }
311
303
 
312
304
  path = { parent: path, node, depth: (path ? path.depth : -1) + 1, arrays: new Set() };
313
305
  } else {
314
- const { language, type, flags, attributes } = tag.value;
306
+ const { type, flags, attributes } = tag.value;
315
307
 
316
308
  const attributes_ = doctype?.value.attributes ?? attributes;
317
- const language_ = attributes?.['bablrLanguage'] ?? language;
318
309
 
319
310
  const node = {
320
311
  flags,
321
- language: language_,
322
312
  type,
323
313
  children: [],
324
314
  properties: {},
@@ -339,7 +329,7 @@ function* __treeFromStream(tags, options) {
339
329
  }
340
330
 
341
331
  if (!suppressTag) {
342
- path.node.children = sumtree.push(path.node.children, tag);
332
+ path.node.children = Children.push(path.node.children, tag);
343
333
  }
344
334
 
345
335
  switch (tag.type) {
@@ -403,14 +393,12 @@ export const evaluateReturnAsync = async (generator) => {
403
393
  return co.value;
404
394
  };
405
395
 
406
- export const streamFromTree = (rootNode, options = {}) => __streamFromTree(rootNode, options);
407
-
408
396
  export const isEmpty = (node) => {
409
397
  const { properties } = node;
410
398
 
411
399
  let ref = null;
412
400
 
413
- for (const tag of sumtree.traverse(node.children)) {
401
+ for (const tag of Children.traverse(node.children)) {
414
402
  switch (tag.type) {
415
403
  case ReferenceTag: {
416
404
  const { name } = tag.value;
@@ -431,8 +419,8 @@ export const isEmpty = (node) => {
431
419
  break;
432
420
  }
433
421
 
434
- case EmbeddedNode: {
435
- if (ref.value.name === '@') {
422
+ case Property: {
423
+ if (tag.value.reference.type === '@') {
436
424
  return false;
437
425
  }
438
426
  break;
@@ -449,7 +437,6 @@ export const isEmpty = (node) => {
449
437
  export const buildStubNode = (tag) => {
450
438
  return freeze({
451
439
  flags: nodeFlags,
452
- language: null,
453
440
  type: null,
454
441
  children: freeze([tag]),
455
442
  properties: freeze({}),
@@ -457,9 +444,15 @@ export const buildStubNode = (tag) => {
457
444
  });
458
445
  };
459
446
 
447
+ export const streamFromTree = (rootNode, options = {}) => {
448
+ let rootNode_ = isPlainObject(rootNode) ? rootNode : rootNode.node;
449
+
450
+ return __streamFromTree(rootNode_, options);
451
+ };
452
+
460
453
  function* __streamFromTree(rootNode, options) {
461
454
  const { unshift = false } = options;
462
- if (!rootNode || !sumtree.getSize(rootNode.children)) return;
455
+ if (!rootNode || !Children.getSize(rootNode.children)) return;
463
456
 
464
457
  let tagPath = TagPath.fromNode(rootNode, 0);
465
458
 
@@ -469,7 +462,14 @@ function* __streamFromTree(rootNode, options) {
469
462
  if (tagPath.tag.type === OpenNodeTag) count++;
470
463
  if (tagPath.tag.type === CloseNodeTag) count--;
471
464
 
472
- yield tagPath.tag;
465
+ if (
466
+ !(
467
+ tagPath.tag.type === AttributeDefinition ||
468
+ (tagPath.tag.type === BindingTag && !tagPath.tag.value.languagePath?.length)
469
+ )
470
+ ) {
471
+ yield tagPath.tag;
472
+ }
473
473
  } while ((tagPath = unshift ? tagPath.nextUnshifted : tagPath.next));
474
474
 
475
475
  if (count !== 0) throw new Error();
@@ -487,26 +487,30 @@ export const getCooked = (cookable) => {
487
487
  // const openTag = getOpenTag(cookable);
488
488
  // const closeTag = getCloseTag(cookable);
489
489
 
490
- let reference = null;
490
+ let referenceTag = null;
491
491
 
492
- for (const tag of sumtree.traverse(children)) {
492
+ for (let tag of Children.traverse(children)) {
493
493
  switch (tag.type) {
494
494
  case ReferenceTag: {
495
- const { name } = tag.value;
495
+ let { type } = tag.value;
496
496
 
497
- if (!(name === '#' || name === '@')) {
497
+ if (!(type === '#' || type === '@')) {
498
498
  throw new Error('cookable nodes must not contain other nodes');
499
499
  }
500
500
 
501
- reference = tag;
501
+ referenceTag = tag;
502
502
  break;
503
503
  }
504
504
 
505
- case EmbeddedNode: {
506
- const { attributes } = tag.value;
505
+ case BindingTag:
506
+ break;
507
+
508
+ case Property: {
509
+ let { node, reference } = tag.value;
510
+ let { attributes } = node;
507
511
 
508
- if (reference.value.name === '@') {
509
- const { cooked: cookedValue } = attributes;
512
+ if (reference.type === '@') {
513
+ let { cooked: cookedValue } = attributes;
510
514
 
511
515
  if (!isString(cookedValue))
512
516
  throw new Error('cannot cook string: it contains uncooked escapes');
@@ -556,22 +560,27 @@ export const sourceTextFor = printSource;
556
560
  export const getRange = (node) => {
557
561
  const { children } = node;
558
562
  let path = Path.from(node);
559
- return sumtree.getSize(children) ? [TagPath.from(path, 0), TagPath.from(path, -1)] : null;
563
+ return Children.getSize(children) ? [TagPath.from(path, 0), TagPath.from(path, -1)] : null;
560
564
  };
561
565
 
562
566
  export const createNode = (openTag) => {
563
567
  if (!openTag || openTag.type === GapTag || openTag.type === NullTag) {
564
568
  return {
565
569
  flags: nodeFlags,
566
- language: openTag?.language,
567
570
  type: openTag && ([NullTag, GapTag].includes(openTag.type) ? null : openTag.type),
568
571
  children: [],
569
572
  properties: {},
570
573
  attributes: openTag?.attributes || {},
571
574
  };
572
575
  } else {
573
- const { flags, language, type, attributes = {} } = openTag.value || {};
574
- return { flags, language, type, children: [], properties: {}, attributes };
576
+ const { flags, type, attributes = {} } = openTag.value || {};
577
+ return {
578
+ flags,
579
+ type,
580
+ children: [],
581
+ properties: {},
582
+ attributes,
583
+ };
575
584
  }
576
585
  };
577
586
 
@@ -595,7 +604,7 @@ export const branchProperties = (properties) => {
595
604
 
596
605
  for (const { 0: key, 1: value } of Object.entries(copy)) {
597
606
  if (isArray(value)) {
598
- copy[key] = btree.treeFromValues(value);
607
+ copy[key] = btree.fromValues(value);
599
608
  }
600
609
  }
601
610
 
@@ -603,10 +612,9 @@ export const branchProperties = (properties) => {
603
612
  };
604
613
 
605
614
  export const branchNode = (node) => {
606
- const { flags, language, type, children, properties, attributes } = node;
615
+ const { flags, type, children, properties, attributes } = node;
607
616
  return {
608
617
  flags,
609
- language,
610
618
  type,
611
619
  children,
612
620
  properties: branchProperties(properties),
@@ -622,10 +630,6 @@ export const acceptNode = (node, accepted) => {
622
630
  return node;
623
631
  };
624
632
 
625
- export const getRoot = (node) => {
626
- return node == null ? node : isFragmentNode(node) ? node.properties['.'].node : node;
627
- };
628
-
629
633
  export function* traverseProperties(properties) {
630
634
  for (const value of Object.values(properties)) {
631
635
  if (isArray(value)) {
@@ -641,3 +645,245 @@ export function* traverseProperties(properties) {
641
645
  }
642
646
  }
643
647
  }
648
+
649
+ export const addToProperties = (properties, property) => {
650
+ let { reference, node: value } = property;
651
+ let { flags, name, isArray } = reference;
652
+
653
+ if (!name) throw new Error();
654
+ if (value === null) throw new Error();
655
+ if (!hasOwn(property, 'binding')) throw new Error();
656
+
657
+ if (Object.isFrozen(properties)) {
658
+ throw new Error('not implemented');
659
+ }
660
+
661
+ let isInitializer = value === undefined || Array.isArray(value);
662
+
663
+ let outerProperty = property;
664
+
665
+ if (flags.expression && !isInitializer) {
666
+ let shiftStack =
667
+ getFirstNodeShiftStack(value) ||
668
+ btree.fromValues([buildProperty(reference, null, buildStubNode(buildGapTag()))]);
669
+
670
+ shiftStack = btree.push(shiftStack, property);
671
+ outerProperty = buildProperty(reference, null, shiftStack);
672
+ }
673
+
674
+ if (isArray && !isInitializer) {
675
+ properties[name] = buildProperty(
676
+ reference,
677
+ null,
678
+ btree.push(properties[name]?.node || btree.fromValues([]), outerProperty),
679
+ );
680
+ } else {
681
+ properties[name] = outerProperty;
682
+ }
683
+
684
+ return properties;
685
+ };
686
+
687
+ export const shiftToProperties = (properties, property) => {
688
+ let { reference, node: value } = property;
689
+ let { flags, name, isArray } = reference;
690
+
691
+ if (!name) throw new Error();
692
+
693
+ if (Object.isFrozen(properties)) {
694
+ throw new Error('not implemented');
695
+ }
696
+
697
+ if (!flags.expression) {
698
+ return addToProperties(properties, property);
699
+ }
700
+ let isInitializer = value === undefined || Array.isArray(value);
701
+
702
+ let outerProperty;
703
+ if (flags.expression && !isInitializer) {
704
+ let shiftStack =
705
+ getFirstNodeShiftStack(value) ||
706
+ btree.fromValues([buildProperty(reference, null, buildStubNode(buildGapTag()))]);
707
+
708
+ shiftStack = btree.push(shiftStack, property);
709
+ outerProperty = buildProperty(reference, null, shiftStack);
710
+ }
711
+
712
+ if (isArray) {
713
+ properties[name] = buildProperty(
714
+ reference,
715
+ null,
716
+ btree.replaceAt(-1, properties[name].node, outerProperty),
717
+ );
718
+ } else {
719
+ properties[name] = outerProperty;
720
+ }
721
+ return properties;
722
+ };
723
+
724
+ export const addProperty = (node, property) => {
725
+ if (!node || !property) throw new Error();
726
+ if (!Object.isFrozen(property)) throw new Error();
727
+
728
+ let back1 = Children.getAt(-1, node.children);
729
+ let back2 = Children.getAt(-2, node.children);
730
+
731
+ let referenceTag;
732
+ let bindingTag = null;
733
+
734
+ if (property.node === null) {
735
+ throw new Error();
736
+ }
737
+
738
+ let { node: value } = property;
739
+
740
+ let isArrayInitializer = isArray(value);
741
+ let isInitializer = value === undefined || isArrayInitializer;
742
+
743
+ if (isArrayInitializer && value.length) throw new Error();
744
+
745
+ if (!isInitializer && property.node.type && !property.binding) {
746
+ throw new Error();
747
+ }
748
+
749
+ let foundShift = false;
750
+ let foundBinding = false;
751
+ if (back1.type === BindingTag) {
752
+ bindingTag = property.binding && back1;
753
+ referenceTag = back2;
754
+ foundShift = foundBinding = true;
755
+ } else if (back1.type === ReferenceTag) {
756
+ bindingTag = property.binding && buildChild(BindingTag, property.binding);
757
+ referenceTag = back1;
758
+ foundShift = true;
759
+ } else {
760
+ bindingTag = property.binding && buildChild(BindingTag, property.binding);
761
+ referenceTag = buildChild(ReferenceTag, property.reference);
762
+ }
763
+
764
+ if (!referenceTag) throw new Error();
765
+
766
+ if (isInitializer) {
767
+ if (property.reference.name) {
768
+ addToProperties(node.properties, property);
769
+ }
770
+
771
+ if (!foundShift) {
772
+ node.children = Children.push(node.children, referenceTag);
773
+ }
774
+ node.children = Children.push(node.children, buildInitializerTag(isArrayInitializer));
775
+
776
+ return;
777
+ }
778
+
779
+ if (property.reference.name) {
780
+ addToProperties(node.properties, property);
781
+ }
782
+
783
+ if (!foundShift) {
784
+ node.children = Children.push(node.children, referenceTag);
785
+ }
786
+ if (!foundBinding && bindingTag) {
787
+ node.children = Children.push(node.children, bindingTag);
788
+ }
789
+ node.children = Children.push(node.children, buildChild(Property, property));
790
+
791
+ return node;
792
+ };
793
+
794
+ export const shiftProperty = (node, property) => {
795
+ if (!node || !property) throw new Error();
796
+ if (!property.reference) throw new Error();
797
+ if (!Object.isFrozen(property)) throw new Error();
798
+
799
+ let back1 = Children.getAt(-1, node.children);
800
+ let back2 = Children.getAt(-2, node.children);
801
+
802
+ let shiftTag;
803
+ let bindingTag = null;
804
+
805
+ if (property.node === null) {
806
+ property.node = buildNullNode();
807
+ }
808
+
809
+ let { node: value } = property;
810
+
811
+ let isArrayInitializer = isArray(value);
812
+ let isInitializer = value === undefined || isArrayInitializer;
813
+
814
+ if (!isInitializer && !property.binding) {
815
+ throw new Error();
816
+ }
817
+
818
+ let foundShift = false;
819
+ let foundBinding = false;
820
+ if (back1.type === BindingTag) {
821
+ bindingTag = back1;
822
+ shiftTag = back2;
823
+ foundShift = foundBinding = true;
824
+
825
+ if (bindingTag.value !== property.binding) throw new Error();
826
+ } else if ([ShiftTag, ReferenceTag].includes(back1.type)) {
827
+ bindingTag = buildChild(BindingTag, property.binding);
828
+ shiftTag = back1;
829
+ foundShift = true;
830
+ } else {
831
+ bindingTag = buildChild(BindingTag, property.binding);
832
+ shiftTag = buildChild(ReferenceTag, property.reference);
833
+ }
834
+
835
+ if (!shiftTag) throw new Error();
836
+
837
+ if (isInitializer) {
838
+ shiftToProperties(node.properties, property);
839
+
840
+ if (!foundShift) {
841
+ node.children = Children.push(node.children, shiftTag);
842
+ }
843
+ node.children = Children.push(node.children, buildInitializerTag(isArrayInitializer));
844
+
845
+ return;
846
+ }
847
+
848
+ let shiftStack = node.properties[property.reference.name]?.node || btree.fromValues([]);
849
+ let stackSize = shiftStack && property.reference.flags.expression ? btree.getSize(shiftStack) : 0;
850
+
851
+ shiftToProperties(node.properties, property);
852
+
853
+ let index = shiftTag.type === ReferenceTag ? 1 : shiftTag.value.index + 1;
854
+ let height = btree.getSize(shiftStack) + 1;
855
+
856
+ let newShiftTag = stackSize ? buildShiftTag(index, height) : shiftTag;
857
+
858
+ if (!foundShift) {
859
+ node.children = Children.push(node.children, newShiftTag);
860
+ }
861
+ if (!foundBinding) {
862
+ node.children = Children.push(node.children, bindingTag);
863
+ }
864
+ node.children = Children.push(node.children, buildChild(Property, property));
865
+
866
+ return node;
867
+ };
868
+
869
+ export const add = (node, referenceTag, value, bindingTag) => {
870
+ let lastChild = Children.getAt(-1, node.children);
871
+ let reference = referenceTag.value;
872
+ let binding = bindingTag
873
+ ? bindingTag.value
874
+ : lastChild.type === BindingTag
875
+ ? lastChild.value
876
+ : buildBindingTag().value;
877
+ return addProperty(node, buildProperty(reference, binding, value));
878
+ };
879
+
880
+ export const shift = (node, referenceTag, value, bindingTag) => {
881
+ let lastChild = Children.getAt(-1, node.children);
882
+ let reference = referenceTag.value;
883
+ let binding = bindingTag
884
+ ? bindingTag.value
885
+ : lastChild.type === BindingTag
886
+ ? lastChild.value
887
+ : buildBindingTag().value;
888
+ return shiftProperty(node, buildProperty(reference, binding, value));
889
+ };