@bablr/agast-helpers 0.5.2 → 0.6.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/stream.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Coroutine } from '@bablr/coroutine';
2
2
  import emptyStack from '@iter-tools/imm-stack';
3
3
  import { printSelfClosingNodeTag, printTag } from './print.js';
4
- import { buildWriteEffect, buildTokenGroup } from './builders.js';
4
+ import { buildTokenGroup, buildWriteEffect } from './builders.js';
5
5
  import {
6
6
  DoctypeTag,
7
7
  OpenNodeTag,
@@ -10,18 +10,16 @@ import {
10
10
  ShiftTag,
11
11
  GapTag,
12
12
  NullTag,
13
- ArrayTag,
13
+ ArrayInitializerTag,
14
14
  LiteralTag,
15
- EmbeddedExpression,
15
+ EmbeddedObject,
16
16
  TokenGroup,
17
- OpenFragmentTag,
18
- CloseFragmentTag,
19
17
  } from './symbols.js';
20
18
 
21
19
  export * from './print.js';
22
20
 
23
- const getEmbeddedExpression = (obj) => {
24
- if (obj.type !== EmbeddedExpression) throw new Error();
21
+ const getEmbeddedObject = (obj) => {
22
+ if (obj.type !== EmbeddedObject) throw new Error();
25
23
  return obj.value;
26
24
  };
27
25
 
@@ -171,23 +169,24 @@ function* __isEmpty(tags) {
171
169
  if (co.done) break;
172
170
 
173
171
  let depth = 0;
172
+ let ref = null;
174
173
 
175
174
  const tag = co.value;
176
175
 
177
176
  switch (tag.type) {
178
- case OpenFragmentTag:
177
+ case ReferenceTag:
178
+ ref = tag;
179
+ break;
180
+
179
181
  case OpenNodeTag:
180
- if (tag.value.flags.trivia) {
181
- ++depth;
182
- }
182
+ ++depth;
183
183
 
184
- if (depth === 0 && tag.value.flags.escape) {
184
+ if (depth === 0 && ref.value.name === '@') {
185
185
  return false;
186
186
  }
187
187
 
188
188
  break;
189
189
 
190
- case CloseFragmentTag:
191
190
  case CloseNodeTag:
192
191
  --depth;
193
192
  break;
@@ -220,7 +219,7 @@ function* __generateStandardOutput(tags) {
220
219
  if (tag.type === 'Effect') {
221
220
  const effect = tag.value;
222
221
  if (effect.verb === 'write') {
223
- const writeEffect = getEmbeddedExpression(effect.value);
222
+ const writeEffect = getEmbeddedObject(effect.value);
224
223
  if (writeEffect.stream == null || writeEffect.stream === 1) {
225
224
  yield* writeEffect.text;
226
225
  }
@@ -249,10 +248,14 @@ function* __generateAllOutput(tags) {
249
248
  if (tag.type === 'Effect') {
250
249
  const effect = tag.value;
251
250
  if (effect.verb === 'write') {
252
- const writeEffect = getEmbeddedExpression(effect.value);
251
+ const writeEffect = getEmbeddedObject(effect.value);
253
252
  const prevStream = currentStream;
254
- currentStream = getEmbeddedExpression(writeEffect.options).stream || 1;
255
- if (prevStream && prevStream !== currentStream && !writeEffect.text.startsWith('\n')) {
253
+ currentStream = getEmbeddedObject(writeEffect.options).stream || 1;
254
+ if (
255
+ prevStream &&
256
+ (prevStream !== currentStream || currentStream === 2) &&
257
+ !writeEffect.text.startsWith('\n')
258
+ ) {
256
259
  yield* '\n';
257
260
  }
258
261
  yield* writeEffect.text;
@@ -264,7 +267,7 @@ function* __generateAllOutput(tags) {
264
267
  export const generateAllOutput = (tags) => new StreamIterable(__generateAllOutput(tags));
265
268
 
266
269
  export const printCSTML = (tags) => {
267
- return stringFromStream(generateStandardOutput(generateCSTMLStrategy(tags)));
270
+ return stringFromStream(generateStandardOutput(generateCSTML(tags)));
268
271
  };
269
272
 
270
273
  function* __emptyStreamIterator() {}
@@ -309,17 +312,15 @@ export const stringFromStream = (stream) => {
309
312
  return str;
310
313
  };
311
314
 
312
- function* __generateCSTMLStrategy(tags, options) {
313
- let { emitEffects = false, inline: inlineOption = true } = options;
314
-
315
+ function* __generateCSTML(tags, options) {
315
316
  if (!tags) {
316
- yield buildWriteEffect('<//>');
317
+ yield* '<//>';
317
318
  return;
318
319
  }
319
320
 
320
321
  let prevTag = null;
321
322
 
322
- const co = new Coroutine(getStreamIterator(prettyGroupTokens(tags)));
323
+ const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
323
324
 
324
325
  for (;;) {
325
326
  co.advance();
@@ -332,45 +333,43 @@ function* __generateCSTMLStrategy(tags, options) {
332
333
  const tag = co.value;
333
334
 
334
335
  if (tag.type === ReferenceTag && prevTag.type === NullTag) {
335
- yield buildWriteEffect(' ');
336
+ yield* ' ';
336
337
  }
337
338
 
338
339
  if (tag.type === 'Effect') {
339
- const effect = tag.value;
340
- if (emitEffects && effect.verb === 'write') {
341
- yield buildWriteEffect(effect.value.text, effect.value.options);
342
- }
343
340
  continue;
344
341
  }
345
342
 
346
343
  if (tag.type === TokenGroup) {
347
344
  const intrinsicValue = getCooked(tag.value);
348
- yield buildWriteEffect(printSelfClosingNodeTag(tag.value[0], intrinsicValue));
345
+ yield* printSelfClosingNodeTag(tag.value[0], intrinsicValue);
349
346
  } else {
350
- yield buildWriteEffect(printTag(tag));
347
+ yield* printTag(tag);
351
348
  }
352
349
 
353
350
  prevTag = tag;
354
351
  }
355
352
 
356
- yield buildWriteEffect('\n');
353
+ yield* '\n';
357
354
  }
358
355
 
359
- export const generateCSTMLStrategy = (tags, options = {}) =>
360
- new StreamIterable(__generateCSTMLStrategy(tags, options));
356
+ export const generateCSTML = (tags, options = {}) =>
357
+ new StreamIterable(__generateCSTML(tags, options));
361
358
 
362
359
  const isToken = (tag) => {
363
360
  return tag.value.flags.token;
364
361
  };
365
362
 
366
- export const prettyGroupTokens = (tags) => new StreamIterable(__prettyGroupTokens(tags));
363
+ export const prettyGroupTags = (tags) => new StreamIterable(__prettyGroupTags(tags));
367
364
 
368
- function* __prettyGroupTokens(tags) {
365
+ function* __prettyGroupTags(tags) {
369
366
  let states = emptyStack.push({ holding: [], broken: false, open: null });
370
367
  let state = states.value;
371
368
 
372
369
  const co = new Coroutine(getStreamIterator(tags));
373
370
 
371
+ let ref = null;
372
+
374
373
  for (;;) {
375
374
  co.advance();
376
375
 
@@ -381,18 +380,18 @@ function* __prettyGroupTokens(tags) {
381
380
  }
382
381
 
383
382
  const tag = co.value;
384
- const isOpenClose =
385
- tag.type === CloseNodeTag ||
386
- tag.type === OpenNodeTag ||
387
- tag.type === CloseFragmentTag ||
388
- tag.type === OpenFragmentTag;
383
+ const isOpenClose = tag.type === CloseNodeTag || tag.type === OpenNodeTag;
384
+
385
+ if (tag.type === ReferenceTag) {
386
+ ref = tag;
387
+ }
389
388
 
390
389
  if (
391
390
  (tag.type === 'Effect' && tag.value.verb === 'write') ||
392
- [ReferenceTag, DoctypeTag, GapTag, NullTag, ArrayTag, ShiftTag, OpenFragmentTag].includes(
391
+ [ReferenceTag, DoctypeTag, GapTag, NullTag, ArrayInitializerTag, ShiftTag].includes(
393
392
  tag.type,
394
393
  ) ||
395
- (tag.type === OpenNodeTag && tag.value.flags.escape)
394
+ (tag.type === OpenNodeTag && (!tag.value.type || ref?.value.name === '@'))
396
395
  ) {
397
396
  state.broken = true;
398
397
 
@@ -408,7 +407,7 @@ function* __prettyGroupTokens(tags) {
408
407
  yield tag;
409
408
  }
410
409
 
411
- if (tag.type === CloseNodeTag || tag.type === CloseFragmentTag) {
410
+ if (tag.type === CloseNodeTag) {
412
411
  if (!state.broken && (isToken(state.open) || state.holding.length === 1)) {
413
412
  state.holding.push(tag);
414
413
  yield buildTokenGroup(state.holding);
@@ -423,8 +422,8 @@ function* __prettyGroupTokens(tags) {
423
422
  state = states.value;
424
423
  }
425
424
 
426
- if (tag.type === OpenNodeTag || tag.type === OpenFragmentTag) {
427
- if (tag.type === OpenFragmentTag) {
425
+ if (tag.type === OpenNodeTag) {
426
+ if (!tag.value.type) {
428
427
  states = states.push({ holding: [], broken: false, open: tag });
429
428
  yield tag;
430
429
  } else {
@@ -436,7 +435,138 @@ function* __prettyGroupTokens(tags) {
436
435
  }
437
436
  }
438
437
 
439
- function* __generatePrettyCSTMLStrategy(tags, options) {
438
+ function* __generatePrettyCSTML(tags, options) {
439
+ let { indent = ' ', inline: inlineOption = true } = options;
440
+
441
+ if (!tags) {
442
+ yield* '<//>';
443
+ return;
444
+ }
445
+
446
+ const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
447
+ let indentLevel = 0;
448
+ let first = true;
449
+ let inline = false;
450
+ let ref = null;
451
+
452
+ for (;;) {
453
+ co.advance();
454
+
455
+ if (co.done) break;
456
+
457
+ if (co.current instanceof Promise) {
458
+ co.current = yield co.current;
459
+ }
460
+
461
+ const tag = co.value;
462
+
463
+ if (tag.type === 'Effect') {
464
+ continue;
465
+ }
466
+
467
+ inline =
468
+ inlineOption &&
469
+ inline &&
470
+ ref &&
471
+ (tag.type === NullTag ||
472
+ tag.type === GapTag ||
473
+ tag.type === ArrayInitializerTag ||
474
+ tag.type === TokenGroup);
475
+
476
+ if (!first && !inline) {
477
+ yield* '\n';
478
+ }
479
+
480
+ if (tag.type === CloseNodeTag) {
481
+ ref = null;
482
+
483
+ indentLevel--;
484
+ }
485
+
486
+ if (!inline) {
487
+ yield* indent.repeat(indentLevel);
488
+ } else {
489
+ yield* ' ';
490
+ }
491
+
492
+ if (tag.type === TokenGroup) {
493
+ ref = null;
494
+ const intrinsicValue = tag.value[0].value.flags.token ? getCooked(tag.value) : null;
495
+ yield* printSelfClosingNodeTag(tag.value[0], intrinsicValue);
496
+ } else {
497
+ yield* printTag(tag);
498
+ }
499
+
500
+ if (tag.type === ReferenceTag) {
501
+ inline = true;
502
+ ref = tag;
503
+ }
504
+
505
+ if (tag.type === OpenNodeTag) {
506
+ indentLevel++;
507
+ }
508
+
509
+ first = false;
510
+ }
511
+
512
+ if (indentLevel !== 0) {
513
+ throw new Error('imbalanced tags');
514
+ }
515
+
516
+ yield* '\n';
517
+ }
518
+
519
+ export const generatePrettyCSTML = (tags, options = {}) => {
520
+ return new StreamIterable(__generatePrettyCSTML(tags, options));
521
+ };
522
+
523
+ function* __writeCSTMLStrategy(tags) {
524
+ if (!tags) {
525
+ yield buildWriteEffect('<//>');
526
+ return;
527
+ }
528
+
529
+ let prevTag = null;
530
+
531
+ const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
532
+
533
+ for (;;) {
534
+ co.advance();
535
+
536
+ if (co.current instanceof Promise) {
537
+ co.current = yield co.current;
538
+ }
539
+ if (co.done) break;
540
+
541
+ const tag = co.value;
542
+
543
+ if (tag.type === ReferenceTag && prevTag.type === NullTag) {
544
+ yield buildWriteEffect(' ');
545
+ }
546
+
547
+ if (tag.type === 'Effect') {
548
+ yield tag;
549
+
550
+ continue;
551
+ }
552
+
553
+ if (tag.type === TokenGroup) {
554
+ const intrinsicValue = getCooked(tag.value);
555
+ yield buildWriteEffect(printSelfClosingNodeTag(tag.value[0], intrinsicValue));
556
+ } else {
557
+ yield buildWriteEffect(printTag(tag));
558
+ }
559
+
560
+ prevTag = tag;
561
+ }
562
+
563
+ yield buildWriteEffect('\n');
564
+ }
565
+
566
+ export const writeCSTMLStrategy = (tags, options = {}) =>
567
+ new StreamIterable(__writeCSTMLStrategy(tags, options));
568
+
569
+ function* __writePrettyCSTMLStrategy(tags, options) {
440
570
  let { indent = ' ', emitEffects = false, inline: inlineOption = true } = options;
441
571
 
442
572
  if (!tags) {
@@ -444,7 +574,7 @@ function* __generatePrettyCSTMLStrategy(tags, options) {
444
574
  return;
445
575
  }
446
576
 
447
- const co = new Coroutine(getStreamIterator(prettyGroupTokens(tags)));
577
+ const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
448
578
  let indentLevel = 0;
449
579
  let first = true;
450
580
  let inline = false;
@@ -464,14 +594,16 @@ function* __generatePrettyCSTMLStrategy(tags, options) {
464
594
  if (tag.type === 'Effect') {
465
595
  const effect = tag.value;
466
596
  if (emitEffects && effect.verb === 'write') {
467
- const writeEffect = getEmbeddedExpression(effect.value);
597
+ const writeEffect = getEmbeddedObject(effect.value);
468
598
  yield buildWriteEffect(
469
599
  (first ? '' : '\n') + writeEffect.text,
470
- getEmbeddedExpression(writeEffect.options),
600
+ getEmbeddedObject(writeEffect.options),
471
601
  );
472
602
 
473
603
  inline = false;
474
604
  first = false;
605
+ } else {
606
+ yield tag;
475
607
  }
476
608
  continue;
477
609
  }
@@ -482,14 +614,14 @@ function* __generatePrettyCSTMLStrategy(tags, options) {
482
614
  ref &&
483
615
  (tag.type === NullTag ||
484
616
  tag.type === GapTag ||
485
- tag.type === ArrayTag ||
617
+ tag.type === ArrayInitializerTag ||
486
618
  tag.type === TokenGroup);
487
619
 
488
620
  if (!first && !inline) {
489
621
  yield buildWriteEffect('\n');
490
622
  }
491
623
 
492
- if (tag.type === CloseNodeTag || tag.type === CloseFragmentTag) {
624
+ if (tag.type === CloseNodeTag) {
493
625
  ref = null;
494
626
  if (indentLevel === 0) {
495
627
  throw new Error('imbalanced tag stack');
@@ -517,7 +649,7 @@ function* __generatePrettyCSTMLStrategy(tags, options) {
517
649
  ref = tag;
518
650
  }
519
651
 
520
- if (tag.type === OpenNodeTag || tag.type === OpenFragmentTag) {
652
+ if (tag.type === OpenNodeTag) {
521
653
  indentLevel++;
522
654
  }
523
655
 
@@ -527,12 +659,12 @@ function* __generatePrettyCSTMLStrategy(tags, options) {
527
659
  yield buildWriteEffect('\n');
528
660
  }
529
661
 
530
- export const generatePrettyCSTMLStrategy = (tags, options = {}) => {
531
- return new StreamIterable(__generatePrettyCSTMLStrategy(tags, options));
662
+ export const writePrettyCSTMLStrategy = (tags, options = {}) => {
663
+ return new StreamIterable(__writePrettyCSTMLStrategy(tags, options));
532
664
  };
533
665
 
534
666
  export const printPrettyCSTML = (tags, options = {}) => {
535
- return stringFromStream(generateStandardOutput(generatePrettyCSTMLStrategy(tags, options)));
667
+ return stringFromStream(generateStandardOutput(generatePrettyCSTML(tags, options)));
536
668
  };
537
669
 
538
670
  export const getCooked = (tags) => {
@@ -541,19 +673,20 @@ export const getCooked = (tags) => {
541
673
  let first = true;
542
674
  let foundLast = false;
543
675
  let depth = 0;
676
+ let ref = null;
544
677
 
545
678
  for (const tag of tags) {
546
679
  if (foundLast) throw new Error();
547
680
 
548
681
  switch (tag.type) {
549
682
  case ReferenceTag: {
683
+ ref = tag;
550
684
  if (depth === 1) {
551
685
  throw new Error('cookable nodes must not contain other nodes');
552
686
  }
553
687
  break;
554
688
  }
555
689
 
556
- case OpenFragmentTag:
557
690
  case OpenNodeTag: {
558
691
  const { flags, attributes } = tag.value;
559
692
 
@@ -567,11 +700,11 @@ export const getCooked = (tags) => {
567
700
  }
568
701
  }
569
702
 
570
- if (!(flags.trivia || (flags.escape && attributes.cooked))) {
703
+ if (!(ref.value.name === '#' || (ref.value.name === '@' && attributes.cooked))) {
571
704
  throw new Error('cookable nodes must not contain other nodes');
572
705
  }
573
706
 
574
- if (flags.escape) {
707
+ if (ref.value.name === '@') {
575
708
  const { cooked: cookedValue } = tag.value.attributes;
576
709
 
577
710
  if (!cookedValue) throw new Error('cannot cook string: it contains uncooked escapes');
@@ -582,7 +715,6 @@ export const getCooked = (tags) => {
582
715
  break;
583
716
  }
584
717
 
585
- case CloseFragmentTag:
586
718
  case CloseNodeTag: {
587
719
  if (depth === 1) {
588
720
  foundLast = true;
@@ -617,6 +749,8 @@ export const printSource = (tags) => {
617
749
  for (const tag of tags) {
618
750
  if (tag.type === LiteralTag) {
619
751
  printed += tag.value;
752
+ } else if (tag.type === GapTag) {
753
+ throw new Error('use generateSourceTextFor');
620
754
  }
621
755
  }
622
756
 
@@ -627,6 +761,8 @@ export function* generateSourceTextFor(tags) {
627
761
  for (const tag of tags) {
628
762
  if (tag.type === LiteralTag) {
629
763
  yield* tag.value;
764
+ } else if (tag.type === GapTag) {
765
+ yield null;
630
766
  }
631
767
  }
632
768
  }
package/lib/symbols.js CHANGED
@@ -1,27 +1,20 @@
1
1
  export const node = Symbol.for('@bablr/node');
2
-
3
- export const gap = Symbol.for('@bablr/gap');
4
-
5
2
  export const fragment = Symbol.for('@bablr/fragment');
6
3
 
7
- export const null_ = Symbol.for('@bablr/null');
8
-
9
4
  export const DoctypeTag = Symbol.for('DoctypeTag');
10
5
  export const OpenNodeTag = Symbol.for('OpenNodeTag');
11
6
  export const CloseNodeTag = Symbol.for('CloseNodeTag');
12
- export const OpenFragmentTag = Symbol.for('OpenFragmentTag');
13
- export const CloseFragmentTag = Symbol.for('CloseFragmentTag');
14
7
  export const ReferenceTag = Symbol.for('ReferenceTag');
15
8
  export const ShiftTag = Symbol.for('ShiftTag');
16
9
  export const GapTag = Symbol.for('GapTag');
17
10
  export const NullTag = Symbol.for('NullTag');
18
- export const ArrayTag = Symbol.for('ArrayTag');
11
+ export const ArrayInitializerTag = Symbol.for('ArrayInitializerTag');
19
12
  export const LiteralTag = Symbol.for('LiteralTag');
20
13
 
21
14
  export const EmbeddedNode = Symbol.for('EmbeddedNode');
15
+ export const EmbeddedMatcher = Symbol.for('EmbeddedMatcher');
16
+ export const EmbeddedRegex = Symbol.for('EmbeddedRegex');
22
17
  export const EmbeddedTag = Symbol.for('EmbeddedTag');
23
- export const EmbeddedExpression = Symbol.for('EmbeddedExpression');
18
+ export const EmbeddedObject = Symbol.for('EmbeddedObject');
24
19
 
25
20
  export const TokenGroup = Symbol.for('TokenGroup');
26
-
27
- export { null_ as null };
package/lib/template.js CHANGED
@@ -1,93 +1,107 @@
1
1
  import * as t from './builders.js';
2
2
  import {
3
- LiteralTag,
4
- EmbeddedNode,
5
3
  ReferenceTag,
6
- ArrayTag,
7
- OpenFragmentTag,
4
+ ArrayInitializerTag,
8
5
  OpenNodeTag,
6
+ CloseNodeTag,
9
7
  DoctypeTag,
10
- CloseFragmentTag,
8
+ EmbeddedNode,
9
+ GapTag,
11
10
  } from './symbols.js';
12
11
  import * as btree from './btree.js';
13
- import { getOpenTag, getRoot } from './tree.js';
12
+ import { getOpenTag, get, isFragmentNode } from './tree.js';
14
13
 
15
- const { isArray } = Array;
16
14
  const { freeze } = Object;
17
- const { isFinite } = Number;
18
-
19
- export const interpolateArray = (fragment) => {
20
- const value = getRoot(fragment);
21
15
 
22
- if (isArray(value)) {
23
- if (isFinite(value[0])) {
24
- return [...btree.traverse(value)];
25
- } else {
26
- return value;
27
- }
28
- } else {
29
- return [value];
30
- }
16
+ export const buildFilledGapFunction = (expressions) => (value) => {
17
+ expressions.push(value);
18
+ return t.buildGapTag();
31
19
  };
32
20
 
33
- export function* interpolateFragmentChildren(value, ref) {
34
- const open = getOpenTag(value);
21
+ export function* interpolateFragment(node, ref, expressions) {
22
+ const open = getOpenTag(node);
23
+
24
+ if (node.type !== null) throw new Error();
25
+
26
+ const gap = buildFilledGapFunction(expressions);
35
27
 
36
- if (open.type === OpenFragmentTag) {
28
+ const counters = new Map();
29
+
30
+ if (!open.value.type) {
37
31
  let currentRef = null;
38
- for (let child of btree.traverse(value.children)) {
39
- if (
40
- child.type === DoctypeTag ||
41
- child.type === OpenFragmentTag ||
42
- child.type === CloseFragmentTag
43
- ) {
44
- continue;
45
- }
32
+ let isFragment = isFragmentNode(node);
33
+ for (let tag of btree.traverse(node.children)) {
34
+ switch (tag.type) {
35
+ case DoctypeTag: {
36
+ break;
37
+ }
38
+ case OpenNodeTag:
39
+ case CloseNodeTag: {
40
+ if (!isFragment) {
41
+ yield tag;
42
+ }
43
+ break;
44
+ }
45
+
46
+ case ReferenceTag: {
47
+ currentRef = tag;
48
+ break;
49
+ }
50
+
51
+ case ArrayInitializerTag: {
52
+ const { name } = currentRef.value;
53
+ counters.set(name, -1);
54
+ if (name === '.') {
55
+ yield freeze({ ...ref });
56
+ } else {
57
+ yield currentRef;
58
+ }
59
+ yield tag;
60
+ break;
61
+ }
62
+
63
+ case GapTag: {
64
+ const { name, isArray, flags } = currentRef.value;
65
+
66
+ if (name === '.') {
67
+ // TODO check/combine flags
68
+ yield freeze({ ...ref });
69
+ } else {
70
+ yield currentRef;
71
+ }
72
+
73
+ const count = counters.get(name) + 1;
46
74
 
47
- if (child.type === ArrayTag) {
48
- // if (notAlreadyInitialized) {
49
- yield child;
50
- // }
51
- } else if (child.type === ReferenceTag) {
52
- currentRef = child;
53
- if (child.value.name === '.') {
54
- yield freeze({ ...ref });
55
- } else {
56
- yield child;
75
+ counters.set(name, count);
76
+
77
+ const resolvedRef = t.buildReferenceTag(name, isArray, flags, count);
78
+
79
+ yield gap(get(resolvedRef, node));
80
+
81
+ break;
82
+ }
83
+
84
+ case EmbeddedNode: {
85
+ const { name } = currentRef.value;
86
+ if (name === '.') {
87
+ yield freeze({ ...ref });
88
+ } else {
89
+ yield currentRef;
90
+ }
91
+ yield gap(tag.value);
92
+ break;
93
+ }
94
+
95
+ default: {
96
+ yield tag;
97
+ break;
57
98
  }
58
- } else {
59
- yield child;
60
99
  }
61
100
  }
62
101
  } else if (open.type === OpenNodeTag) {
63
102
  yield freeze({ ...ref });
103
+ yield gap(get(ref, node));
64
104
  } else {
65
105
  throw new Error();
66
106
  }
67
107
  }
68
-
69
- const validateTag = (tag) => {
70
- if (!tag || (tag.type !== LiteralTag && tag.type !== EmbeddedNode)) {
71
- throw new Error('Invalid tag');
72
- }
73
- if (tag.type === EmbeddedNode && !tag.value.flags.escape) {
74
- throw new Error();
75
- }
76
- };
77
-
78
- export const interpolateString = (value) => {
79
- const tags = [];
80
- if (isArray(value)) {
81
- for (const element of value) {
82
- validateTag(element);
83
-
84
- tags.push(element);
85
- }
86
- } else {
87
- // we can't safely interpolate strings here, though I wish we could
88
- validateTag(value);
89
- tags.push(value);
90
- }
91
-
92
- return t.buildNode('String', 'Content', tags);
93
- };