@bablr/agast-helpers 0.8.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,160 +1,155 @@
1
1
  import { Coroutine } from '@bablr/coroutine';
2
2
  import emptyStack from '@iter-tools/imm-stack';
3
- import { printSelfClosingNodeTag, printTag } from './print.js';
4
- import { buildOpenNodeTag, buildTokenGroup } from './builders.js';
3
+ import { getStreamIterator, StreamIterable, StreamGenerator, wait } from '@bablr/stream-iterator';
4
+ import * as Tags from './tags.js';
5
+ import { printTag } from './print.js';
6
+ import {
7
+ buildChild,
8
+ buildGapTag,
9
+ buildNullTag,
10
+ buildOpenNodeTag,
11
+ buildProperty,
12
+ buildReference,
13
+ } from './builders.js';
5
14
  import {
6
- DoctypeTag,
7
15
  OpenNodeTag,
8
16
  CloseNodeTag,
9
17
  ReferenceTag,
10
- ShiftTag,
11
18
  GapTag,
12
19
  NullTag,
13
- InitializerTag,
14
20
  LiteralTag,
15
- TokenGroup,
16
21
  BindingTag,
22
+ DoctypeTag,
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 * from './print.js';
21
-
22
- export const getStreamIterator = (obj) => {
23
- return obj[Symbol.for('@@streamIterator')]?.() || obj[Symbol.iterator]?.();
24
- };
29
+ export { getStreamIterator, StreamIterable, StreamGenerator, wait };
25
30
 
26
- export class SyncGenerator {
27
- constructor(embeddedGenerator) {
28
- if (!embeddedGenerator.next) throw new Error();
31
+ export * from './print.js';
29
32
 
30
- this.generator = embeddedGenerator;
33
+ export const maybeWait = (maybePromise, callback) => {
34
+ if (maybePromise instanceof Promise) {
35
+ return maybePromise.then(callback);
36
+ } else {
37
+ return callback(maybePromise);
31
38
  }
39
+ };
32
40
 
33
- next(value) {
34
- const step = this.generator.next(value);
41
+ function* __flatMap(fn, tags) {
42
+ const co = new Coroutine(getStreamIterator(tags));
35
43
 
36
- if (step instanceof Promise) {
37
- throw new Error('invalid embedded generator');
38
- }
44
+ for (;;) {
45
+ co.advance();
39
46
 
40
- if (step.done) {
41
- return step;
42
- } else if (step.value instanceof Promise) {
43
- throw new Error('sync generators cannot resolve promises');
44
- } else {
45
- return step;
47
+ if (co.current instanceof Promise) {
48
+ co.current = yield wait(co.current);
46
49
  }
47
- }
50
+ if (co.done) break;
48
51
 
49
- return(value) {
50
- const step = this.generator.return(value);
51
- if (step instanceof Promise) {
52
- throw new Error('invalid embedded generator');
53
- }
52
+ let { value } = co;
54
53
 
55
- if (step.value instanceof Promise) {
56
- throw new Error('sync generators cannot resolve promises');
57
- }
58
- return step;
54
+ yield* fn(value);
59
55
  }
60
56
 
61
- [Symbol.iterator]() {
62
- return this;
63
- }
57
+ return true;
64
58
  }
65
59
 
66
- export class AsyncGenerator {
67
- constructor(embeddedGenerator) {
68
- this.generator = embeddedGenerator;
69
- }
60
+ export const flatMap = (fn, tags) => new StreamIterable(__flatMap(fn, tags));
70
61
 
71
- next(value) {
72
- const step = this.generator.next(value);
62
+ export const evaluateReturnSync = (iterable) => {
63
+ const co = new Coroutine(iterable[Symbol.iterator]());
64
+ while (!co.done) co.advance();
65
+ return co.value;
66
+ };
73
67
 
74
- if (step instanceof Promise) {
75
- throw new Error('invalid embedded generator');
76
- }
68
+ export const evaluateReturnAsync = async (iterable) => {
69
+ const co = new Coroutine(getStreamIterator(iterable));
70
+ while (!co.done) {
71
+ co.advance();
77
72
 
78
- if (step.done) {
79
- return Promise.resolve(step);
80
- } else if (step.value instanceof Promise) {
81
- return step.value.then((value) => {
82
- return this.next(value);
83
- });
84
- } else {
85
- return Promise.resolve(step);
73
+ if (co.current instanceof Promise) {
74
+ co.current = await co.current;
86
75
  }
87
76
  }
77
+ return co.value;
78
+ };
88
79
 
89
- return(value) {
90
- const result = this.generator.return(value);
91
- if (result instanceof Promise) {
92
- throw new Error('sync generators cannot resolve promises');
93
- }
94
- return result;
95
- }
80
+ function* __treeFromStream(tags, options) {
81
+ let path = null;
82
+ const co = new Coroutine(getStreamIterator(tags));
83
+ const expressionsCo = new Coroutine(getStreamIterator(options.expressions || []));
96
84
 
97
- [Symbol.asyncIterator]() {
98
- return this;
99
- }
100
- }
85
+ for (;;) {
86
+ co.advance();
101
87
 
102
- export class StreamGenerator {
103
- constructor(embeddedGenerator) {
104
- this.generator = embeddedGenerator;
105
- }
88
+ if (co.current instanceof Promise) {
89
+ co.current = yield wait(co.current);
90
+ }
106
91
 
107
- next(value) {
108
- const step = this.generator.next(value);
92
+ if (co.done) break;
109
93
 
110
- if (step.done) {
111
- return step;
112
- } else if (step.value instanceof Promise) {
113
- return step.value.then((value) => {
114
- return this.next(value);
115
- });
116
- } else {
117
- return step;
94
+ let tag = co.value;
95
+
96
+ if (tag.type === 'Effect' || tag.type === DoctypeTag) {
97
+ continue;
118
98
  }
119
- }
120
99
 
121
- return(value) {
122
- return this.generator.return(value);
123
- }
100
+ if (!path) {
101
+ path = Path.from(buildNode(Tags.fromValues([tag])));
124
102
 
125
- [Symbol.iterator]() {
126
- return this;
127
- }
103
+ // if (tag.value.selfClosing) {
104
+ // path = null;
105
+ // }
128
106
 
129
- [Symbol.for('@@streamIterator')]() {
130
- return this;
131
- }
132
- }
107
+ continue;
108
+ }
133
109
 
134
- export class StreamIterable {
135
- constructor(embeddedStreamIterable) {
136
- this.iterable = embeddedStreamIterable;
137
- }
110
+ if (tag.type === GapTag && !path.held) {
111
+ if (!expressionsCo.current || !expressionsCo.done) {
112
+ expressionsCo.advance();
113
+ }
138
114
 
139
- [Symbol.iterator]() {
140
- return new SyncGenerator(this.iterable);
141
- }
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;
142
130
 
143
- [Symbol.asyncIterator]() {
144
- return new AsyncGenerator(this.iterable);
131
+ path = path.advance(node);
132
+ }
133
+ } else {
134
+ path = path.advance(tag);
135
+ }
145
136
  }
146
137
 
147
- [Symbol.for('@@streamIterator')]() {
148
- return new StreamGenerator(this.iterable);
138
+ if (!path.done) {
139
+ throw new Error('imbalanced tag stack');
149
140
  }
141
+
142
+ return path?.node;
150
143
  }
151
144
 
152
- export const maybeWait = (maybePromise, callback) => {
153
- if (maybePromise instanceof Promise) {
154
- return maybePromise.then(callback);
155
- } else {
156
- return callback(maybePromise);
157
- }
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));
158
153
  };
159
154
 
160
155
  function* __isEmpty(tags) {
@@ -164,7 +159,7 @@ function* __isEmpty(tags) {
164
159
  co.advance();
165
160
 
166
161
  if (co.current instanceof Promise) {
167
- co.current = yield co.current;
162
+ co.current = yield wait(co.current);
168
163
  }
169
164
  if (co.done) break;
170
165
 
@@ -181,7 +176,9 @@ function* __isEmpty(tags) {
181
176
  case OpenNodeTag:
182
177
  ++depth;
183
178
 
184
- if (depth === 0 && ref.value.type === '@') {
179
+ if (tag.value.literalValue) return false;
180
+
181
+ if (depth === 0 && ref.type === '@') {
185
182
  return false;
186
183
  }
187
184
 
@@ -224,9 +221,9 @@ export const asyncStringFromStream = async (stream) => {
224
221
 
225
222
  if (co.done) break;
226
223
 
227
- const tag = co.value;
224
+ const chr = co.value;
228
225
 
229
- str += printTag(tag);
226
+ str += chr;
230
227
  }
231
228
 
232
229
  return str;
@@ -263,7 +260,7 @@ function* __generateCSTML(tags, options) {
263
260
  co.advance();
264
261
 
265
262
  if (co.current instanceof Promise) {
266
- co.current = yield co.current;
263
+ co.current = yield wait(co.current);
267
264
  }
268
265
  if (co.done) break;
269
266
 
@@ -277,17 +274,10 @@ function* __generateCSTML(tags, options) {
277
274
  continue;
278
275
  }
279
276
 
280
- if (tag.type === TokenGroup) {
281
- const intrinsicValue = getCooked(tag.value);
282
- yield* printSelfClosingNodeTag(tag.value[0], intrinsicValue);
283
- } else {
284
- yield* printTag(tag);
285
- }
277
+ yield* printTag(tag);
286
278
 
287
279
  prevTag = tag;
288
280
  }
289
-
290
- yield* '\n';
291
281
  }
292
282
 
293
283
  export const generateCSTML = (tags, options = {}) =>
@@ -305,24 +295,18 @@ function* __prettyGroupTags(tags) {
305
295
 
306
296
  const co = new Coroutine(getStreamIterator(tags));
307
297
 
308
- let ref = null;
309
-
310
298
  for (;;) {
311
299
  co.advance();
312
300
 
313
- if (co.done) break;
314
-
315
301
  if (co.current instanceof Promise) {
316
- co.current = yield co.current;
302
+ co.current = yield wait(co.current);
317
303
  }
318
304
 
305
+ if (co.done) break;
306
+
319
307
  const tag = co.value;
320
308
  const isOpenClose = tag.type === CloseNodeTag || tag.type === OpenNodeTag;
321
309
 
322
- if (tag.type === ReferenceTag) {
323
- ref = tag;
324
- }
325
-
326
310
  if (
327
311
  (tag.type === 'Effect' && tag.value.verb === 'write') ||
328
312
  [
@@ -331,11 +315,10 @@ function* __prettyGroupTags(tags) {
331
315
  BindingTag,
332
316
  GapTag,
333
317
  NullTag,
334
- InitializerTag,
335
318
  ShiftTag,
336
319
  AttributeDefinition,
337
- ].includes(tag.type) ||
338
- (tag.type === OpenNodeTag && (!tag.value.type || ref?.value.type === '@'))
320
+ OpenNodeTag,
321
+ ].includes(tag.type)
339
322
  ) {
340
323
  state.broken = true;
341
324
 
@@ -353,8 +336,14 @@ function* __prettyGroupTags(tags) {
353
336
 
354
337
  if (tag.type === CloseNodeTag) {
355
338
  if (!state.broken && (isToken(state.open) || state.holding.length === 1)) {
356
- state.holding.push(tag);
357
- yield buildTokenGroup(state.holding);
339
+ let { flags, name, attributes } = state.holding[0].value;
340
+
341
+ let literal = state.holding
342
+ .slice(1)
343
+ .map((lit) => lit.value)
344
+ .join('');
345
+
346
+ yield buildOpenNodeTag(flags, name, literal, attributes, true);
358
347
  } else {
359
348
  if (state.holding.length) {
360
349
  yield* state.holding;
@@ -367,13 +356,304 @@ function* __prettyGroupTags(tags) {
367
356
  }
368
357
 
369
358
  if (tag.type === OpenNodeTag) {
370
- states = states.push({ holding: [tag], broken: false, open: tag });
359
+ if (tag.value.selfClosing) {
360
+ yield tag;
361
+ } else {
362
+ states = states.push({ holding: [tag], broken: false, open: tag });
371
363
 
372
- state = states.value;
364
+ state = states.value;
365
+ }
373
366
  }
374
367
  }
375
368
  }
376
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
+
377
657
  function* __generatePrettyCSTML(tags, options) {
378
658
  let { indent = ' ', inline: inlineOption = true } = options;
379
659
 
@@ -382,7 +662,8 @@ function* __generatePrettyCSTML(tags, options) {
382
662
  return;
383
663
  }
384
664
 
385
- const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
665
+ const co = new Coroutine(getStreamIterator(prettyGroupTags(hoistTrivia(tags))));
666
+ // const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
386
667
  let indentLevel = 0;
387
668
  let first = true;
388
669
  let inline = false;
@@ -391,19 +672,19 @@ function* __generatePrettyCSTML(tags, options) {
391
672
  for (;;) {
392
673
  co.advance();
393
674
 
394
- if (co.done) break;
395
-
396
675
  if (co.current instanceof Promise) {
397
- co.current = yield co.current;
676
+ co.current = yield wait(co.current);
398
677
  }
399
678
 
679
+ if (co.done) break;
680
+
400
681
  const tag = co.value;
401
682
 
402
683
  if (tag.type === 'Effect') {
403
684
  continue;
404
685
  }
405
686
 
406
- if (tag.type === BindingTag && !tag.value.languagePath?.length) {
687
+ if (tag.type === BindingTag && !tag.value.segments?.length) {
407
688
  continue;
408
689
  }
409
690
 
@@ -414,8 +695,7 @@ function* __generatePrettyCSTML(tags, options) {
414
695
  (tag.type === NullTag ||
415
696
  tag.type === GapTag ||
416
697
  tag.type === BindingTag ||
417
- tag.type === InitializerTag ||
418
- tag.type === TokenGroup);
698
+ (tag.type === OpenNodeTag && tag.value.selfClosing));
419
699
 
420
700
  if (!first && !inline) {
421
701
  yield* '\n';
@@ -428,23 +708,12 @@ function* __generatePrettyCSTML(tags, options) {
428
708
  }
429
709
 
430
710
  if (!inline) {
431
- yield* indent.repeat(indentLevel);
711
+ yield* indent.repeat(Math.max(0, indentLevel));
432
712
  } else {
433
713
  yield* ' ';
434
714
  }
435
715
 
436
- if (tag.type === TokenGroup) {
437
- ref = null;
438
- const intrinsicValue = tag.value[0].value.flags.token ? getCooked(tag.value) : null;
439
- let openTag = tag.value[0];
440
- let { flags, type, attributes } = openTag.value;
441
- yield* printSelfClosingNodeTag(buildOpenNodeTag(flags, type, attributes), intrinsicValue);
442
- } else if (tag.type === OpenNodeTag) {
443
- let { flags, type, attributes } = tag.value;
444
- yield* printTag(buildOpenNodeTag(flags, type, attributes));
445
- } else {
446
- yield* printTag(tag);
447
- }
716
+ yield* printTag(tag);
448
717
 
449
718
  if (tag.type === ReferenceTag) {
450
719
  inline = true;
@@ -452,7 +721,7 @@ function* __generatePrettyCSTML(tags, options) {
452
721
  }
453
722
 
454
723
  if (tag.type === OpenNodeTag) {
455
- indentLevel++;
724
+ indentLevel += tag.value.selfClosing ? 0 : 1;
456
725
  }
457
726
 
458
727
  first = false;
@@ -494,9 +763,9 @@ export const getCooked = (tags) => {
494
763
  }
495
764
 
496
765
  case OpenNodeTag: {
497
- const { flags, attributes } = tag.value;
766
+ const { flags, attributes, literalValue, selfClosing } = tag.value;
498
767
 
499
- depth++;
768
+ depth += selfClosing ? 0 : 1;
500
769
 
501
770
  if (first) {
502
771
  if (flags.token) {
@@ -506,16 +775,18 @@ export const getCooked = (tags) => {
506
775
  }
507
776
  }
508
777
 
509
- if (!(ref.value.type === '#' || (ref.value.type === '@' && attributes.cooked))) {
778
+ if (!(ref.type === '#' || (ref.type === '@' && attributes.cooked))) {
510
779
  throw new Error('cookable nodes must not contain other nodes');
511
780
  }
512
781
 
513
- if (ref.value.type === '@') {
782
+ if (ref.type === '@') {
514
783
  const { cooked: cookedValue } = tag.value.attributes;
515
784
 
516
785
  if (!cookedValue) throw new Error('cannot cook string: it contains uncooked escapes');
517
786
 
518
787
  cooked += cookedValue;
788
+ } else if (literalValue) {
789
+ cooked += literalValue;
519
790
  }
520
791
 
521
792
  break;
@@ -553,7 +824,9 @@ export const printSource = (tags) => {
553
824
  if (!tags) return printed;
554
825
 
555
826
  for (const tag of tags) {
556
- if (tag.type === LiteralTag) {
827
+ if (tag.type === OpenNodeTag && tag.value.literalValue) {
828
+ printed += tag.value.literalValue;
829
+ } else if (tag.type === LiteralTag) {
557
830
  printed += tag.value;
558
831
  } else if (tag.type === GapTag) {
559
832
  throw new Error('use generateSourceTextFor');