@bablr/agast-helpers 0.8.0 → 0.10.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
@@ -1,72 +1,77 @@
1
- import { Coroutine } from '@bablr/coroutine';
2
1
  import {
3
- nodeFlags,
4
2
  buildReferenceTag,
5
- buildNullTag,
6
3
  buildOpenNodeTag,
7
4
  buildLiteralTag,
8
5
  buildCloseNodeTag,
9
6
  tokenFlags,
10
- buildInitializerTag,
11
- buildBindingTag,
12
- buildProperty,
13
7
  buildChild,
14
- buildShiftTag,
15
8
  buildGapTag,
9
+ buildOpenFragmentTag,
16
10
  } from './builders.js';
17
11
  import {
18
12
  printPrettyCSTML as printPrettyCSTMLFromStream,
19
13
  printCSTML as printCSTMLFromStream,
20
14
  printSource as printSourceFromStream,
21
- getStreamIterator,
15
+ treeFromStream,
16
+ treeFromStreamSync,
17
+ treeFromStreamAsync,
18
+ evaluateReturnSync,
19
+ evaluateReturnAsync,
22
20
  } from './stream.js';
23
21
  import {
24
- DoctypeTag,
25
22
  OpenNodeTag,
26
23
  CloseNodeTag,
27
24
  ReferenceTag,
28
25
  GapTag,
29
- NullTag,
30
- InitializerTag,
31
26
  LiteralTag,
32
27
  AttributeDefinition,
33
28
  BindingTag,
34
- Property,
35
29
  ShiftTag,
30
+ Property,
31
+ NullNode,
32
+ TreeNode,
33
+ GapNode,
36
34
  } from './symbols.js';
37
- import * as btree from './btree.js';
38
- import * as Children from './children.js';
35
+ import * as Tags from './tags.js';
39
36
  export * from './builders.js';
40
37
  export * from './print.js';
41
38
  import {
42
39
  get,
40
+ getOr,
41
+ has,
42
+ list,
43
43
  TagPath,
44
- Path,
45
- isFragmentNode,
44
+ isCover,
46
45
  isNullNode,
47
46
  isGapNode,
48
47
  getOpenTag,
49
48
  getCloseTag,
50
49
  getRoot,
51
- getRootArray,
52
- getFirstNodeShiftStack,
50
+ getTags,
53
51
  } from './path.js';
54
- import { isPlainObject } from './object.js';
52
+ import { freeze, isPlainObject } from './object.js';
55
53
 
56
54
  export {
57
55
  get,
58
- isFragmentNode,
56
+ getOr,
57
+ has,
58
+ list,
59
+ isCover,
59
60
  isNullNode,
60
61
  isGapNode,
61
62
  getOpenTag,
62
63
  getCloseTag,
63
64
  getRoot,
64
- getRootArray,
65
+ treeFromStream,
66
+ treeFromStreamSync,
67
+ treeFromStreamAsync,
68
+ evaluateReturnSync,
69
+ evaluateReturnAsync,
65
70
  };
66
71
 
