@bablr/agast-helpers 0.9.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/stream.js CHANGED
@@ -1,23 +1,32 @@
1
1
  import { Coroutine } from '@bablr/coroutine';
2
2
  import emptyStack from '@iter-tools/imm-stack';
3
- import { getStreamIterator, StreamIterable, StreamGenerator } from '@bablr/stream-iterator';
3
+ import { getStreamIterator, StreamIterable, StreamGenerator, wait } from '@bablr/stream-iterator';
4
+ import * as Tags from './tags.js';
4
5
  import { printTag } from './print.js';
5
- import { buildOpenNodeTag } from './builders.js';
6
+ import {
7
+ buildChild,
8
+ buildGapTag,
9
+ buildNullTag,
10
+ buildOpenNodeTag,
11
+ buildProperty,
12
+ buildReference,
13
+ } from './builders.js';
6
14
  import {
7
15
  OpenNodeTag,
8
16
  CloseNodeTag,
9
17
  ReferenceTag,
10
18
  GapTag,
11
19
  NullTag,
12
- InitializerTag,
13
20
  LiteralTag,
14
21
  BindingTag,
15
22
  DoctypeTag,
16
23
  ShiftTag,
17
24
  AttributeDefinition,
18
25
  } from './symbols.js';
26
+ import { buildNode, isGapNode, Path } from './path.js';
27
+ import { streamFromTree } from './tree.js';
19
28
 
20
- export { getStreamIterator, StreamIterable, StreamGenerator };
29
+ export { getStreamIterator, StreamIterable, StreamGenerator, wait };
21
30
 
22
31
  export * from './print.js';
23
32
 
@@ -29,6 +38,120 @@ export const maybeWait = (maybePromise, callback) => {
29
38
  }
30
39
  };
31
40
 
