@bablr/agast-helpers 0.10.10 → 0.11.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,15 @@
1
1
  import { Coroutine } from '@bablr/coroutine';
2
2
  import emptyStack from '@iter-tools/imm-stack';
3
- import { getStreamIterator, StreamIterable, StreamGenerator, wait } from '@bablr/stream-iterator';
3
+ import {
4
+ getStreamIterator,
5
+ StreamIterable,
6
+ StreamGenerator,
7
+ wait,
8
+ createDeferred,
9
+ continue_,
10
+ } from '@bablr/stream-iterator';
4
11
  import * as Tags from './tags.js';
12
+ import * as BList from './b-list.js';
5
13
  import { printTag } from './print.js';
6
14
  import {
7
15
  buildChild,
@@ -10,6 +18,8 @@ import {
10
18
  buildOpenNodeTag,
11
19
  buildProperty,
12
20
  buildReference,
21
+ parseTag,
22
+ parseTagType,
13
23
  } from './builders.js';
14
24
  import {
15
25
  OpenNodeTag,
@@ -22,78 +32,60 @@ import {
22
32
  DoctypeTag,
23
33
  ShiftTag,
24
34
  AttributeDefinition,
35
+ StreamTag,
25
36
  } from './symbols.js';
26
- import { buildNode, isGapNode, Path } from './path.js';
37
+ import { buildNode, getRoot, isGapNode, isNode, Path } from './path.js';
27
38
  import { streamFromTree } from './tree.js';
39
+ import { freeze, isObject } from './object.js';
40
+ import { maybeWait } from './iterable.js';
41
+ import { arrayValues } from '@bablr/record';
42
+ import { buildParser } from './parse.js';
28
43
 
29
- export { getStreamIterator, StreamIterable, StreamGenerator, wait };
44
+ export { getStreamIterator, StreamIterable, StreamGenerator, wait, continue_ };
30
45
 
31
46
  export * from './print.js';
32
47
 
33
- export const maybeWait = (maybePromise, callback) => {
34
- if (maybePromise instanceof Promise) {
35
- return maybePromise.then(callback);
36
- } else {
37
- return callback(maybePromise);
38
- }
39
- };
40
-
41
- function* __flatMap(fn, tags) {
42
- const co = new Coroutine(getStreamIterator(tags));
48
+ function* __evaluateReturn(iterable) {
49
+ let iter = getStreamIterator(iterable);
50
+ let step;
43
51
 
44
52
  for (;;) {
45
- co.advance();
46
-
47
- if (co.current instanceof Promise) {
48
- co.current = yield wait(co.current);
53
+ step = iter.next();
54
+ while (step === null || step instanceof Promise) {
55
+ if (step === null) yield continue_(), (step = iter.next());
56
+ if (step instanceof Promise) step = yield wait(step);
49
57
  }
50
- if (co.done) break;
51
-
52
- let { value } = co;
53
-
54
- yield* fn(value);
58
+ if (step.done) break;
55
59
  }
56
60
 
57
- return true;
61
+ return step.value;
58
62
  }
59
63
 
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
- };
64
+ export const evaluateReturn = (iterable) => {
65
+ let iter = new StreamGenerator(__evaluateReturn(iterable));
67
66
 
68
- export const evaluateReturnAsync = async (iterable) => {
69
- const co = new Coroutine(getStreamIterator(iterable));
70
- while (true) {
71
- if (co.current instanceof Promise) {
72
- co.current = await co.current;
73
- }
74
- if (co.done) break;
75
- co.advance();
76
- }
77
- return co.value;
67
+ return maybeWait(iter.next(), (step) => step.value);
78
68
  };
79
69
 
80
70
  function* __treeFromStream(tags, options) {
81
71
  let path = null;
82
- const co = new Coroutine(getStreamIterator(tags));
83
- const expressionsCo = new Coroutine(getStreamIterator(options.expressions || []));
72
+ let iter = getStreamIterator(tags);
73
+ let expressionsIter = getStreamIterator(options.expressions || []);
74
+ let step;
75
+ let expressionsStep;
84
76
 
85
77
  for (;;) {
86
- co.advance();
87
-
88
- if (co.current instanceof Promise) {
89
- co.current = yield wait(co.current);
78
+ step = iter.next();
79
+ while (step === null || step instanceof Promise) {
80
+ if (step === null) yield continue_(), (step = iter.next());
81
+ if (step instanceof Promise) step = yield wait(step);
90
82
  }
83
+ if (step.done) break;
91
84
 
92
- if (co.done) break;
85
+ let tag = step.value;
86
+ let tagType = parseTagType(tag);
93
87
 
94
- let tag = co.value;
95
-
96
- if (tag.type === 'Effect' || tag.type === DoctypeTag) {
88
+ if (tagType === 'Effect' || tagType === DoctypeTag) {
97
89
  continue;
98
90
  }
99
91
 
@@ -107,14 +99,18 @@ function* __treeFromStream(tags, options) {
107
99
  continue;
108
100
  }
109
101
 
110
- if (tag.type === GapTag && !path.held) {
111
- if (!expressionsCo.current || !expressionsCo.done) {
112
- expressionsCo.advance();
102
+ if (tagType === GapTag && !path.held) {
103
+ if (!expressionsStep || !expressionsStep.done) {
104
+ expressionsStep = expressionsIter.next();
105
+ while (expressionsStep === null || expressionsStep instanceof Promise) {
106
+ if (expressionsStep === null) yield continue_(), (expressionsStep = iter.next());
107
+ if (expressionsStep instanceof Promise) expressionsStep = yield wait(expressionsStep);
108
+ }
113
109
  }
114
110
 
115
111
  if (path.node.value.flags.token) {
116
- if (expressionsCo.done || expressionsCo.value != null) {
117
- let node = expressionsCo.value;
112
+ if (expressionsStep.done || expressionsStep.value != null) {
113
+ let node = expressionsStep.value;
118
114
  if (isGapNode(node)) {
119
115
  throw new Error('not implemented');
120
116
  } else {
@@ -122,11 +118,11 @@ function* __treeFromStream(tags, options) {
122
118
  }
123
119
  }
124
120
  } else {
125
- let node = expressionsCo.done
121
+ let node = expressionsStep.done
126
122
  ? buildNode(buildGapTag())
127
- : path == null || expressionsCo.value == null
123
+ : path == null || expressionsStep.value == null
128
124
  ? buildNode(buildNullTag())
129
- : expressionsCo.value;
125
+ : expressionsStep.value;
130
126
 
131
127
  path = path.advance(node);
132
128
  }
@@ -142,33 +138,30 @@ function* __treeFromStream(tags, options) {
142
138
  return path?.node;
143
139
  }
144
140
 
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));
141
+ export const treeFromStream = (tags, options = freeze({})) => {
142
+ let iter = new StreamGenerator(__treeFromStream(tags, options));
143
+ return maybeWait(iter.next(), (step) => step.value);
153
144
  };
154
145
 
155
146
  function* __isEmpty(tags) {
156
- const co = new Coroutine(getStreamIterator(tags));
147
+ let iter = getStreamIterator(tags);
148
+ let step;
157
149
 
158
150
  for (;;) {
159
- co.advance();
160
-
161
- if (co.current instanceof Promise) {
162
- co.current = yield wait(co.current);
151
+ step = iter.next();
152
+ while (step === null || step instanceof Promise) {
153
+ if (step === null) yield continue_(), (step = iter.next());
154
+ if (step instanceof Promise) step = yield wait(step);
163
155
  }
164
- if (co.done) break;
156
+ if (step.done) break;
165
157
 
166
158
  let depth = 0;
167
159
  let ref = null;
168
160
 
169
- const tag = co.value;
161
+ const tag = step.value;
162
+ let tagType = parseTagType(tag);
170
163
 
171
- switch (tag.type) {
164
+ switch (tagType) {
172
165
  case ReferenceTag:
173
166
  ref = tag;
174
167
  break;
@@ -176,7 +169,7 @@ function* __isEmpty(tags) {
176
169
  case OpenNodeTag:
177
170
  ++depth;
178
171
 
179
- if (tag.value.literalValue) return false;
172
+ if (parseTag(tag).value.literalValue) return false;
180
173
 
181
174
  if (depth === 0 && ref.type === '@') {
182
175
  return false;
@@ -204,73 +197,76 @@ export const printCSTML = (tags) => {
204
197
  return stringFromStream(generateCSTML(tags));
205
198
  };
206
199
 
200
+ export function* streamFromString(input) {
201
+ let p = buildParser(input);
202
+ let { str } = p;
203
+ let chr = str[p.idx];
204
+
205
+ while (' \t\r\n'.includes(chr)) chr = str[++p.idx];
206
+
207
+ while (p.idx < str.length) {
208
+ yield printTag(parseTag(p));
209
+ chr = str[p.idx];
210
+
211
+ while (' \t\r\n'.includes(chr)) chr = str[++p.idx];
212
+ }
213
+ }
214
+
207
215
  function* __emptyStreamIterator() {}
208
216
 
209
217
  export const emptyStreamIterator = () => new StreamIterable(__emptyStreamIterator());
210
218
 
211
- export const asyncStringFromStream = async (stream) => {
212
- const co = new Coroutine(getStreamIterator(stream));
219
+ function* __stringFromStream(stream) {
220
+ let iter = getStreamIterator(stream);
221
+ let step;
213
222
  let str = '';
214
223
 
215
224
  for (;;) {
216
- co.advance();
217
-
218
- if (co.current instanceof Promise) {
219
- co.current = await co.current;
225
+ step = iter.next();
226
+ while (step === null || step instanceof Promise) {
227
+ if (step === null) yield continue_(), (step = iter.next());
228
+ if (step instanceof Promise) step = yield wait(step);
220
229
  }
230
+ if (step.done) break;
221
231
 
222
- if (co.done) break;
223
-
224
- const chr = co.value;
232
+ let chr = step.value;
225
233
 
226
234
  str += chr;
227
235
  }
228
236
 
229
237
  return str;
230
- };
238
+ }
231
239
 
232
240
  export const stringFromStream = (stream) => {
233
- const co = new Coroutine(stream[Symbol.iterator]());
234
- let str = '';
235
-
236
- for (;;) {
237
- co.advance();
238
-
239
- if (co.done) break;
240
-
241
- const chr = co.value;
242
-
243
- str += chr;
244
- }
245
-
246
- return str;
241
+ return evaluateReturn(__stringFromStream(stream));
247
242
  };
248
243
 
249
244
  function* __generateCSTML(tags, options) {
250
245
  if (!tags) {
251
- yield* '<//>';
246
+ yield* 'null';
252
247
  return;
253
248
  }
254
249
 
255
250
  let prevTag = null;
256
-
257
- const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
251
+ let iter = getStreamIterator(prettyGroupTags(tags));
252
+ let step;
258
253
 
259
254
  for (;;) {
260
- co.advance();
261
-
262
- if (co.current instanceof Promise) {
263
- co.current = yield wait(co.current);
255
+ step = iter.next();
256
+ while (step === null || step instanceof Promise) {
257
+ if (step === null) yield continue_(), (step = iter.next());
258
+ if (step instanceof Promise) step = yield wait(step);
264
259
  }
265
- if (co.done) break;
260
+ if (step.done) break;
266
261
 
267
- const tag = co.value;
262
+ const tag = step.value;
263
+ let tagType = parseTagType(tag);
268
264
 
269
- if (tag.type === ReferenceTag && prevTag.type === NullTag) {
265
+ if (tagType === ReferenceTag && prevTag.type === NullTag) {
270
266
  yield* ' ';
271
267
  }
272
268
 
273
- if (tag.type === 'Effect') {
269
+ if (tagType === 'Effect') {
274
270
  continue;
275
271
  }
276
272
 
@@ -280,11 +276,11 @@ function* __generateCSTML(tags, options) {
280
276
  }
281
277
  }
282
278
 
283
- export const generateCSTML = (tags, options = {}) =>
279
+ export const generateCSTML = (tags, options = freeze({})) =>
284
280
  new StreamIterable(__generateCSTML(tags, options));
285
281
 
286
282
  const isToken = (tag) => {
287
- return tag.value.flags.token;
283
+ return parseTag(tag).value.flags.token;
288
284
  };
289
285
 
290
286
  export const prettyGroupTags = (tags) => new StreamIterable(__prettyGroupTags(tags));
@@ -292,19 +288,19 @@ export const prettyGroupTags = (tags) => new StreamIterable(__prettyGroupTags(ta
292
288
  function* __prettyGroupTags(tags) {
293
289
  let states = emptyStack.push({ holding: [], broken: false, open: null });
294
290
  let state = states.value;
295
-
296
- const co = new Coroutine(getStreamIterator(tags));
291
+ let iter = getStreamIterator(tags);
292
+ let step;
297
293
 
298
294
  for (;;) {
299
- co.advance();
295
+ step = iter.next();
300
296
 
301
- if (co.current instanceof Promise) {
302
- co.current = yield wait(co.current);
297
+ while (step === null || step instanceof Promise) {
298
+ if (step === null) yield continue_(), (step = iter.next());
299
+ if (step instanceof Promise) step = yield wait(step);
303
300
  }
301
+ if (step.done) break;
304
302
 
305
- if (co.done) break;
306
-
307
- const tag = co.value;
303
+ const tag = parseTag(step.value);
308
304
  const isOpenClose = tag.type === CloseNodeTag || tag.type === OpenNodeTag;
309
305
 
310
306
  if (
@@ -367,6 +363,99 @@ function* __prettyGroupTags(tags) {
367
363
  }
368
364
  }
369
365
 
366
+ function* __transformStream(tags, stream, strategy) {
367
+ let iter = getStreamIterator(tags);
368
+ let step;
369
+ let str = '';
370
+ let stream_ = 1;
371
+ let transforming = stream === stream_;
372
+ let alreadyAdvanced = false;
373
+
374
+ function* __streamTags() {
375
+ for (;;) {
376
+ while (step === null || step instanceof Promise) {
377
+ if (step === null) yield continue_(), (step = iter.next());
378
+ if (step instanceof Promise) step = yield wait(step);
379
+ }
380
+ if (step.done) break;
381
+
382
+ let tag = parseTag(step.value);
383
+
384
+ if (tag.type === StreamTag) {
385
+ if (tag.value.stream !== stream) {
386
+ transforming = false;
387
+ yield continue_();
388
+ }
389
+
390
+ stream_ = tag.value.stream;
391
+ } else {
392
+ yield step.value;
393
+ }
394
+
395
+ step = iter.next();
396
+ }
397
+ }
398
+
399
+ let strategyIter = getStreamIterator(strategy(new StreamGenerator(__streamTags())));
400
+ let strategyStep;
401
+
402
+ for (;;) {
403
+ if (!alreadyAdvanced) {
404
+ step = iter.next();
405
+ }
406
+ alreadyAdvanced = false;
407
+ while (step === null || step instanceof Promise) {
408
+ if (step === null) yield continue_(), (step = iter.next());
409
+ if (step instanceof Promise) step = yield wait(step);
410
+ }
411
+ if (step.done) break;
412
+
413
+ let tag = parseTag(step.value);
414
+
415
+ if (transforming) {
416
+ strategyStep = strategyIter.next();
417
+
418
+ while (strategyStep !== null) {
419
+ if (strategyStep instanceof Promise) strategyStep = yield wait(strategyStep);
420
+ if (strategyStep) {
421
+ if (strategyStep.done) break;
422
+ yield strategyStep.value;
423
+
424
+ strategyStep = strategyIter.next();
425
+ }
426
+ }
427
+ if (step.done) break;
428
+ tag = parseTag(step.value);
429
+ if (tag.type === StreamTag) {
430
+ stream_ = tag.value.stream;
431
+ } else {
432
+ throw new Error();
433
+ }
434
+ yield step.value;
435
+ transforming = stream_ === stream;
436
+ } else {
437
+ if (tag.type === StreamTag) {
438
+ stream_ = tag.value.stream;
439
+
440
+ yield step.value;
441
+
442
+ if (stream_ === stream) {
443
+ transforming = true;
444
+ alreadyAdvanced = true;
445
+ } else {
446
+ transforming = false;
447
+ }
448
+ } else {
449
+ yield step.value;
450
+ }
451
+ }
452
+ }
453
+ }
454
+
455
+ export const transformStream = (tags, stream, strategy) => {
456
+ return new StreamIterable(__transformStream(tags, stream, strategy));
457
+ };
458
+
370
459
  let pushFrame = (parent = null) => {
371
460
  let heldProperties = parent ? [...parent.heldProperties] : [];
372
461
  if (parent) {
@@ -380,7 +469,7 @@ let pushFrame = (parent = null) => {
380
469
  open: null,
381
470
  coverDepth: 0,
382
471
  heldProperties,
383
- expanded: false,
472
+ expanded: !parent,
384
473
  shifting: parent?.shifting || false,
385
474
  };
386
475
  };
@@ -406,7 +495,7 @@ function* emitHeld(frame, nextTag) {
406
495
  yield buildChild(ReferenceTag, reference);
407
496
  let isAnonymous = node.value.flags.token && !node.value.name;
408
497
  if (!isAnonymous) {
409
- for (let binding of bindings) {
498
+ for (let binding of arrayValues(bindings)) {
410
499
  yield buildChild(BindingTag, binding);
411
500
  }
412
501
  }
@@ -424,16 +513,18 @@ export const hoistTrivia = (tags) => {
424
513
  function* __hoistTriviaOuter(co) {
425
514
  co.advance();
426
515
 
427
- if (co.current instanceof Promise) {
428
- co.current = yield wait(co.current);
516
+ while (co.current === null || co.current instanceof Promise) {
517
+ if (co.current === null) yield continue_(), co.advance();
518
+ if (co.current instanceof Promise) co.current = yield wait(co.current);
429
519
  }
430
520
 
431
- let firstTag = co.value;
521
+ let firstTag = parseTag(co.value);
432
522
 
433
523
  co.advance();
434
524
 
435
- if (co.current instanceof Promise) {
436
- co.current = yield wait(co.current);
525
+ while (co.current === null || co.current instanceof Promise) {
526
+ if (co.current === null) yield continue_(), co.advance();
527
+ if (co.current instanceof Promise) co.current = yield wait(co.current);
437
528
  }
438
529
 
439
530
  yield* __hoistTrivia(co, firstTag);
@@ -443,25 +534,25 @@ function* __hoistTrivia(co, firstTag) {
443
534
  let frame = null;
444
535
  let lastFrame = null;
445
536
  let alreadyAdvanced = true;
446
- let tag;
537
+ let tag = firstTag;
538
+
447
539
  let nextTag;
448
540
 
449
- tag = firstTag;
450
- nextTag = co.value;
541
+ nextTag = parseTag(co.value);
451
542
 
452
543
  for (;;) {
453
544
  if (co.done || !nextTag) break;
454
-
455
545
  if (!alreadyAdvanced) {
456
546
  co.advance();
457
547
 
458
- if (co.current instanceof Promise) {
459
- co.current = yield wait(co.current);
548
+ while (co.current === null || co.current instanceof Promise) {
549
+ if (co.current === null) yield continue_(), co.advance();
550
+ if (co.current instanceof Promise) co.current = yield wait(co.current);
460
551
  }
461
552
  }
462
553
  alreadyAdvanced = false;
463
554
 
464
- nextTag = co.value;
555
+ nextTag = co.done ? nextTag : parseTag(co.value);
465
556
 
466
557
  if (tag.type === OpenNodeTag) {
467
558
  let isCover = tag.value.type === Symbol.for('_');
@@ -471,21 +562,53 @@ function* __hoistTrivia(co, firstTag) {
471
562
  if (frame?.ref.type === '#' && !frame.ref.name) {
472
563
  let { heldProperties } = frame;
473
564
  if (heldProperties.length) {
474
- let property = heldProperties[heldProperties.length - 1];
565
+ let heldProperty = heldProperties[heldProperties.length - 1];
475
566
 
476
- if (property.node) {
567
+ if (heldProperty.node) {
477
568
  throw new Error();
478
569
  }
479
570
 
480
- let node = yield* treeFromStream(new StreamIterable(__hoistTrivia(co, tag)));
571
+ let node = treeFromStream(new StreamIterable(__hoistTrivia(co, tag)));
572
+
573
+ if (node instanceof Promise) {
574
+ node = yield wait(node);
575
+ }
481
576
 
482
- frame.heldProperties[heldProperties.length - 1] = buildProperty([
483
- property.tags[0],
484
- property.tags[1] || [],
485
- node,
486
- ]);
577
+ if (node.value.type === Symbol.for('_')) {
578
+ let leading = true;
579
+ for (let property of Tags.traverse(node.value.children)) {
580
+ if (property.value.reference.type === '_') {
581
+ leading = false;
582
+ frame.heldProperties[heldProperties.length - 1] = buildProperty(
583
+ BList.fromValues(
584
+ [
585
+ heldProperty.tags[1][0],
586
+ heldProperty.tags[1][1] || Tags.fromValues([]),
587
+ getRoot(node),
588
+ ],
589
+ 1,
590
+ ),
591
+ );
592
+ } else {
593
+ if (leading) {
594
+ let top = frame.heldProperties.pop();
595
+ frame.heldProperties.push(property);
596
+ frame.heldProperties.push(top);
597
+ } else {
598
+ frame.heldProperties.push(property);
599
+ }
600
+ }
601
+ }
602
+ } else {
603
+ frame.heldProperties[heldProperties.length - 1] = buildProperty(
604
+ BList.fromValues(
605
+ [heldProperty.tags[1][0], heldProperty.tags[1][1] || Tags.fromValues([]), node],
606
+ 1,
607
+ ),
608
+ );
609
+ }
487
610
 
488
- nextTag = co.value;
611
+ nextTag = parseTag(co.value);
489
612
 
490
613
  lastFrame = frame;
491
614
 
@@ -493,7 +616,7 @@ function* __hoistTrivia(co, firstTag) {
493
616
 
494
617
  if (
495
618
  !frame.ref.flags.expression &&
496
- (frame.expanded || frame.parent?.open.value.type !== Symbol.for('_'))
619
+ (frame.expanded || frame.parent?.open?.value.type !== Symbol.for('_'))
497
620
  ) {
498
621
  yield* emitHeld(frame, nextTag);
499
622
  }
@@ -501,7 +624,7 @@ function* __hoistTrivia(co, firstTag) {
501
624
  done = true;
502
625
  }
503
626
  } else if (frame && !isCover) {
504
- if (!frame.shifting && frame.ref && frame.ref.type !== '_') {
627
+ if (!frame.shifting && ((frame.ref && frame.ref.type !== '_') || frame.expanded)) {
505
628
  yield buildChild(ReferenceTag, frame.ref);
506
629
  let isAnonymous = tag.value.flags.token && !tag.value.name;
507
630
  if (!isAnonymous) {
@@ -549,7 +672,7 @@ function* __hoistTrivia(co, firstTag) {
549
672
  yield tag;
550
673
  }
551
674
  } else {
552
- if (!isCover) {
675
+ if (!isCover || !parentFrame) {
553
676
  yield tag;
554
677
  }
555
678
  }
@@ -560,13 +683,7 @@ function* __hoistTrivia(co, firstTag) {
560
683
  frame = pushFrame(parentFrame);
561
684
 
562
685
  if (tag.value.type === '#' && !tag.value.name) {
563
- if (parentFrame.depth === 0 && parentFrame.coverDepth && !parentFrame.expanded) {
564
- yield parentFrame.open;
565
-
566
- parentFrame.expanded = true;
567
- }
568
-
569
- frame.heldProperties.push(buildProperty([tag]));
686
+ frame.heldProperties.push(buildProperty(BList.fromValues([printTag(tag)])));
570
687
  } else {
571
688
  if (!parentFrame.shifting) {
572
689
  yield* emitHeld(frame, nextTag);
@@ -577,8 +694,9 @@ function* __hoistTrivia(co, firstTag) {
577
694
  frame.ref = tag.value.type === '_' ? parentFrame.ref : tag.value;
578
695
  frame.bindings = parentFrame && tag.value.type === '_' ? [...parentFrame.bindings] : [];
579
696
  frame.shifting = parentFrame && tag.value.type === '_' ? parentFrame.shifting : false;
697
+ frame.expanded = frame.depth === 1 && frame.coverDepth === 1;
580
698
 
581
- if (tag.value.type === '_' && parentFrame.expanded) {
699
+ if (tag.value.type === '_' && parentFrame.expanded && parentFrame.depth) {
582
700
  yield tag;
583
701
  }
584
702
  } else if (tag.type === ShiftTag) {
@@ -588,7 +706,7 @@ function* __hoistTrivia(co, firstTag) {
588
706
 
589
707
  frame.coverDepth = doneFrame?.coverDepth || 0;
590
708
  frame.ref = lastFrame.ref;
591
- frame.bindings = doneFrame && tag.value.type === '_' ? [...doneFrame.bindings] : [];
709
+ frame.bindings = doneFrame && tag.value?.type === '_' ? [...doneFrame.bindings] : [];
592
710
  frame.shifting = true;
593
711
  // frame.heldProperties = [...doneFrame.heldProperties];
594
712
 
@@ -599,10 +717,15 @@ function* __hoistTrivia(co, firstTag) {
599
717
  let property = heldProperties[heldProperties.length - 1];
600
718
  let { tags, node } = property;
601
719
  if (!node) {
602
- heldProperties[heldProperties.length - 1] = buildProperty([
603
- tags[0],
604
- [...(tags[1] || []), tag],
605
- ]);
720
+ heldProperties[heldProperties.length - 1] = buildProperty(
721
+ BList.fromValues(
722
+ [
723
+ tags[1][0] || Tags.fromValues([]),
724
+ Tags.push(tag, tags[1][1] || Tags.fromValues([])),
725
+ ],
726
+ 1,
727
+ ),
728
+ );
606
729
  frame.bindings.push(tag.value);
607
730
  }
608
731
  }
@@ -611,11 +734,13 @@ function* __hoistTrivia(co, firstTag) {
611
734
  frame.ref = buildReference();
612
735
  }
613
736
 
614
- frame.bindings.push(tag.value);
737
+ if (!heldProperties.length) {
738
+ frame.bindings.push(tag.value);
739
+ }
615
740
  } else if (tag.type === CloseNodeTag) {
616
741
  lastFrame = frame;
617
742
 
618
- if (!lastFrame.open.value.type || !lastFrame.parent) {
743
+ if (!lastFrame.open?.value.type || !lastFrame.parent) {
619
744
  yield* emitHeld(lastFrame, nextTag);
620
745
  }
621
746
 
@@ -663,36 +788,30 @@ function* __generatePrettyCSTML(tags, options) {
663
788
  let { indent = ' ', inline: inlineOption = true } = options;
664
789
 
665
790
  if (!tags) {
666
- yield* '<//>';
791
+ yield* 'null';
667
792
  return;
668
793
  }
669
794
 
670
- const co = new Coroutine(getStreamIterator(prettyGroupTags(hoistTrivia(tags))));
671
- // const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
795
+ let iter = getStreamIterator(prettyGroupTags(hoistTrivia(tags)));
796
+ let step;
672
797
  let indentLevel = 0;
673
- let first = true;
674
798
  let inline = false;
675
799
  let ref = null;
676
800
 
677
801
  for (;;) {
678
- co.advance();
679
-
680
- if (co.current instanceof Promise) {
681
- co.current = yield wait(co.current);
802
+ step = iter.next();
803
+ while (step === null || step instanceof Promise) {
804
+ if (step === null) yield continue_(), (step = iter.next());
805
+ if (step instanceof Promise) step = yield wait(step);
682
806
  }
807
+ if (step.done) break;
683
808
 
684
- if (co.done) break;
685
-
686
- const tag = co.value;
809
+ const tag = parseTag(step.value);
687
810
 
688
811
  if (tag.type === 'Effect') {
689
812
  continue;
690
813
  }
691
814
 
692
- if (tag.type === BindingTag && !tag.value.segments?.length) {
693
- continue;
694
- }
695
-
696
815
  inline =
697
816
  inlineOption &&
698
817
  inline &&
@@ -702,7 +821,7 @@ function* __generatePrettyCSTML(tags, options) {
702
821
  tag.type === BindingTag ||
703
822
  (tag.type === OpenNodeTag && tag.value.selfClosing));
704
823
 
705
- if (!first && !inline) {
824
+ if (!inline) {
706
825
  yield* '\n';
707
826
  }
708
827
 
@@ -728,8 +847,6 @@ function* __generatePrettyCSTML(tags, options) {
728
847
  if (tag.type === OpenNodeTag) {
729
848
  indentLevel += tag.value.selfClosing ? 0 : 1;
730
849
  }
731
-
732
- first = false;
733
850
  }
734
851
 
735
852
  if (indentLevel !== 0) {
@@ -739,11 +856,11 @@ function* __generatePrettyCSTML(tags, options) {
739
856
  yield* '\n';
740
857
  }
741
858
 
742
- export const generatePrettyCSTML = (tags, options = {}) => {
859
+ export const generatePrettyCSTML = (tags, options = freeze({})) => {
743
860
  return new StreamIterable(__generatePrettyCSTML(tags, options));
744
861
  };
745
862
 
746
- export const printPrettyCSTML = (tags, options = {}) => {
863
+ export const printPrettyCSTML = (tags, options = freeze({})) => {
747
864
  return stringFromStream(generatePrettyCSTML(tags, options));
748
865
  };
749
866
 
@@ -756,11 +873,12 @@ export const getCooked = (tags) => {
756
873
  let ref = null;
757
874
 
758
875
  for (const tag of tags) {
876
+ let tagType = parseTagType(tag);
759
877
  if (foundLast) throw new Error();
760
878
 
761
- switch (tag.type) {
879
+ switch (tagType) {
762
880
  case ReferenceTag: {
763
- ref = tag;
881
+ ref = parseTag(tag);
764
882
  if (depth === 1) {
765
883
  throw new Error('cookable nodes must not contain other nodes');
766
884
  }
@@ -768,7 +886,7 @@ export const getCooked = (tags) => {
768
886
  }
769
887
 
770
888
  case OpenNodeTag: {
771
- const { flags, attributes, literalValue, selfClosing } = tag.value;
889
+ const { flags, attributes, literalValue, selfClosing } = parseTag(tag).value;
772
890
 
773
891
  depth += selfClosing ? 0 : 1;
774
892
 
@@ -776,7 +894,7 @@ export const getCooked = (tags) => {
776
894
  if (flags.token) {
777
895
  break;
778
896
  } else {
779
- throw new Error(JSON.stringify(flags));
897
+ throw new Error(flags);
780
898
  }
781
899
  }
782
900
 
@@ -785,7 +903,7 @@ export const getCooked = (tags) => {
785
903
  }
786
904
 
787
905
  if (ref.type === '@') {
788
- const { cooked: cookedValue } = tag.value.attributes;
906
+ const { cooked: cookedValue } = attributes;
789
907
 
790
908
  if (!cookedValue) throw new Error('cannot cook string: it contains uncooked escapes');
791
909
 
@@ -807,7 +925,7 @@ export const getCooked = (tags) => {
807
925
 
808
926
  case LiteralTag: {
809
927
  if (depth === 1) {
810
- cooked += tag.value;
928
+ cooked += parseTag(tag).value;
811
929
  }
812
930
  break;
813
931
  }
@@ -830,13 +948,17 @@ export const printSource = (tags) => {
830
948
  if (!tags) return printed;
831
949
 
832
950
  for (const tag of tags) {
833
- if (tag.type === OpenNodeTag && tag.value.literalValue) {
834
- printed += tag.value.literalValue;
835
- } else if (tag.type === LiteralTag) {
836
- printed += tag.value;
837
- } else if (tag.type === ShiftTag) {
951
+ let tagType = parseTagType(tag);
952
+ if (tagType === OpenNodeTag) {
953
+ let { literalValue } = parseTag(tag).value;
954
+ if (literalValue) {
955
+ printed += literalValue;
956
+ }
957
+ } else if (tagType === LiteralTag) {
958
+ printed += parseTag(tag).value;
959
+ } else if (tagType === ShiftTag) {
838
960
  held = true;
839
- } else if (tag.type === GapTag) {
961
+ } else if (tagType === GapTag) {
840
962
  if (held) {
841
963
  held = false;
842
964
  } else {
@@ -850,12 +972,39 @@ export const printSource = (tags) => {
850
972
 
851
973
  export function* generateSourceTextFor(tags) {
852
974
  for (const tag of tags) {
853
- if (tag.type === LiteralTag) {
854
- yield* tag.value;
855
- } else if (tag.type === GapTag) {
975
+ let tagType = parseTagType(tag);
976
+ if (tagType === LiteralTag) {
977
+ yield* parseTag(tag).value;
978
+ } else if (tagType === GapTag) {
856
979
  yield null;
857
980
  }
858
981
  }
859
982
  }
860
983
 
861
984
  export const sourceTextFor = printSource;
985
+
986
+ export function* interpolateFragment(tags) {
987
+ let tags_ = isNode(tags) ? Tags.traverseInner(Tags.getTags(tags)) : tags;
988
+ let lastTag = null;
989
+ let first = true;
990
+ let dropFirst = false;
991
+ let depth = 0;
992
+ for (let tag of tags_) {
993
+ let tagType = parseTagType(tag);
994
+ if (lastTag) yield lastTag;
995
+
996
+ if (tagType === OpenNodeTag && !parseTag(tag).value.selfClosing) depth++;
997
+ if (tagType === CloseNodeTag) depth--;
998
+
999
+ dropFirst ||= first && tag === '<__>';
1000
+ lastTag = first && dropFirst ? null : tag;
1001
+ first = false;
1002
+ }
1003
+ if (dropFirst) {
1004
+ if (lastTag !== '</>') throw new Error();
1005
+ } else {
1006
+ yield lastTag;
1007
+ }
1008
+
1009
+ if (depth !== 0) throw new Error();
1010
+ }