67
- export const buildToken = (type, value, attributes = {}) => {
72
+ export const buildToken = (name, value, attributes = {}) => {
68
73
  return treeFromStreamSync([
69
- buildOpenNodeTag(tokenFlags, type, attributes),
74
+ buildOpenNodeTag(tokenFlags, name, null, attributes),
70
75
  buildLiteralTag(value),
71
76
  buildCloseNodeTag(),
72
77
  ]);
@@ -75,25 +80,26 @@ export const buildToken = (type, value, attributes = {}) => {
75
80
  const isString = (str) => typeof str === 'string';
76
81
 
77
82
  const { isArray } = Array;
78
- const { freeze, hasOwn } = Object;
79
83
 
80
84
  export const mergeReferences = (outer, inner) => {
81
- let { type, name, isArray, index, flags: { expression, hasGap } = {} } = outer;
85
+ let { type, name, index, flags: { array, expression, intrinsic, hasGap } = {} } = outer;
82
86
 
83
87
  if (
84
88
  (name != null && inner.name != null && name !== inner.name) ||
85
- (type != null && inner.type != null && type !== inner.type)
89
+ (type != null && inner.type != null && type !== inner.type && inner.type !== '.')
86
90
  ) {
87
91
  return inner;
88
92
  }
89
93
 
90
- isArray = isArray || inner.isArray;
94
+ array = !!(array || inner.flags.array);
91
95
  expression = !!(expression || inner.flags.expression);
96
+ intrinsic = !!(intrinsic || inner.flags.intrinsic);
92
97
  hasGap = !!(hasGap || inner.flags.hasGap);
93
98
  name = type === '.' ? inner.name : name;
94
99
  type = type === '.' ? inner.type : type;
95
100
 
96
- return buildReferenceTag(type, name, isArray, { expression, hasGap }, index).value;
101
+ return buildReferenceTag(type, name, freeze({ array, expression, intrinsic, hasGap }), index)
102
+ .value;
97
103
  };
98
104
 
99
105
  export const mergeReferenceTags = (outer, inner) => {
@@ -102,385 +108,176 @@ export const mergeReferenceTags = (outer, inner) => {
102
108
  return buildChild(ReferenceTag, value);
103
109
  };
104
110
 
105
- export const isEmptyReference = (ref) => {
106
- let { type, isArray, flags } = ref.value;
107
- return type === '.' && !isArray && !(flags.expression || flags.hasGap);
108
- };
109
-
110
- function* __treeFromStream(tags, options) {
111
- let path = null;
112
- let rootPath = null;
113
- let held = null;
114
- let doctype = null;
115
- const co = new Coroutine(getStreamIterator(tags));
116
- const expressionsCo = new Coroutine(getStreamIterator(options.expressions || []));
117
- let referenceTag = null;
118
- let bindingTag = null;
119
-
120
- for (;;) {
121
- co.advance();
122
-
123
- if (co.current instanceof Promise) {
124
- co.current = yield co.current;
125
- }
126
-
127
- if (co.done) break;
111
+ export const documentFromStream = (tags, options) => {
112
+ throw new Error('not implemented');
128
113
 
129
- const tag = co.value;
114
+ // strip doctype tag off
130
115
 
131
- if (tag.type === 'Effect') {
132
- continue;
133
- }
134
-
135
- if (tag.type === DoctypeTag) {
136
- doctype = tag;
137
- continue;
138
- }
139
-
140
- if (held && tag.type !== OpenNodeTag && tag.type !== GapTag) {
141
- throw new Error('cannot eat this type of tag while holding');
142
- }
116
+ // return buildChild(Document, freeze({ doctype, tree }));
117
+ };
143
118
 
144
- let suppressTag = false;
119
+ export const isEmpty = (node) => {
120
+ if (node == null) return true;
145
121
 
122
+ for (const tag of Tags.traverse(getTags(node))) {
146
123
  switch (tag.type) {
147
- case LiteralTag:
148
- case CloseNodeTag: {
149
- break;
150
- }
151
-
152
- case ReferenceTag: {
153
- referenceTag = tag;
154
- suppressTag = true;
155
- break;
156
- }
157
-
158
- case BindingTag: {
159
- bindingTag = tag;
160
- suppressTag = true;
161
- break;
162
- }
163
-
164
- case InitializerTag: {
165
- add(path.node, referenceTag, []);
166
- suppressTag = true;
167
- referenceTag = null;
168
- break;
169
- }
170
-
171
- case NullTag:
172
- case GapTag: {
173
- if (!path) {
174
- return buildStubNode(tag);
175
- }
124
+ case Property: {
125
+ if (tag.value.property.reference.type === '@') {
126
+ return false;
127
+ } else {
128
+ const { property } = tag.value;
176
129
 
177
- const isGap = tag.type === GapTag;
178
-
179
- if (path.parent && referenceTag.type !== ReferenceTag) throw new Error();
180
-
181
- let node = createNode(tag);
182
-
183
- if (isGap) {
184
- if (held) {
185
- node = held;
186
- add(path.node, referenceTag, node, bindingTag);
187
- suppressTag = true;
188
- } else if (!expressionsCo.done) {
189
- expressionsCo.advance();
190
-
191
- let outerReference = referenceTag;
192
-
193
- if (!expressionsCo.done) {
194
- node =
195
- node == null
196
- ? buildStubNode(buildNullTag())
197
- : expressionsCo.value == null
198
- ? buildStubNode(buildNullTag())
199
- : expressionsCo.value;
200
- suppressTag = true;
201
-
202
- if (isFragmentNode(node)) {
203
- const parentNode = path.node;
204
-
205
- let referenceTag;
206
- let bindingTag;
207
-
208
- for (const tag of Children.traverse(node.children)) {
209
- switch (tag.type) {
210
- case DoctypeTag: {
211
- break;
212
- }
213
-
214
- case OpenNodeTag: {
215
- referenceTag = bindingTag = null;
216
- if (!tag.value.type) {
217
- break;
218
- } else {
219
- throw new Error();
220
- }
221
- }
222
-
223
- case ReferenceTag:
224
- referenceTag = tag;
225
- break;
226
-
227
- case InitializerTag: {
228
- add(parentNode, mergeReferenceTags(outerReference, referenceTag), []);
229
- break;
230
- }
231
-
232
- case BindingTag: {
233
- bindingTag = tag;
234
- break;
235
- }
236
-
237
- case GapTag:
238
- case NullTag: {
239
- add(
240
- parentNode,
241
- mergeReferenceTags(outerReference, referenceTag),
242
- buildStubNode(tag),
243
- bindingTag,
244
- );
245
- referenceTag = bindingTag = null;
246
- break;
247
- }
248
-
249
- default:
250
- throw new Error();
251
- }
252
- }
253
- } else {
254
- if (path.node.flags.token) {
255
- throw new Error('not implemented');
256
- }
257
- add(path.node, referenceTag, node, bindingTag ?? buildBindingTag());
258
- }
259
- } else {
260
- if (!path.node.flags.token) {
261
- add(path.node, referenceTag, node, bindingTag ?? buildBindingTag());
262
- }
263
- }
130
+ if (!isNullNode(property.node)) {
131
+ return false;
264
132
  }
265
133
  }
266
-
267
- referenceTag = null;
268
- held = isGap ? null : held;
269
-
270
- if (!path.node.flags.token) {
271
- path = { parent: path, node, depth: (path.depth ?? -1) + 1, arrays: new Set() };
272
- }
273
-
274
134
  break;
275
135
  }
276
136
 
277
- // case ShiftTag: {
278
- // const { children, properties } = path.node;
279
-
280
- // let property = properties[ref.value.name];
281
- // let node;
282
-
283
- // if (ref.value.isArray) {
284
- // ({ node } = btree.getAt(-1, property));
285
- // properties[ref.value.name].pop();
286
- // } else {
287
- // ({ node } = property);
288
- // properties[ref.value.name] = null;
289
- // }
290
-
291
- // held = node;
292
- // break;
293
- // }
294
-
295
- case OpenNodeTag: {
296
- if (path) {
297
- const node = createNode(tag);
298
-
299
- if (path) {
300
- add(path.node, referenceTag, node, bindingTag ?? buildBindingTag());
301
- referenceTag = null;
302
- }
137
+ case LiteralTag:
138
+ case GapTag:
139
+ return false;
140
+ }
141
+ }
142
+ return true;
143
+ };
303
144
 
304
- path = { parent: path, node, depth: (path ? path.depth : -1) + 1, arrays: new Set() };
305
- } else {
306
- const { type, flags, attributes } = tag.value;
145
+ export const streamFromTree = (tree, options = {}) => {
146
+ if (tree && !isPlainObject(tree)) throw new Error();
307
147
 
308
- const attributes_ = doctype?.value.attributes ?? attributes;
148
+ return __streamFromTree(null, tree, options);
149
+ };
309
150
 
310
- const node = {
311
- flags,
312
- type,
313
- children: [],
314
- properties: {},
315
- attributes: attributes_,
316
- };
151
+ function* __streamFromTree(doctypeTag, rootNode, options) {
152
+ const { unshift = false } = options;
153
+ if (!rootNode || !Tags.getSize(getTags(rootNode))) return;
317
154
 
318
- path = { parent: null, node, depth: 0, arrays: new Set() };
155
+ let tagPath = TagPath.fromNode(rootNode, 0);
319
156
 
320
- rootPath = path;
321
- }
157
+ let count = 0;
322
158
 
323
- break;
324
- }
159
+ if (doctypeTag) {
160
+ yield doctypeTag;
161
+ }
325
162
 
326
- default: {
327
- throw new Error();
328
- }
329
- }
163
+ do {
164
+ if (tagPath.tag.type === OpenNodeTag && !tagPath.tag.value.selfClosing) count++;
165
+ if (tagPath.tag.type === CloseNodeTag) count--;
330
166
 
331
- if (!suppressTag) {
332
- path.node.children = Children.push(path.node.children, tag);
167
+ if (
168
+ !(
169
+ tagPath.tag.type === AttributeDefinition ||
170
+ (tagPath.tag.type === BindingTag && !tagPath.tag.value.segments?.length)
171
+ )
172
+ ) {
173
+ yield tagPath.tag;
333
174
  }
175
+ } while ((tagPath = unshift ? tagPath.nextUnshifted : tagPath.next));
334
176
 
335
- switch (tag.type) {
336
- case NullTag:
337
- case GapTag:
338
- case CloseNodeTag: {
339
- const completedNode = path.node;
177
+ // if (count !== 0) throw new Error();
178
+ }
340
179
 
341
- if (!(tag.type === GapTag && completedNode.flags.token)) {
342
- finalizeNode(completedNode);
343
- }
180
+ export const vcsStreamFromTree = (rootNode) => {
181
+ return __vcsStreamFromTree(rootNode);
182
+ };
344
183
 
345
- if (tag.type === GapTag) {
346
- if (path && completedNode.type === null && completedNode.flags.token) {
347
- break;
348
- }
184
+ function* __vcsStreamFromTree(rootNode) {
185
+ let stack = null;
186
+ let agastNode = rootNode;
187
+ let depth = 0;
188
+ let nodeShifted = false;
189
+ let i = 0;
190
+ let seenFirstProperty = false;
191
+ let node = Tags.getValues(rootNode.value.tags);
192
+
193
+ outer: while (node) {
194
+ while (i >= node.length) {
195
+ if (stack) {
196
+ let oldAgastNode = agastNode;
197
+ let hadSeenFirst = seenFirstProperty;
198
+ ({ stack, agastNode, node, depth, i, nodeShifted, seenFirstProperty } = stack);
199
+ if (oldAgastNode === agastNode) {
200
+ seenFirstProperty = hadSeenFirst;
201
+ yield buildCloseNodeTag();
349
202
  }
350
203
 
351
- path = path.parent;
352
- break;
204
+ i++;
205
+ } else {
206
+ return;
353
207
  }
354
208
  }
355
- }
356
-
357
- if (path && path.node.type) {
358
- throw new Error('imbalanced tag stack');
359
- }
360
-
361
- return rootPath.node;
362
- }
363
-
364
- export const buildNullNode = () => {
365
- return treeFromStreamSync([buildNullTag()]);
366
- };
367
209
 
368
- export const treeFromStream = (tags, options = {}) => __treeFromStream(tags, options);
210
+ let child = node[i];
369
211
 
370
- export const treeFromStreamSync = (tokens, options = {}) => {
371
- return evaluateReturnSync(treeFromStream(tokens, options));
372
- };
373
-
374
- export const treeFromStreamAsync = async (tokens, options = {}) => {
375
- return evaluateReturnAsync(treeFromStream(tokens, options));
376
- };
212
+ if (isArray(child)) {
213
+ stack = { stack, agastNode, node, depth, i, nodeShifted, seenFirstProperty };
377
214
 
378
- export const evaluateReturnSync = (generator) => {
379
- const co = new Coroutine(generator[Symbol.iterator]());
380
- while (!co.done) co.advance();
381
- return co.value;
382
- };
215
+ yield buildOpenFragmentTag();
383
216
 
384
- export const evaluateReturnAsync = async (generator) => {
385
- const co = new Coroutine(getStreamIterator(generator));
386
- while (!co.done) {
387
- co.advance();
217
+ node = Tags.getValues(child);
218
+ depth++;
219
+ i = 0;
220
+ } else {
221
+ let wrappedTag = child;
388
222
 
389
- if (co.current instanceof Promise) {
390
- co.current = await co.current;
391
- }
392
- }
393
- return co.value;
394
- };
223
+ if (wrappedTag.type === Property) {
224
+ for (let tag of wrappedTag.value.tags) {
225
+ switch (tag.type) {
226
+ case TreeNode:
227
+ case NullNode:
228
+ case GapNode: {
229
+ let replaceWithGap = nodeShifted && !seenFirstProperty;
395
230
 
396
- export const isEmpty = (node) => {
397
- const { properties } = node;
231
+ if (replaceWithGap) {
232
+ yield buildGapTag();
398
233
 
399
- let ref = null;
234
+ seenFirstProperty = true;
400
235
 
401
- for (const tag of Children.traverse(node.children)) {
402
- switch (tag.type) {
403
- case ReferenceTag: {
404
- const { name } = tag.value;
236
+ break;
237
+ } else {
238
+ stack = { stack, agastNode, node, depth, i, nodeShifted, seenFirstProperty: true };
239
+ node = Tags.getValues(tag.value.tags);
240
+
241
+ depth++;
242
+ i = 0;
243
+ agastNode = tag.value;
244
+ nodeShifted = wrappedTag.value.tags[0].type === ShiftTag;
245
+ seenFirstProperty = false;
246
+ continue outer;
247
+ }
248
+ }
405
249
 
406
- ref = tag;
250
+ default: {
251
+ yield tag;
407
252
 
408
- if (properties[name]) {
409
- const property = properties[name];
253
+ if (tag.type === OpenNodeTag && tag.value.literalValue) {
254
+ ({ stack, agastNode, node, depth, i, nodeShifted } = stack);
255
+ seenFirstProperty = true;
256
+ }
410
257
 
411
- if (
412
- property != null ||
413
- (isArray(property) && property.length) ||
414
- !isNullNode(property.node)
415
- ) {
416
- return false;
258
+ break;
259
+ }
417
260
  }
418
261
  }
419
- break;
420
- }
262
+ } else {
263
+ yield wrappedTag;
421
264
 
422
- case Property: {
423
- if (tag.value.reference.type === '@') {
424
- return false;
265
+ if (wrappedTag.type === OpenNodeTag && wrappedTag.value.literalValue) {
266
+ ({ stack, agastNode, node, depth, i, nodeShifted } = stack);
267
+ seenFirstProperty = true;
425
268
  }
426
- break;
427
269
  }
428
-
429
- case LiteralTag:
430
- case GapTag:
431
- return false;
270
+ i++;
432
271
  }
433
272
  }
434
- return true;
435
- };
436
-
437
- export const buildStubNode = (tag) => {
438
- return freeze({
439
- flags: nodeFlags,
440
- type: null,
441
- children: freeze([tag]),
442
- properties: freeze({}),
443
- attributes: freeze({}),
444
- });
445
- };
446
-
447
- export const streamFromTree = (rootNode, options = {}) => {
448
- let rootNode_ = isPlainObject(rootNode) ? rootNode : rootNode.node;
449
-
450
- return __streamFromTree(rootNode_, options);
451
- };
452
-
453
- function* __streamFromTree(rootNode, options) {
454
- const { unshift = false } = options;
455
- if (!rootNode || !Children.getSize(rootNode.children)) return;
456
-
457
- let tagPath = TagPath.fromNode(rootNode, 0);
458
-
459
- let count = 0;
460
-
461
- do {
462
- if (tagPath.tag.type === OpenNodeTag) count++;
463
- if (tagPath.tag.type === CloseNodeTag) count--;
464
-
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
- } while ((tagPath = unshift ? tagPath.nextUnshifted : tagPath.next));
474
-
475
- if (count !== 0) throw new Error();
476
273
  }
477
274
 
478
275
  export const getCooked = (cookable) => {
479
- if (!cookable || isGapNode(cookable.type)) {
276
+ if (!cookable || isGapNode(cookable)) {
480
277
  return '';
481
278
  }
482
279
 
483
- const children = cookable.children || cookable;
280
+ const tags = getTags(cookable) || cookable;
484
281
 
485
282
  let cooked = '';
486
283
 
@@ -489,7 +286,7 @@ export const getCooked = (cookable) => {
489
286
 
490
287
  let referenceTag = null;
491
288
 
492
- for (let tag of Children.traverse(children)) {
289
+ for (let tag of Tags.traverse(tags)) {
493
290
  switch (tag.type) {
494
291
  case ReferenceTag: {
495
292
  let { type } = tag.value;
@@ -507,7 +304,7 @@ export const getCooked = (cookable) => {
507
304
 
508
305
  case Property: {
509
306
  let { node, reference } = tag.value;
510
- let { attributes } = node;
307
+ let { attributes } = node.value;
511
308
 
512
309
  if (reference.type === '@') {
513
310
  let { cooked: cookedValue } = attributes;
@@ -521,6 +318,10 @@ export const getCooked = (cookable) => {
521
318
  break;
522
319
  }
523
320
 
321
+ case GapTag: {
322
+ return null;
323
+ }
324
+
524
325
  case LiteralTag: {
525
326
  cooked += tag.value;
526
327
  break;
@@ -543,54 +344,20 @@ export const getCooked = (cookable) => {
543
344
  return cooked;
544
345
  };
545
346
 
546
- export const printCSTML = (rootNode) => {
547
- return printCSTMLFromStream(streamFromTree(rootNode));
347
+ export const printCSTML = (tree) => {
348
+ return printCSTMLFromStream(streamFromTree(tree));
548
349
  };
549
350
 
550
- export const printPrettyCSTML = (rootNode, options = {}) => {
551
- return printPrettyCSTMLFromStream(streamFromTree(rootNode), options);
351
+ export const printPrettyCSTML = (tree, options = {}) => {
352
+ return printPrettyCSTMLFromStream(streamFromTree(tree), options);
552
353
  };
553
354
 
554
- export const printSource = (rootNode) => {
555
- return printSourceFromStream(streamFromTree(rootNode, { unshift: true }));
355
+ export const printSource = (tree) => {
356
+ return printSourceFromStream(streamFromTree(tree, { unshift: true }));
556
357
  };
557
358
 
558
359
  export const sourceTextFor = printSource;
559
360
 
560
- export const getRange = (node) => {
561
- const { children } = node;
562
- let path = Path.from(node);
563
- return Children.getSize(children) ? [TagPath.from(path, 0), TagPath.from(path, -1)] : null;
564
- };
565
-
566
- export const createNode = (openTag) => {
567
- if (!openTag || openTag.type === GapTag || openTag.type === NullTag) {
568
- return {
569
- flags: nodeFlags,
570
- type: openTag && ([NullTag, GapTag].includes(openTag.type) ? null : openTag.type),
571
- children: [],
572
- properties: {},
573
- attributes: openTag?.attributes || {},
574
- };
575
- } else {
576
- const { flags, type, attributes = {} } = openTag.value || {};
577
- return {
578
- flags,
579
- type,
580
- children: [],
581
- properties: {},
582
- attributes,
583
- };
584
- }
585
- };
586
-
587
- export const finalizeNode = (node) => {
588
- freeze(node);
589
- freeze(node.properties);
590
- freeze(node.attributes);
591
- return node;
592
- };
593
-
594
361
  export const notNull = (node) => {
595
362
  return node != null && !isNullNode(node);
596
363
  };
@@ -598,292 +365,3 @@ export const notNull = (node) => {
598
365
  export const isNull = (node) => {
599
366
  return node == null || isNullNode(node);
600
367
  };
601
-
602
- export const branchProperties = (properties) => {
603
- const copy = { ...properties };
604
-
605
- for (const { 0: key, 1: value } of Object.entries(copy)) {
606
- if (isArray(value)) {
607
- copy[key] = btree.fromValues(value);
608
- }
609
- }
610
-
611
- return copy;
612
- };
613
-
614
- export const branchNode = (node) => {
615
- const { flags, type, children, properties, attributes } = node;
616
- return {
617
- flags,
618
- type,
619
- children,
620
- properties: branchProperties(properties),
621
- attributes: { ...attributes },
622
- };
623
- };
624
-
625
- export const acceptNode = (node, accepted) => {
626
- const { children, properties, attributes } = accepted;
627
- node.children = children;
628
- node.properties = properties;
629
- node.attributes = attributes;
630
- return node;
631
- };
632
-
633
- export function* traverseProperties(properties) {
634
- for (const value of Object.values(properties)) {
635
- if (isArray(value)) {
636
- for (let item of btree.traverse(value)) {
637
- if (isArray(item.node)) {
638
- yield btree.getAt(-1, item.node);
639
- } else {
640
- yield item.node;
641
- }
642
- }
643
- } else {
644
- yield value.node;
645
- }
646
- }
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
- };