@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/builders.js +69 -215
- package/lib/path.js +921 -14
- package/lib/print.js +91 -70
- package/lib/shorthand.js +11 -31
- package/lib/stream.js +195 -59
- package/lib/symbols.js +4 -11
- package/lib/template.js +83 -69
- package/lib/tree.js +390 -297
- package/package.json +9 -2
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 {
|
|
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
|
-
|
|
13
|
+
ArrayInitializerTag,
|
|
14
14
|
LiteralTag,
|
|
15
|
-
|
|
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
|
|
24
|
-
if (obj.type !==
|
|
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
|
|
177
|
+
case ReferenceTag:
|
|
178
|
+
ref = tag;
|
|
179
|
+
break;
|
|
180
|
+
|
|
179
181
|
case OpenNodeTag:
|
|
180
|
-
|
|
181
|
-
++depth;
|
|
182
|
-
}
|
|
182
|
+
++depth;
|
|
183
183
|
|
|
184
|
-
if (depth === 0 &&
|
|
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 =
|
|
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 =
|
|
251
|
+
const writeEffect = getEmbeddedObject(effect.value);
|
|
253
252
|
const prevStream = currentStream;
|
|
254
|
-
currentStream =
|
|
255
|
-
if (
|
|
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(
|
|
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*
|
|
313
|
-
let { emitEffects = false, inline: inlineOption = true } = options;
|
|
314
|
-
|
|
315
|
+
function* __generateCSTML(tags, options) {
|
|
315
316
|
if (!tags) {
|
|
316
|
-
yield
|
|
317
|
+
yield* '<//>';
|
|
317
318
|
return;
|
|
318
319
|
}
|
|
319
320
|
|
|
320
321
|
let prevTag = null;
|
|
321
322
|
|
|
322
|
-
const co = new Coroutine(getStreamIterator(
|
|
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
|
|
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
|
|
345
|
+
yield* printSelfClosingNodeTag(tag.value[0], intrinsicValue);
|
|
349
346
|
} else {
|
|
350
|
-
yield
|
|
347
|
+
yield* printTag(tag);
|
|
351
348
|
}
|
|
352
349
|
|
|
353
350
|
prevTag = tag;
|
|
354
351
|
}
|
|
355
352
|
|
|
356
|
-
yield
|
|
353
|
+
yield* '\n';
|
|
357
354
|
}
|
|
358
355
|
|
|
359
|
-
export const
|
|
360
|
-
new StreamIterable(
|
|
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
|
|
363
|
+
export const prettyGroupTags = (tags) => new StreamIterable(__prettyGroupTags(tags));
|
|
367
364
|
|
|
368
|
-
function*
|
|
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
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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,
|
|
391
|
+
[ReferenceTag, DoctypeTag, GapTag, NullTag, ArrayInitializerTag, ShiftTag].includes(
|
|
393
392
|
tag.type,
|
|
394
393
|
) ||
|
|
395
|
-
(tag.type === OpenNodeTag && tag.value.
|
|
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
|
|
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
|
|
427
|
-
if (tag.type
|
|
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*
|
|
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(
|
|
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 =
|
|
597
|
+
const writeEffect = getEmbeddedObject(effect.value);
|
|
468
598
|
yield buildWriteEffect(
|
|
469
599
|
(first ? '' : '\n') + writeEffect.text,
|
|
470
|
-
|
|
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 ===
|
|
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
|
|
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
|
|
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
|
|
531
|
-
return new StreamIterable(
|
|
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(
|
|
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 (!(
|
|
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 (
|
|
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
|
|
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
|
|
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
|
-
|
|
7
|
-
OpenFragmentTag,
|
|
4
|
+
ArrayInitializerTag,
|
|
8
5
|
OpenNodeTag,
|
|
6
|
+
CloseNodeTag,
|
|
9
7
|
DoctypeTag,
|
|
10
|
-
|
|
8
|
+
EmbeddedNode,
|
|
9
|
+
GapTag,
|
|
11
10
|
} from './symbols.js';
|
|
12
11
|
import * as btree from './btree.js';
|
|
13
|
-
import { getOpenTag,
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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*
|
|
34
|
-
const open = getOpenTag(
|
|
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
|
-
|
|
28
|
+
const counters = new Map();
|
|
29
|
+
|
|
30
|
+
if (!open.value.type) {
|
|
37
31
|
let currentRef = null;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
};
|