@bablr/agast-helpers 0.3.2 → 0.5.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,11 +1,27 @@
1
1
  import { Coroutine } from '@bablr/coroutine';
2
2
  import emptyStack from '@iter-tools/imm-stack';
3
- import { printSelfClosingNodeTag, printTerminal } from './print.js';
3
+ import { printSelfClosingNodeTag, printTag } from './print.js';
4
4
  import { buildWriteEffect, buildTokenGroup } from './builders.js';
5
+ import {
6
+ DoctypeTag,
7
+ OpenNodeTag,
8
+ CloseNodeTag,
9
+ ReferenceTag,
10
+ ShiftTag,
11
+ GapTag,
12
+ NullTag,
13
+ ArrayTag,
14
+ LiteralTag,
15
+ EmbeddedExpression,
16
+ TokenGroup,
17
+ OpenFragmentTag,
18
+ CloseFragmentTag,
19
+ } from './symbols.js';
20
+
5
21
  export * from './print.js';
6
22
 
7
23
  const getEmbeddedExpression = (obj) => {
8
- if (obj.type !== 'EmbeddedExpression') throw new Error();
24
+ if (obj.type !== EmbeddedExpression) throw new Error();
9
25
  return obj.value;
10
26
  };
11
27
 
@@ -89,12 +105,6 @@ export class AsyncGenerator {
89
105
  }
90
106
  }
91
107
 
