@bablr/agast-helpers 0.5.3 → 0.6.1

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(generateCSTML(tags));
270
+ return stringFromStream(generateStandardOutput(generateCSTML(tags)));
268
271
  };
269
272
 
270
273
  function* __emptyStreamIterator() {}
@@ -310,16 +313,14 @@ export const stringFromStream = (stream) => {
310
313
  };
311
314
 
312
315
  function* __generateCSTML(tags, options) {
313
- let { emitEffects = false, inline: inlineOption = true } = options;
314
-
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(prettyGroupTokens(tags)));
323
+ const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
323
324
 
324
325
  for (;;) {
325
326
  co.advance();
@@ -332,28 +333,24 @@ function* __generateCSTML(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 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 printSelfClosingNodeTag(tag.value[0], intrinsicValue);
345
+ yield* printSelfClosingNodeTag(tag.value[0], intrinsicValue);
349
346
  } else {
350
- yield printTag(tag);
347
+ yield* printTag(tag);
351
348
  }
352
349
 
353
350
  prevTag = tag;
354
351
  }
355
352
 
356
- yield '\n';
353
+ yield* '\n';
357
354
  }
358
355
 
359
356
  export const generateCSTML = (tags, options = {}) =>
@@ -363,14 +360,16 @@ 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 {
@@ -437,14 +436,145 @@ function* __prettyGroupTokens(tags) {
437
436
  }
438
437
 
439
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) {
443
- yield '<//>';
573
+ yield buildWriteEffect('<//>');
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,11 +594,16 @@ function* __generatePrettyCSTML(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);
468
- yield (first ? '' : '\n') + writeEffect.text, getEmbeddedExpression(writeEffect.options);
597
+ const writeEffect = getEmbeddedObject(effect.value);
598
+ yield buildWriteEffect(
599
+ (first ? '' : '\n') + writeEffect.text,
600
+ getEmbeddedObject(writeEffect.options),
601
+ );
469
602
 
470
603
  inline = false;
471
604
  first = false;
605
+ } else {
606
+ yield tag;
472
607
  }
473
608
  continue;
474
609
  }
@@ -479,14 +614,14 @@ function* __generatePrettyCSTML(tags, options) {
479
614
  ref &&
480
615
  (tag.type === NullTag ||
481
616
  tag.type === GapTag ||
482
- tag.type === ArrayTag ||
617
+ tag.type === ArrayInitializerTag ||
483
618
  tag.type === TokenGroup);
484
619
 
485
620
  if (!first && !inline) {
486
- yield '\n';
621
+ yield buildWriteEffect('\n');
487
622
  }
488
623
 
489
- if (tag.type === CloseNodeTag || tag.type === CloseFragmentTag) {
624
+ if (tag.type === CloseNodeTag) {
490
625
  ref = null;
491
626
  if (indentLevel === 0) {
492
627
  throw new Error('imbalanced tag stack');
@@ -496,17 +631,17 @@ function* __generatePrettyCSTML(tags, options) {
496
631
  }
497
632
 
498
633
  if (!inline) {
499
- yield indent.repeat(indentLevel);
634
+ yield buildWriteEffect(indent.repeat(indentLevel));
500
635
  } else {
501
- yield ' ';
636
+ yield buildWriteEffect(' ');
502
637
  }
503
638
 
504
639
  if (tag.type === TokenGroup) {
505
640
  ref = null;
506
641
  const intrinsicValue = tag.value[0].value.flags.token ? getCooked(tag.value) : null;
507
- yield printSelfClosingNodeTag(tag.value[0], intrinsicValue);
642
+ yield buildWriteEffect(printSelfClosingNodeTag(tag.value[0], intrinsicValue));
508
643
  } else {
509
- yield printTag(tag);
644
+ yield buildWriteEffect(printTag(tag));
510
645
  }
511
646
 
512
647
  if (tag.type === ReferenceTag) {
@@ -514,22 +649,22 @@ function* __generatePrettyCSTML(tags, options) {
514
649
  ref = tag;
515
650
  }
516
651
 
517
- if (tag.type === OpenNodeTag || tag.type === OpenFragmentTag) {
652
+ if (tag.type === OpenNodeTag) {
518
653
  indentLevel++;
519
654
  }
520
655
 
521
656
  first = false;
522
657
  }
523
658
 
524
- yield '\n';
659
+ yield buildWriteEffect('\n');
525
660
  }
526
661
 
527
- export const generatePrettyCSTML = (tags, options = {}) => {
528
- return new StreamIterable(__generatePrettyCSTML(tags, options));
662
+ export const writePrettyCSTMLStrategy = (tags, options = {}) => {
663
+ return new StreamIterable(__writePrettyCSTMLStrategy(tags, options));
529
664
  };
530
665
 
531
666
  export const printPrettyCSTML = (tags, options = {}) => {
532
- return stringFromStream(generatePrettyCSTML(tags, options));
667
+ return stringFromStream(generateStandardOutput(generatePrettyCSTML(tags, options)));
533
668
  };
534
669
 
535
670
  export const getCooked = (tags) => {
@@ -538,19 +673,20 @@ export const getCooked = (tags) => {
538
673
  let first = true;
539
674
  let foundLast = false;
540
675
  let depth = 0;
676
+ let ref = null;
541
677
 
542
678
  for (const tag of tags) {
543
679
  if (foundLast) throw new Error();
544
680
 
545
681
  switch (tag.type) {
546
682
  case ReferenceTag: {
683
+ ref = tag;
547
684
  if (depth === 1) {
548
685
  throw new Error('cookable nodes must not contain other nodes');
549
686
  }
550
687
  break;
551
688
  }
552
689
 
553
- case OpenFragmentTag:
554
690
  case OpenNodeTag: {
555
691
  const { flags, attributes } = tag.value;
556
692
 
@@ -564,11 +700,11 @@ export const getCooked = (tags) => {
564
700
  }
565
701
  }
566
702
 
567
- if (!(flags.trivia || (flags.escape && attributes.cooked))) {
703
+ if (!(ref.value.name === '#' || (ref.value.name === '@' && attributes.cooked))) {
568
704
  throw new Error('cookable nodes must not contain other nodes');
569
705
  }
570
706
 
571
- if (flags.escape) {
707
+ if (ref.value.name === '@') {
572
708
  const { cooked: cookedValue } = tag.value.attributes;
573
709
 
574
710
  if (!cookedValue) throw new Error('cannot cook string: it contains uncooked escapes');
@@ -579,7 +715,6 @@ export const getCooked = (tags) => {
579
715
  break;
580
716
  }
581
717
 
582
- case CloseFragmentTag:
583
718
  case CloseNodeTag: {
584
719
  if (depth === 1) {
585
720
  foundLast = true;
@@ -614,6 +749,8 @@ export const printSource = (tags) => {
614
749
  for (const tag of tags) {
615
750
  if (tag.type === LiteralTag) {
616
751
  printed += tag.value;
752
+ } else if (tag.type === GapTag) {
753
+ throw new Error('use generateSourceTextFor');
617
754
  }
618
755
  }
619
756
 
@@ -624,6 +761,8 @@ export function* generateSourceTextFor(tags) {
624
761
  for (const tag of tags) {
625
762
  if (tag.type === LiteralTag) {
626
763
  yield* tag.value;
764
+ } else if (tag.type === GapTag) {
765
+ yield null;
627
766
  }
628
767
  }
629
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 };