41
+ function* __flatMap(fn, tags) {
42
+ const co = new Coroutine(getStreamIterator(tags));
43
+
44
+ for (;;) {
45
+ co.advance();
46
+
47
+ if (co.current instanceof Promise) {
48
+ co.current = yield wait(co.current);
49
+ }
50
+ if (co.done) break;
51
+
52
+ let { value } = co;
53
+
54
+ yield* fn(value);
55
+ }
56
+
57
+ return true;
58
+ }
59
+
60
+ export const flatMap = (fn, tags) => new StreamIterable(__flatMap(fn, tags));
61
+
62
+ export const evaluateReturnSync = (iterable) => {
63
+ const co = new Coroutine(iterable[Symbol.iterator]());
64
+ while (!co.done) co.advance();
65
+ return co.value;
66
+ };
67
+
68
+ export const evaluateReturnAsync = async (iterable) => {
69
+ const co = new Coroutine(getStreamIterator(iterable));
70
+ while (!co.done) {
71
+ co.advance();
72
+
73
+ if (co.current instanceof Promise) {
74
+ co.current = await co.current;
75
+ }
76
+ }
77
+ return co.value;
78
+ };
79
+
80
+ function* __treeFromStream(tags, options) {
81
+ let path = null;
82
+ const co = new Coroutine(getStreamIterator(tags));
83
+ const expressionsCo = new Coroutine(getStreamIterator(options.expressions || []));
84
+
85
+ for (;;) {
86
+ co.advance();
87
+
88
+ if (co.current instanceof Promise) {
89
+ co.current = yield wait(co.current);
90
+ }
91
+
92
+ if (co.done) break;
93
+
94
+ let tag = co.value;
95
+
96
+ if (tag.type === 'Effect' || tag.type === DoctypeTag) {
97
+ continue;
98
+ }
99
+
100
+ if (!path) {
101
+ path = Path.from(buildNode(Tags.fromValues([tag])));
102
+
103
+ // if (tag.value.selfClosing) {
104
+ // path = null;
105
+ // }
106
+
107
+ continue;
108
+ }
109
+
110
+ if (tag.type === GapTag && !path.held) {
111
+ if (!expressionsCo.current || !expressionsCo.done) {
112
+ expressionsCo.advance();
113
+ }
114
+
115
+ if (path.node.value.flags.token) {
116
+ if (expressionsCo.done || expressionsCo.value != null) {
117
+ let node = expressionsCo.value;
118
+ if (isGapNode(node)) {
119
+ throw new Error('not implemented');
120
+ } else {
121
+ path = path.advance(tag);
122
+ }
123
+ }
124
+ } else {
125
+ let node = expressionsCo.done
126
+ ? buildNode(buildGapTag())
127
+ : path == null || expressionsCo.value == null
128
+ ? buildNode(buildNullTag())
129
+ : expressionsCo.value;
130
+
131
+ path = path.advance(node);
132
+ }
133
+ } else {
134
+ path = path.advance(tag);
135
+ }
136
+ }
137
+
138
+ if (!path.done) {
139
+ throw new Error('imbalanced tag stack');
140
+ }
141
+
142
+ return path?.node;
143
+ }
144
+
145
+ export const treeFromStream = (tags, options = {}) => __treeFromStream(tags, options);
146
+
147
+ export const treeFromStreamSync = (tokens, options = {}) => {
148
+ return evaluateReturnSync(treeFromStream(tokens, options));
149
+ };
150
+
151
+ export const treeFromStreamAsync = async (tokens, options = {}) => {
152
+ return evaluateReturnAsync(treeFromStream(tokens, options));
153
+ };
154
+
32
155
  function* __isEmpty(tags) {
33
156
  const co = new Coroutine(getStreamIterator(tags));
34
157
 
@@ -36,7 +159,7 @@ function* __isEmpty(tags) {
36
159
  co.advance();
37
160
 
38
161
  if (co.current instanceof Promise) {
39
- co.current = yield co.current;
162
+ co.current = yield wait(co.current);
40
163
  }
41
164
  if (co.done) break;
42
165
 
@@ -55,7 +178,7 @@ function* __isEmpty(tags) {
55
178
 
56
179
  if (tag.value.literalValue) return false;
57
180
 
58
- if (depth === 0 && ref.value.type === '@') {
181
+ if (depth === 0 && ref.type === '@') {
59
182
  return false;
60
183
  }
61
184
 
@@ -137,7 +260,7 @@ function* __generateCSTML(tags, options) {
137
260
  co.advance();
138
261
 
139
262
  if (co.current instanceof Promise) {
140
- co.current = yield co.current;
263
+ co.current = yield wait(co.current);
141
264
  }
142
265
  if (co.done) break;
143
266
 
@@ -155,8 +278,6 @@ function* __generateCSTML(tags, options) {
155
278
 
156
279
  prevTag = tag;
157
280
  }
158
-
159
- yield* '\n';
160
281
  }
161
282
 
162
283
  export const generateCSTML = (tags, options = {}) =>
@@ -174,24 +295,18 @@ function* __prettyGroupTags(tags) {
174
295
 
175
296
  const co = new Coroutine(getStreamIterator(tags));
176
297
 
177
- let ref = null;
178
-
179
298
  for (;;) {
180
299
  co.advance();
181
300
 
182
- if (co.done) break;
183
-
184
301
  if (co.current instanceof Promise) {
185
- co.current = yield co.current;
302
+ co.current = yield wait(co.current);
186
303
  }
187
304
 
305
+ if (co.done) break;
306
+
188
307
  const tag = co.value;
189
308
  const isOpenClose = tag.type === CloseNodeTag || tag.type === OpenNodeTag;
190
309
 
191
- if (tag.type === ReferenceTag) {
192
- ref = tag;
193
- }
194
-
195
310
  if (
196
311
  (tag.type === 'Effect' && tag.value.verb === 'write') ||
197
312
  [
@@ -200,11 +315,10 @@ function* __prettyGroupTags(tags) {
200
315
  BindingTag,
201
316
  GapTag,
202
317
  NullTag,
203
- InitializerTag,
204
318
  ShiftTag,
205
319
  AttributeDefinition,
206
- ].includes(tag.type) ||
207
- (tag.type === OpenNodeTag && (!tag.value.type || ref?.value.type === '@'))
320
+ OpenNodeTag,
321
+ ].includes(tag.type)
208
322
  ) {
209
323
  state.broken = true;
210
324
 
@@ -222,14 +336,14 @@ function* __prettyGroupTags(tags) {
222
336
 
223
337
  if (tag.type === CloseNodeTag) {
224
338
  if (!state.broken && (isToken(state.open) || state.holding.length === 1)) {
225
- let { flags, type, attributes } = state.holding[0].value;
339
+ let { flags, name, attributes } = state.holding[0].value;
226
340
 
227
341
  let literal = state.holding
228
342
  .slice(1)
229
343
  .map((lit) => lit.value)
230
344
  .join('');
231
345
 
232
- yield buildOpenNodeTag(flags, type, attributes, literal, true);
346
+ yield buildOpenNodeTag(flags, name, literal, attributes, true);
233
347
  } else {
234
348
  if (state.holding.length) {
235
349
  yield* state.holding;
@@ -242,7 +356,7 @@ function* __prettyGroupTags(tags) {
242
356
  }
243
357
 
244
358
  if (tag.type === OpenNodeTag) {
245
- if (tag.value.literalValue) {
359
+ if (tag.value.selfClosing) {
246
360
  yield tag;
247
361
  } else {
248
362
  states = states.push({ holding: [tag], broken: false, open: tag });
@@ -253,6 +367,293 @@ function* __prettyGroupTags(tags) {
253
367
  }
254
368
  }
255
369
 
370
+ let pushFrame = (parent = null) => {
371
+ let heldProperties = parent ? [...parent.heldProperties] : [];
372
+ if (parent) {
373
+ parent.heldProperties = [];
374
+ }
375
+ return {
376
+ parent,
377
+ depth: !parent ? 0 : parent.depth + 1,
378
+ ref: null,
379
+ bindings: [],
380
+ open: null,
381
+ coverDepth: 0,
382
+ heldProperties,
383
+ expanded: false,
384
+ shifting: parent?.shifting || false,
385
+ };
386
+ };
387
+
388
+ let popFrame = (frame) => {
389
+ let { heldProperties } = frame;
390
+
391
+ if (frame.parent) {
392
+ frame.parent.heldProperties = heldProperties;
393
+ frame.parent.shifting = frame.shifting;
394
+ } else if (heldProperties.length) {
395
+ throw new Error();
396
+ }
397
+
398
+ return frame.parent;
399
+ };
400
+
401
+ function* emitHeld(frame, nextTag) {
402
+ if (frame.ref?.flags.expression && nextTag?.type === ShiftTag) return;
403
+
404
+ for (let property of frame.heldProperties) {
405
+ let { reference, bindings, node } = property;
406
+ yield buildChild(ReferenceTag, reference);
407
+ let isAnonymous = node.value.flags.token && !node.value.name;
408
+ if (!isAnonymous) {
409
+ for (let binding of bindings) {
410
+ yield buildChild(BindingTag, binding);
411
+ }
412
+ }
413
+ yield* streamFromTree(node);
414
+ }
415
+ frame.heldProperties = [];
416
+ }
417
+
418
+ export const hoistTrivia = (tags) => {
419
+ const co = new Coroutine(getStreamIterator(tags));
420
+
421
+ return new StreamIterable(__hoistTriviaOuter(co));
422
+ };
423
+
424
+ function* __hoistTriviaOuter(co) {
425
+ co.advance();
426
+
427
+ let firstTag = co.value;
428
+
429
+ co.advance();
430
+
431
+ yield* __hoistTrivia(co, firstTag);
432
+ }
433
+
434
+ function* __hoistTrivia(co, firstTag) {
435
+ if (co.current instanceof Promise) {
436
+ co.current = yield wait(co.current);
437
+ }
438
+ let frame = null;
439
+ let lastFrame = null;
440
+ let alreadyAdvanced = true;
441
+ let tag;
442
+ let nextTag;
443
+
444
+ tag = firstTag;
445
+ nextTag = co.value;
446
+
447
+ for (;;) {
448
+ if (co.done || !nextTag) break;
449
+
450
+ if (!alreadyAdvanced) {
451
+ co.advance();
452
+
453
+ if (co.current instanceof Promise) {
454
+ co.current = yield wait(co.current);
455
+ }
456
+ }
457
+ alreadyAdvanced = false;
458
+
459
+ nextTag = co.value;
460
+
461
+ if (tag.type === OpenNodeTag) {
462
+ let isCover = tag.value.type === Symbol.for('_');
463
+ let parentFrame = frame?.ref && !frame.open ? frame.parent : frame;
464
+ let done = false;
465
+
466
+ if (frame?.ref.type === '#' && !frame.ref.name) {
467
+ let { heldProperties } = frame;
468
+ if (heldProperties.length) {
469
+ let property = heldProperties[heldProperties.length - 1];
470
+
471
+ if (property.node) {
472
+ throw new Error();
473
+ }
474
+
475
+ let node = yield* treeFromStream(new StreamIterable(__hoistTrivia(co, tag)));
476
+
477
+ frame.heldProperties[heldProperties.length - 1] = buildProperty([
478
+ property.tags[0],
479
+ property.tags[1] || [],
480
+ node,
481
+ ]);
482
+
483
+ nextTag = co.value;
484
+
485
+ lastFrame = frame;
486
+
487
+ frame = popFrame(frame);
488
+
489
+ if (
490
+ !frame.ref.flags.expression &&
491
+ (frame.expanded || frame.parent?.open.value.type !== Symbol.for('_'))
492
+ ) {
493
+ yield* emitHeld(frame, nextTag);
494
+ }
495
+
496
+ done = true;
497
+ }
498
+ } else if (frame && !isCover) {
499
+ if (!frame.shifting && frame.ref && frame.ref.type !== '_') {
500
+ yield buildChild(ReferenceTag, frame.ref);
501
+ let isAnonymous = tag.value.flags.token && !tag.value.name;
502
+ if (!isAnonymous) {
503
+ for (let binding of frame.bindings) {
504
+ yield buildChild(BindingTag, binding);
505
+ }
506
+ }
507
+ }
508
+ }
509
+
510
+ if (!done) {
511
+ if (!frame?.ref || frame.open) {
512
+ frame = pushFrame(parentFrame);
513
+
514
+ frame.heldProperties = parentFrame?.heldProperties ? [...parentFrame.heldProperties] : [];
515
+
516
+ if (tag.value.type === Symbol.for('__')) {
517
+ frame.open = tag;
518
+ frame.ref = buildReference();
519
+ } else if (isCover) {
520
+ frame.ref = parentFrame?.ref || buildReference('_');
521
+ frame.bindings =
522
+ parentFrame && tag.value.type === Symbol.for('_') ? [...parentFrame.bindings] : [];
523
+ frame.shifting =
524
+ parentFrame && tag.value.type === Symbol.for('_') ? parentFrame.shifting : false;
525
+ } else {
526
+ frame.ref = buildReference();
527
+ }
528
+ }
529
+
530
+ frame.coverDepth = isCover ? (parentFrame?.coverDepth ?? 0) + 1 : 0;
531
+
532
+ frame.open = tag;
533
+
534
+ if (tag.value.selfClosing) {
535
+ lastFrame = frame;
536
+
537
+ frame = popFrame(frame);
538
+
539
+ if (!frame) {
540
+ yield tag;
541
+
542
+ break;
543
+ } else {
544
+ yield tag;
545
+ }
546
+ } else {
547
+ if (!isCover) {
548
+ yield tag;
549
+ }
550
+ }
551
+ }
552
+ } else if (tag.type === ReferenceTag) {
553
+ let parentFrame = frame;
554
+
555
+ frame = pushFrame(parentFrame);
556
+
557
+ if (tag.value.type === '#' && !tag.value.name) {
558
+ if (parentFrame.depth === 0 && parentFrame.coverDepth && !parentFrame.expanded) {
559
+ yield parentFrame.open;
560
+
561
+ parentFrame.expanded = true;
562
+ }
563
+
564
+ frame.heldProperties.push(buildProperty([tag]));
565
+ } else {
566
+ if (!parentFrame.shifting) {
567
+ yield* emitHeld(frame, nextTag);
568
+ }
569
+ }
570
+
571
+ frame.coverDepth = parentFrame.coverDepth;
572
+ frame.ref = tag.value.type === '_' ? parentFrame.ref : tag.value;
573
+ frame.bindings = parentFrame && tag.value.type === '_' ? [...parentFrame.bindings] : [];
574
+ frame.shifting = parentFrame && tag.value.type === '_' ? parentFrame.shifting : false;
575
+
576
+ if (tag.value.type === '_' && parentFrame.expanded) {
577
+ yield tag;
578
+ }
579
+ } else if (tag.type === ShiftTag) {
580
+ let doneFrame = frame;
581
+
582
+ frame = pushFrame(doneFrame);
583
+
584
+ frame.coverDepth = doneFrame?.coverDepth || 0;
585
+ frame.ref = lastFrame.ref;
586
+ frame.bindings = doneFrame && tag.value.type === '_' ? [...doneFrame.bindings] : [];
587
+ frame.shifting = true;
588
+ // frame.heldProperties = [...doneFrame.heldProperties];
589
+
590
+ yield tag;
591
+ } else if (tag.type === BindingTag) {
592
+ let { heldProperties } = frame;
593
+ if (heldProperties.length) {
594
+ let property = heldProperties[heldProperties.length - 1];
595
+ let { tags, node } = property;
596
+ if (!node) {
597
+ heldProperties[heldProperties.length - 1] = buildProperty([
598
+ tags[0],
599
+ [...(tags[1] || []), tag],
600
+ ]);
601
+ frame.bindings.push(tag.value);
602
+ }
603
+ }
604
+ if (!frame.ref) {
605
+ frame = pushFrame(frame);
606
+ frame.ref = buildReference();
607
+ }
608
+
609
+ frame.bindings.push(tag.value);
610
+ } else if (tag.type === CloseNodeTag) {
611
+ lastFrame = frame;
612
+
613
+ if (!lastFrame.open.value.type || !lastFrame.parent) {
614
+ yield* emitHeld(lastFrame, nextTag);
615
+ }
616
+
617
+ frame = popFrame(frame);
618
+
619
+ if (frame) {
620
+ frame.heldProperties = [...lastFrame.heldProperties];
621
+ if (!lastFrame?.coverDepth) {
622
+ yield tag;
623
+ }
624
+ } else {
625
+ if (lastFrame.expanded || lastFrame.open.value.type !== Symbol.for('_')) {
626
+ yield tag;
627
+ }
628
+ break;
629
+ }
630
+ } else if (tag.type === GapTag || tag.type === NullTag) {
631
+ if (frame.ref) {
632
+ yield buildChild(ReferenceTag, frame.ref);
633
+ }
634
+ lastFrame = frame;
635
+
636
+ frame = popFrame(frame);
637
+
638
+ if (tag.type === GapTag && frame.heldProperties.length) {
639
+ yield tag;
640
+
641
+ frame.shifting = false;
642
+
643
+ yield* emitHeld(frame, nextTag);
644
+ } else {
645
+ yield tag;
646
+ }
647
+ } else {
648
+ yield tag;
649
+ }
650
+
651
+ tag = nextTag;
652
+ }
653
+
654
+ if (frame) throw new Error();
655
+ }
656
+
256
657
  function* __generatePrettyCSTML(tags, options) {
257
658
  let { indent = ' ', inline: inlineOption = true } = options;
258
659
 
@@ -261,7 +662,8 @@ function* __generatePrettyCSTML(tags, options) {
261
662
  return;
262
663
  }
263
664
 
264
- const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
665
+ const co = new Coroutine(getStreamIterator(prettyGroupTags(hoistTrivia(tags))));
666
+ // const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
265
667
  let indentLevel = 0;
266
668
  let first = true;
267
669
  let inline = false;
@@ -270,19 +672,19 @@ function* __generatePrettyCSTML(tags, options) {
270
672
  for (;;) {
271
673
  co.advance();
272
674
 
273
- if (co.done) break;
274
-
275
675
  if (co.current instanceof Promise) {
276
- co.current = yield co.current;
676
+ co.current = yield wait(co.current);
277
677
  }
278
678
 
679
+ if (co.done) break;
680
+
279
681
  const tag = co.value;
280
682
 
281
683
  if (tag.type === 'Effect') {
282
684
  continue;
283
685
  }
284
686
 
285
- if (tag.type === BindingTag && !tag.value.languagePath?.length) {
687
+ if (tag.type === BindingTag && !tag.value.segments?.length) {
286
688
  continue;
287
689
  }
288
690
 
@@ -293,7 +695,6 @@ function* __generatePrettyCSTML(tags, options) {
293
695
  (tag.type === NullTag ||
294
696
  tag.type === GapTag ||
295
697
  tag.type === BindingTag ||
296
- tag.type === InitializerTag ||
297
698
  (tag.type === OpenNodeTag && tag.value.selfClosing));
298
699
 
299
700
  if (!first && !inline) {
@@ -374,11 +775,11 @@ export const getCooked = (tags) => {
374
775
  }
375
776
  }
376
777
 
377
- if (!(ref.value.type === '#' || (ref.value.type === '@' && attributes.cooked))) {
778
+ if (!(ref.type === '#' || (ref.type === '@' && attributes.cooked))) {
378
779
  throw new Error('cookable nodes must not contain other nodes');
379
780
  }
380
781
 
381
- if (ref.value.type === '@') {
782
+ if (ref.type === '@') {
382
783
  const { cooked: cookedValue } = tag.value.attributes;
383
784
 
384
785
  if (!cookedValue) throw new Error('cannot cook string: it contains uncooked escapes');
package/lib/symbols.js CHANGED
@@ -6,12 +6,13 @@ export const ShiftTag = Symbol.for('ShiftTag');
6
6
  export const GapTag = Symbol.for('GapTag');
7
7
  export const BindingTag = Symbol.for('BindingTag');
8
8
  export const NullTag = Symbol.for('NullTag');
9
- export const InitializerTag = Symbol.for('InitializerTag');
10
9
  export const AttributeDefinition = Symbol.for('AttributeDefinition');
11
10
  export const LiteralTag = Symbol.for('LiteralTag');
12
11
 
13
12
  export const Document = Symbol.for('Document');
13
+ export const TreeNode = Symbol.for('TreeNode');
14
+ export const NullNode = Symbol.for('NullNode');
15
+ export const GapNode = Symbol.for('GapNode');
14
16
  export const Node = Symbol.for('Node');
15
17
 
16
18
  export const Property = Symbol.for('Property');
17
- export const PropertyWrapper = Symbol.for('PropertyWrapper');