92
- export const isIntrinsicToken = (terminal) => {
93
- return (
94
- terminal.type === 'OpenNodeTag' && terminal.value.flags.intrinsic && terminal.value.flags.token
95
- );
96
- };
97
-
98
108
  export class StreamGenerator {
99
109
  constructor(embeddedGenerator) {
100
110
  this.generator = embeddedGenerator;
@@ -149,8 +159,8 @@ export const maybeWait = (maybePromise, callback) => {
149
159
  }
150
160
  };
151
161
 
152
- function* __generateStandardOutput(terminals) {
153
- const co = new Coroutine(getStreamIterator(terminals));
162
+ function* __isEmpty(tags) {
163
+ const co = new Coroutine(getStreamIterator(tags));
154
164
 
155
165
  for (;;) {
156
166
  co.advance();
@@ -160,10 +170,55 @@ function* __generateStandardOutput(terminals) {
160
170
  }
161
171
  if (co.done) break;
162
172
 
163
- const terminal = co.value;
173
+ let depth = 0;
174
+
175
+ const tag = co.value;
176
+
177
+ switch (tag.type) {
178
+ case OpenFragmentTag:
179
+ case OpenNodeTag:
180
+ if (tag.value.flags.trivia) {
181
+ ++depth;
182
+ }
183
+
184
+ if (depth === 0 && tag.value.flags.escape) {
185
+ return false;
186
+ }
187
+
188
+ break;
189
+
190
+ case CloseFragmentTag:
191
+ case CloseNodeTag:
192
+ --depth;
193
+ break;
194
+
195
+ case LiteralTag:
196
+ case GapTag:
197
+ return false;
198
+ }
199
+ }
200
+
201
+ return true;
202
+ }
164
203
 
165
- if (terminal.type === 'Effect') {
166
- const effect = getEmbeddedExpression(terminal.value);
204
+ export const isEmpty = (tags) =>
205
+ new StreamIterable(__isEmpty(tags))[Symbol.iterator]().next().value;
206
+
207
+ function* __generateStandardOutput(tags) {
208
+ const co = new Coroutine(getStreamIterator(tags));
209
+
210
+ for (;;) {
211
+ co.advance();
212
+
213
+ if (co.current instanceof Promise) {
214
+ co.current = yield co.current;
215
+ }
216
+ if (co.done) break;
217
+
218
+ const tag = co.value;
219
+
220
+ if (tag.type === 'Effect') {
221
+ const effect = tag.value;
167
222
  if (effect.verb === 'write') {
168
223
  const writeEffect = getEmbeddedExpression(effect.value);
169
224
  if (writeEffect.stream == null || writeEffect.stream === 1) {
@@ -174,11 +229,10 @@ function* __generateStandardOutput(terminals) {
174
229
  }
175
230
  }
176
231
 
177
- export const generateStandardOutput = (terminals) =>
178
- new StreamIterable(__generateStandardOutput(terminals));
232
+ export const generateStandardOutput = (tags) => new StreamIterable(__generateStandardOutput(tags));
179
233
 
180
- function* __generateAllOutput(terminals) {
181
- const co = new Coroutine(getStreamIterator(terminals));
234
+ function* __generateAllOutput(tags) {
235
+ const co = new Coroutine(getStreamIterator(tags));
182
236
 
183
237
  let currentStream = null;
184
238
 
@@ -190,10 +244,10 @@ function* __generateAllOutput(terminals) {
190
244
  }
191
245
  if (co.done) break;
192
246
 
193
- const terminal = co.value;
247
+ const tag = co.value;
194
248
 
195
- if (terminal.type === 'Effect') {
196
- const effect = getEmbeddedExpression(terminal.value);
249
+ if (tag.type === 'Effect') {
250
+ const effect = tag.value;
197
251
  if (effect.verb === 'write') {
198
252
  const writeEffect = getEmbeddedExpression(effect.value);
199
253
  const prevStream = currentStream;
@@ -207,10 +261,10 @@ function* __generateAllOutput(terminals) {
207
261
  }
208
262
  }
209
263
 
210
- export const generateAllOutput = (terminals) => new StreamIterable(__generateAllOutput(terminals));
264
+ export const generateAllOutput = (tags) => new StreamIterable(__generateAllOutput(tags));
211
265
 
212
- export const printCSTML = (terminals) => {
213
- return stringFromStream(generateStandardOutput(generateCSTMLStrategy(terminals)));
266
+ export const printCSTML = (tags) => {
267
+ return stringFromStream(generateStandardOutput(generateCSTMLStrategy(tags)));
214
268
  };
215
269
 
216
270
  function* __emptyStreamIterator() {}
@@ -230,9 +284,9 @@ export const asyncStringFromStream = async (stream) => {
230
284
 
231
285
  if (co.done) break;
232
286
 
233
- const terminal = co.value;
287
+ const tag = co.value;
234
288
 
235
- str += printTerminal(terminal);
289
+ str += printTag(tag);
236
290
  }
237
291
 
238
292
  return str;
@@ -255,17 +309,17 @@ export const stringFromStream = (stream) => {
255
309
  return str;
256
310
  };
257
311
 
258
- function* __generateCSTMLStrategy(terminals, options) {
312
+ function* __generateCSTMLStrategy(tags, options) {
259
313
  let { emitEffects = false, inline: inlineOption = true } = options;
260
314
 
261
- if (!terminals) {
315
+ if (!tags) {
262
316
  yield buildWriteEffect('<//>');
263
317
  return;
264
318
  }
265
319
 
266
- let prevTerminal = null;
320
+ let prevTag = null;
267
321
 
268
- const co = new Coroutine(getStreamIterator(prettyGroupTokens(terminals)));
322
+ const co = new Coroutine(getStreamIterator(prettyGroupTokens(tags)));
269
323
 
270
324
  for (;;) {
271
325
  co.advance();
@@ -275,43 +329,47 @@ function* __generateCSTMLStrategy(terminals, options) {
275
329
  }
276
330
  if (co.done) break;
277
331
 
278
- const terminal = co.value;
332
+ const tag = co.value;
279
333
 
280
- if (terminal.type === 'Reference' && prevTerminal.type === 'Null') {
334
+ if (tag.type === ReferenceTag && prevTag.type === NullTag) {
281
335
  yield buildWriteEffect(' ');
282
336
  }
283
337
 
284
- if (terminal.type === 'Effect') {
285
- const effect = terminal.value;
338
+ if (tag.type === 'Effect') {
339
+ const effect = tag.value;
286
340
  if (emitEffects && effect.verb === 'write') {
287
341
  yield buildWriteEffect(effect.value.text, effect.value.options);
288
342
  }
289
343
  continue;
290
344
  }
291
345
 
292
- if (terminal.type === 'TokenGroup') {
293
- const intrinsicValue = getCooked(terminal.value.slice(1, -1));
294
- yield buildWriteEffect(printSelfClosingNodeTag(terminal.value[0], intrinsicValue));
346
+ if (tag.type === TokenGroup) {
347
+ const intrinsicValue = getCooked(tag.value);
348
+ yield buildWriteEffect(printSelfClosingNodeTag(tag.value[0], intrinsicValue));
295
349
  } else {
296
- yield buildWriteEffect(printTerminal(terminal));
350
+ yield buildWriteEffect(printTag(tag));
297
351
  }
298
352
 
299
- prevTerminal = terminal;
353
+ prevTag = tag;
300
354
  }
301
355
 
302
356
  yield buildWriteEffect('\n');
303
357
  }
304
358
 
305
- export const generateCSTMLStrategy = (terminals, options = {}) =>
306
- new StreamIterable(__generateCSTMLStrategy(terminals, options));
359
+ export const generateCSTMLStrategy = (tags, options = {}) =>
360
+ new StreamIterable(__generateCSTMLStrategy(tags, options));
361
+
362
+ const isToken = (tag) => {
363
+ return tag.value.flags.token;
364
+ };
307
365
 
308
- export const prettyGroupTokens = (terminals) => new StreamIterable(__prettyGroupTokens(terminals));
366
+ export const prettyGroupTokens = (tags) => new StreamIterable(__prettyGroupTokens(tags));
309
367
 
310
- function* __prettyGroupTokens(terminals) {
368
+ function* __prettyGroupTokens(tags) {
311
369
  let states = emptyStack.push({ holding: [], broken: false, open: null });
312
370
  let state = states.value;
313
371
 
314
- const co = new Coroutine(getStreamIterator(terminals));
372
+ const co = new Coroutine(getStreamIterator(tags));
315
373
 
316
374
  for (;;) {
317
375
  co.advance();
@@ -322,18 +380,19 @@ function* __prettyGroupTokens(terminals) {
322
380
  co.current = yield co.current;
323
381
  }
324
382
 
325
- const terminal = co.value;
383
+ const tag = co.value;
326
384
  const isOpenClose =
327
- terminal.type === 'CloseNodeTag' || (terminal.type === 'OpenNodeTag' && terminal.value.type);
385
+ tag.type === CloseNodeTag ||
386
+ tag.type === OpenNodeTag ||
387
+ tag.type === CloseFragmentTag ||
388
+ tag.type === OpenFragmentTag;
328
389
 
329
390
  if (
330
- (terminal.type === 'Effect' && terminal.value.verb === 'write') ||
331
- ['Reference', 'DoctypeTag', 'Gap', 'Null'].includes(terminal.type) ||
332
- (terminal.type === 'OpenNodeTag' && !terminal.value.type) ||
333
- (state.open &&
334
- !isIntrinsicToken(state.open) &&
335
- (terminal.type === 'Literal' ||
336
- (terminal.type === 'OpenNodeTag' && terminal.value.flags.escape)))
391
+ (tag.type === 'Effect' && tag.value.verb === 'write') ||
392
+ [ReferenceTag, DoctypeTag, GapTag, NullTag, ArrayTag, ShiftTag, OpenFragmentTag].includes(
393
+ tag.type,
394
+ ) ||
395
+ (tag.type === OpenNodeTag && tag.value.flags.escape)
337
396
  ) {
338
397
  state.broken = true;
339
398
 
@@ -341,52 +400,55 @@ function* __prettyGroupTokens(terminals) {
341
400
  yield* state.holding;
342
401
  state.holding = [];
343
402
  }
344
-
345
- if (!isOpenClose && terminal.type !== 'Effect') {
346
- yield terminal;
347
- }
348
- } else if (!isOpenClose && terminal.type !== 'Effect') {
349
- state.holding.push(terminal);
403
+ } else if (tag.type === LiteralTag) {
404
+ state.holding.push(tag);
350
405
  }
351
406
 
352
- if (terminal.type === 'Effect') {
353
- yield terminal;
407
+ if (!state.holding.length && !isOpenClose) {
408
+ yield tag;
354
409
  }
355
410
 
356
- if (terminal.type === 'CloseNodeTag') {
357
- if (!state.broken && (isIntrinsicToken(state.open) || state.holding.length === 1)) {
358
- state.holding.push(terminal);
411
+ if (tag.type === CloseNodeTag || tag.type === CloseFragmentTag) {
412
+ if (!state.broken && (isToken(state.open) || state.holding.length === 1)) {
413
+ state.holding.push(tag);
359
414
  yield buildTokenGroup(state.holding);
360
415
  } else {
361
416
  if (state.holding.length) {
362
417
  yield* state.holding;
363
418
  }
364
- yield terminal;
419
+ yield tag;
365
420
  }
366
421
 
367
422
  states = states.pop();
368
423
  state = states.value;
369
424
  }
370
425
 
371
- if (terminal.type === 'OpenNodeTag' && terminal.value.type) {
372
- states = states.push({ holding: [terminal], broken: false, open: terminal });
426
+ if (tag.type === OpenNodeTag || tag.type === OpenFragmentTag) {
427
+ if (tag.type === OpenFragmentTag) {
428
+ states = states.push({ holding: [], broken: false, open: tag });
429
+ yield tag;
430
+ } else {
431
+ states = states.push({ holding: [tag], broken: false, open: tag });
432
+ }
433
+
373
434
  state = states.value;
374
435
  }
375
436
  }
376
437
  }
377
438
 
378
- function* __generatePrettyCSTMLStrategy(terminals, options) {
439
+ function* __generatePrettyCSTMLStrategy(tags, options) {
379
440
  let { indent = ' ', emitEffects = false, inline: inlineOption = true } = options;
380
441
 
381
- if (!terminals) {
442
+ if (!tags) {
382
443
  yield buildWriteEffect('<//>');
383
444
  return;
384
445
  }
385
446
 
386
- const co = new Coroutine(getStreamIterator(prettyGroupTokens(terminals)));
447
+ const co = new Coroutine(getStreamIterator(prettyGroupTokens(tags)));
387
448
  let indentLevel = 0;
388
449
  let first = true;
389
450
  let inline = false;
451
+ let ref = null;
390
452
 
391
453
  for (;;) {
392
454
  co.advance();
@@ -397,10 +459,10 @@ function* __generatePrettyCSTMLStrategy(terminals, options) {
397
459
  co.current = yield co.current;
398
460
  }
399
461
 
400
- const terminal = co.value;
462
+ const tag = co.value;
401
463
 
402
- if (terminal.type === 'Effect') {
403
- const effect = getEmbeddedExpression(terminal.value);
464
+ if (tag.type === 'Effect') {
465
+ const effect = tag.value;
404
466
  if (emitEffects && effect.verb === 'write') {
405
467
  const writeEffect = getEmbeddedExpression(effect.value);
406
468
  yield buildWriteEffect(
@@ -417,13 +479,18 @@ function* __generatePrettyCSTMLStrategy(terminals, options) {
417
479
  inline =
418
480
  inlineOption &&
419
481
  inline &&
420
- (terminal.type === 'Null' || terminal.type === 'Gap' || terminal.type === 'TokenGroup');
482
+ ref &&
483
+ (tag.type === NullTag ||
484
+ tag.type === GapTag ||
485
+ tag.type === ArrayTag ||
486
+ tag.type === TokenGroup);
421
487
 
422
488
  if (!first && !inline) {
423
489
  yield buildWriteEffect('\n');
424
490
  }
425
491
 
426
- if (terminal.type === 'CloseNodeTag') {
492
+ if (tag.type === CloseNodeTag || tag.type === CloseFragmentTag) {
493
+ ref = null;
427
494
  if (indentLevel === 0) {
428
495
  throw new Error('imbalanced tag stack');
429
496
  }
@@ -437,18 +504,20 @@ function* __generatePrettyCSTMLStrategy(terminals, options) {
437
504
  yield buildWriteEffect(' ');
438
505
  }
439
506
 
440
- if (terminal.type === 'TokenGroup') {
441
- const intrinsicValue = getCooked(terminal.value.slice(1, -1));
442
- yield buildWriteEffect(printSelfClosingNodeTag(terminal.value[0], intrinsicValue));
507
+ if (tag.type === TokenGroup) {
508
+ ref = null;
509
+ const intrinsicValue = tag.value[0].value.flags.token ? getCooked(tag.value) : null;
510
+ yield buildWriteEffect(printSelfClosingNodeTag(tag.value[0], intrinsicValue));
443
511
  } else {
444
- yield buildWriteEffect(printTerminal(terminal));
512
+ yield buildWriteEffect(printTag(tag));
445
513
  }
446
514
 
447
- if (terminal.type === 'Reference') {
515
+ if (tag.type === ReferenceTag) {
448
516
  inline = true;
517
+ ref = tag;
449
518
  }
450
519
 
451
- if (terminal.type === 'OpenNodeTag') {
520
+ if (tag.type === OpenNodeTag || tag.type === OpenFragmentTag) {
452
521
  indentLevel++;
453
522
  }
454
523
 
@@ -458,32 +527,52 @@ function* __generatePrettyCSTMLStrategy(terminals, options) {
458
527
  yield buildWriteEffect('\n');
459
528
  }
460
529
 
461
- export const generatePrettyCSTMLStrategy = (terminals, options = {}) => {
462
- return new StreamIterable(__generatePrettyCSTMLStrategy(terminals, options));
530
+ export const generatePrettyCSTMLStrategy = (tags, options = {}) => {
531
+ return new StreamIterable(__generatePrettyCSTMLStrategy(tags, options));
463
532
  };
464
533
 
465
- export const printPrettyCSTML = (terminals, options = {}) => {
466
- return stringFromStream(generateStandardOutput(generatePrettyCSTMLStrategy(terminals, options)));
534
+ export const printPrettyCSTML = (tags, options = {}) => {
535
+ return stringFromStream(generateStandardOutput(generatePrettyCSTMLStrategy(tags, options)));
467
536
  };
468
537
 
469
- export const getCooked = (terminals) => {
538
+ export const getCooked = (tags) => {
470
539
  let cooked = '';
471
540
 
472
- for (const terminal of terminals) {
473
- switch (terminal.type) {
474
- case 'Reference': {
475
- throw new Error('cookable nodes must not contain other nodes');
541
+ let first = true;
542
+ let foundLast = false;
543
+ let depth = 0;
544
+
545
+ for (const tag of tags) {
546
+ if (foundLast) throw new Error();
547
+
548
+ switch (tag.type) {
549
+ case ReferenceTag: {
550
+ if (depth === 1) {
551
+ throw new Error('cookable nodes must not contain other nodes');
552
+ }
553
+ break;
476
554
  }
477
555
 
478
- case 'OpenNodeTag': {
479
- const { flags, attributes } = terminal.value;
556
+ case OpenFragmentTag:
557
+ case OpenNodeTag: {
558
+ const { flags, attributes } = tag.value;
559
+
560
+ depth++;
561
+
562
+ if (first) {
563
+ if (flags.token) {
564
+ break;
565
+ } else {
566
+ throw new Error(JSON.stringify(flags));
567
+ }
568
+ }
480
569
 
481
570
  if (!(flags.trivia || (flags.escape && attributes.cooked))) {
482
571
  throw new Error('cookable nodes must not contain other nodes');
483
572
  }
484
573
 
485
574
  if (flags.escape) {
486
- const { cooked: cookedValue } = terminal.value.attributes;
575
+ const { cooked: cookedValue } = tag.value.attributes;
487
576
 
488
577
  if (!cookedValue) throw new Error('cannot cook string: it contains uncooked escapes');
489
578
 
@@ -493,8 +582,19 @@ export const getCooked = (terminals) => {
493
582
  break;
494
583
  }
495
584
 
496
- case 'Literal': {
497
- cooked += terminal.value;
585
+ case CloseFragmentTag:
586
+ case CloseNodeTag: {
587
+ if (depth === 1) {
588
+ foundLast = true;
589
+ }
590
+ depth--;
591
+ break;
592
+ }
593
+
594
+ case LiteralTag: {
595
+ if (depth === 1) {
596
+ cooked += tag.value;
597
+ }
498
598
  break;
499
599
  }
500
600
 
@@ -502,29 +602,31 @@ export const getCooked = (terminals) => {
502
602
  throw new Error();
503
603
  }
504
604
  }
605
+
606
+ first = false;
505
607
  }
506
608
 
507
609
  return cooked;
508
610
  };
509
611
 
510
- export const printSource = (terminals) => {
612
+ export const printSource = (tags) => {
511
613
  let printed = '';
512
614
 
513
- if (!terminals) return printed;
615
+ if (!tags) return printed;
514
616
 
515
- for (const terminal of terminals) {
516
- if (terminal.type === 'Literal') {
517
- printed += terminal.value;
617
+ for (const tag of tags) {
618
+ if (tag.type === LiteralTag) {
619
+ printed += tag.value;
518
620
  }
519
621
  }
520
622
 
521
623
  return printed;
522
624
  };
523
625
 
524
- export function* generateSourceTextFor(terminals) {
525
- for (const terminal of terminals) {
526
- if (terminal.type === 'Literal') {
527
- yield* terminal.value;
626
+ export function* generateSourceTextFor(tags) {
627
+ for (const tag of tags) {
628
+ if (tag.type === LiteralTag) {
629
+ yield* tag.value;
528
630
  }
529
631
  }
530
632
  }
package/lib/symbols.js CHANGED
@@ -6,4 +6,22 @@ export const fragment = Symbol.for('@bablr/fragment');
6
6
 
7
7
  export const null_ = Symbol.for('@bablr/null');
8
8
 
9
+ export const DoctypeTag = Symbol.for('DoctypeTag');
10
+ export const OpenNodeTag = Symbol.for('OpenNodeTag');
11
+ export const CloseNodeTag = Symbol.for('CloseNodeTag');
12
+ export const OpenFragmentTag = Symbol.for('OpenFragmentTag');
13
+ export const CloseFragmentTag = Symbol.for('CloseFragmentTag');
14
+ export const ReferenceTag = Symbol.for('ReferenceTag');
15
+ export const ShiftTag = Symbol.for('ShiftTag');
16
+ export const GapTag = Symbol.for('GapTag');
17
+ export const NullTag = Symbol.for('NullTag');
18
+ export const ArrayTag = Symbol.for('ArrayTag');
19
+ export const LiteralTag = Symbol.for('LiteralTag');
20
+
21
+ export const EmbeddedNode = Symbol.for('EmbeddedNode');
22
+ export const EmbeddedTag = Symbol.for('EmbeddedTag');
23
+ export const EmbeddedExpression = Symbol.for('EmbeddedExpression');
24
+
25
+ export const TokenGroup = Symbol.for('TokenGroup');
26
+
9
27
  export { null_ as null };
package/lib/template.js CHANGED
@@ -1,54 +1,93 @@
1
1
  import * as t from './builders.js';
2
+ import {
3
+ LiteralTag,
4
+ EmbeddedNode,
5
+ ReferenceTag,
6
+ ArrayTag,
7
+ OpenFragmentTag,
8
+ OpenNodeTag,
9
+ DoctypeTag,
10
+ CloseFragmentTag,
11
+ } from './symbols.js';
12
+ import * as btree from './btree.js';
13
+ import { getOpenTag, getRoot } from './tree.js';
2
14
 
3
15
  const { isArray } = Array;
4
16
  const { freeze } = Object;
17
+ const { isFinite } = Number;
18
+
19
+ export const interpolateArray = (fragment) => {
20
+ const value = getRoot(fragment);
5
21
 
6
- export const interpolateArray = (value) => {
7
22
  if (isArray(value)) {
8
- return value;
23
+ if (isFinite(value[0])) {
24
+ return [...btree.traverse(value)];
25
+ } else {
26
+ return value;
27
+ }
9
28
  } else {
10
29
  return [value];
11
30
  }
12
31
  };
13
32
 
14
- export const interpolateArrayChildren = (value, ref, sep) => {
15
- if (isArray(value)) {
16
- const values = value;
17
- const children = [];
18
- let first = true;
19
- for (const _ of values) {
20
- if (!first) children.push(freeze({ ...sep }));
21
- children.push(freeze({ ...ref }));
22
- first = false;
33
+ export function* interpolateFragmentChildren(value, ref) {
34
+ const open = getOpenTag(value);
35
+
36
+ if (open.type === OpenFragmentTag) {
37
+ 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
+ }
46
+
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;
57
+ }
58
+ } else {
59
+ yield child;
60
+ }
23
61
  }
24
- return children;
62
+ } else if (open.type === OpenNodeTag) {
63
+ yield freeze({ ...ref });
25
64
  } else {
26
- return [freeze({ ...ref })];
65
+ throw new Error();
27
66
  }
28
- };
67
+ }
29
68
 
30
- const validateTerminal = (term) => {
31
- if (!term || (term.type !== 'Literal' && term.type !== 'EmbeddedNode')) {
32
- throw new Error('Invalid terminal');
69
+ const validateTag = (tag) => {
70
+ if (!tag || (tag.type !== LiteralTag && tag.type !== EmbeddedNode)) {
71
+ throw new Error('Invalid tag');
33
72
  }
34
- if (term.type === 'EmbeddedNode' && !term.value.flags.escape) {
73
+ if (tag.type === EmbeddedNode && !tag.value.flags.escape) {
35
74
  throw new Error();
36
75
  }
37
76
  };
38
77
 
39
78
  export const interpolateString = (value) => {
40
- const terminals = [];
79
+ const tags = [];
41
80
  if (isArray(value)) {
42
81
  for (const element of value) {
43
- validateTerminal(element);
82
+ validateTag(element);
44
83
 
45
- terminals.push(element);
84
+ tags.push(element);
46
85
  }
47
86
  } else {
48
87
  // we can't safely interpolate strings here, though I wish we could
49
- validateTerminal(value);
50
- terminals.push(value);
88
+ validateTag(value);
89
+ tags.push(value);
51
90
  }
52
91
 
53
- return t.buildNode('String', 'Content', terminals);
92
+ return t.buildNode('String', 'Content', tags);
54
93
  };