@bablr/agast-helpers 0.3.2 → 0.5.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/btree.js +1 -0
- package/lib/builders.js +191 -123
- package/lib/path.js +16 -6
- package/lib/print.js +101 -54
- package/lib/shorthand.js +16 -16
- package/lib/stream.js +205 -103
- package/lib/symbols.js +18 -0
- package/lib/template.js +63 -24
- package/lib/tree.js +412 -182
- package/package.json +3 -1
package/lib/stream.js
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
1
|
import { Coroutine } from '@bablr/coroutine';
|
|
2
2
|
import emptyStack from '@iter-tools/imm-stack';
|
|
3
|
-
import { printSelfClosingNodeTag,
|
|
3
|
+
import { printSelfClosingNodeTag, printTag } from './print.js';
|
|
4
4
|
import { buildWriteEffect, buildTokenGroup } from './builders.js';
|
|
5
|
+
import {
|
|
6
|
+
DoctypeTag,
|
|
7
|
+
OpenNodeTag,
|
|
8
|
+
CloseNodeTag,
|
|
9
|
+
ReferenceTag,
|
|
10
|
+
ShiftTag,
|
|
11
|
+
GapTag,
|
|
12
|
+
NullTag,
|
|
13
|
+
ArrayTag,
|
|
14
|
+
LiteralTag,
|
|
15
|
+
EmbeddedExpression,
|
|
16
|
+
TokenGroup,
|
|
17
|
+
OpenFragmentTag,
|
|
18
|
+
CloseFragmentTag,
|
|
19
|
+
} from './symbols.js';
|
|
20
|
+
|
|
5
21
|
export * from './print.js';
|
|
6
22
|
|
|
7
23
|
const getEmbeddedExpression = (obj) => {
|
|
8
|
-
if (obj.type !==
|
|
24
|
+
if (obj.type !== EmbeddedExpression) throw new Error();
|
|
9
25
|
return obj.value;
|
|
10
26
|
};
|
|
11
27
|
|
|
@@ -89,12 +105,6 @@ export class AsyncGenerator {
|
|
|
89
105
|
}
|
|
90
106
|
}
|
|
91
107
|
|
|
92
|
-
export const isIntrinsicToken = (terminal) => {
|
|
93
|
-
return (
|
|
94
|
-
terminal.type === 'OpenNodeTag' && terminal.value.flags.intrinsic && terminal.value.flags.token
|
|
95
|
-
);
|
|
96
|
-
};
|
|
97
|
-
|
|
98
108
|
export class StreamGenerator {
|
|
99
109
|
constructor(embeddedGenerator) {
|
|
100
110
|
this.generator = embeddedGenerator;
|
|
@@ -149,8 +159,8 @@ export const maybeWait = (maybePromise, callback) => {
|
|
|
149
159
|
}
|
|
150
160
|
};
|
|
151
161
|
|
|
152
|
-
function*
|
|
153
|
-
const co = new Coroutine(getStreamIterator(
|
|
162
|
+
function* __isEmpty(tags) {
|
|
163
|
+
const co = new Coroutine(getStreamIterator(tags));
|
|
154
164
|
|
|
155
165
|
for (;;) {
|
|
156
166
|
co.advance();
|
|
@@ -160,10 +170,55 @@ function* __generateStandardOutput(terminals) {
|
|
|
160
170
|
}
|
|
161
171
|
if (co.done) break;
|
|
162
172
|
|
|
163
|
-
|
|
173
|
+
let depth = 0;
|
|
174
|
+
|
|
175
|
+
const tag = co.value;
|
|
176
|
+
|
|
177
|
+
switch (tag.type) {
|
|
178
|
+
case OpenFragmentTag:
|
|
179
|
+
case OpenNodeTag:
|
|
180
|
+
if (tag.value.flags.trivia) {
|
|
181
|
+
++depth;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (depth === 0 && tag.value.flags.escape) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
break;
|
|
189
|
+
|
|
190
|
+
case CloseFragmentTag:
|
|
191
|
+
case CloseNodeTag:
|
|
192
|
+
--depth;
|
|
193
|
+
break;
|
|
194
|
+
|
|
195
|
+
case LiteralTag:
|
|
196
|
+
case GapTag:
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return true;
|
|
202
|
+
}
|
|
164
203
|
|
|
165
|
-
|
|
166
|
-
|
|
204
|
+
export const isEmpty = (tags) =>
|
|
205
|
+
new StreamIterable(__isEmpty(tags))[Symbol.iterator]().next().value;
|
|
206
|
+
|
|
207
|
+
function* __generateStandardOutput(tags) {
|
|
208
|
+
const co = new Coroutine(getStreamIterator(tags));
|
|
209
|
+
|
|
210
|
+
for (;;) {
|
|
211
|
+
co.advance();
|
|
212
|
+
|
|
213
|
+
if (co.current instanceof Promise) {
|
|
214
|
+
co.current = yield co.current;
|
|
215
|
+
}
|
|
216
|
+
if (co.done) break;
|
|
217
|
+
|
|
218
|
+
const tag = co.value;
|
|
219
|
+
|
|
220
|
+
if (tag.type === 'Effect') {
|
|
221
|
+
const effect = tag.value;
|
|
167
222
|
if (effect.verb === 'write') {
|
|
168
223
|
const writeEffect = getEmbeddedExpression(effect.value);
|
|
169
224
|
if (writeEffect.stream == null || writeEffect.stream === 1) {
|
|
@@ -174,11 +229,10 @@ function* __generateStandardOutput(terminals) {
|
|
|
174
229
|
}
|
|
175
230
|
}
|
|
176
231
|
|
|
177
|
-
export const generateStandardOutput = (
|
|
178
|
-
new StreamIterable(__generateStandardOutput(terminals));
|
|
232
|
+
export const generateStandardOutput = (tags) => new StreamIterable(__generateStandardOutput(tags));
|
|
179
233
|
|
|
180
|
-
function* __generateAllOutput(
|
|
181
|
-
const co = new Coroutine(getStreamIterator(
|
|
234
|
+
function* __generateAllOutput(tags) {
|
|
235
|
+
const co = new Coroutine(getStreamIterator(tags));
|
|
182
236
|
|
|
183
237
|
let currentStream = null;
|
|
184
238
|
|
|
@@ -190,10 +244,10 @@ function* __generateAllOutput(terminals) {
|
|
|
190
244
|
}
|
|
191
245
|
if (co.done) break;
|
|
192
246
|
|
|
193
|
-
const
|
|
247
|
+
const tag = co.value;
|
|
194
248
|
|
|
195
|
-
if (
|
|
196
|
-
const effect =
|
|
249
|
+
if (tag.type === 'Effect') {
|
|
250
|
+
const effect = tag.value;
|
|
197
251
|
if (effect.verb === 'write') {
|
|
198
252
|
const writeEffect = getEmbeddedExpression(effect.value);
|
|
199
253
|
const prevStream = currentStream;
|
|
@@ -207,10 +261,10 @@ function* __generateAllOutput(terminals) {
|
|
|
207
261
|
}
|
|
208
262
|
}
|
|
209
263
|
|
|
210
|
-
export const generateAllOutput = (
|
|
264
|
+
export const generateAllOutput = (tags) => new StreamIterable(__generateAllOutput(tags));
|
|
211
265
|
|
|
212
|
-
export const printCSTML = (
|
|
213
|
-
return stringFromStream(generateStandardOutput(generateCSTMLStrategy(
|
|
266
|
+
export const printCSTML = (tags) => {
|
|
267
|
+
return stringFromStream(generateStandardOutput(generateCSTMLStrategy(tags)));
|
|
214
268
|
};
|
|
215
269
|
|
|
216
270
|
function* __emptyStreamIterator() {}
|
|
@@ -230,9 +284,9 @@ export const asyncStringFromStream = async (stream) => {
|
|
|
230
284
|
|
|
231
285
|
if (co.done) break;
|
|
232
286
|
|
|
233
|
-
const
|
|
287
|
+
const tag = co.value;
|
|
234
288
|
|
|
235
|
-
str +=
|
|
289
|
+
str += printTag(tag);
|
|
236
290
|
}
|
|
237
291
|
|
|
238
292
|
return str;
|
|
@@ -255,17 +309,17 @@ export const stringFromStream = (stream) => {
|
|
|
255
309
|
return str;
|
|
256
310
|
};
|
|
257
311
|
|
|
258
|
-
function* __generateCSTMLStrategy(
|
|
312
|
+
function* __generateCSTMLStrategy(tags, options) {
|
|
259
313
|
let { emitEffects = false, inline: inlineOption = true } = options;
|
|
260
314
|
|
|
261
|
-
if (!
|
|
315
|
+
if (!tags) {
|
|
262
316
|
yield buildWriteEffect('<//>');
|
|
263
317
|
return;
|
|
264
318
|
}
|
|
265
319
|
|
|
266
|
-
let
|
|
320
|
+
let prevTag = null;
|
|
267
321
|
|
|
268
|
-
const co = new Coroutine(getStreamIterator(prettyGroupTokens(
|
|
322
|
+
const co = new Coroutine(getStreamIterator(prettyGroupTokens(tags)));
|
|
269
323
|
|
|
270
324
|
for (;;) {
|
|
271
325
|
co.advance();
|
|
@@ -275,43 +329,47 @@ function* __generateCSTMLStrategy(terminals, options) {
|
|
|
275
329
|
}
|
|
276
330
|
if (co.done) break;
|
|
277
331
|
|
|
278
|
-
const
|
|
332
|
+
const tag = co.value;
|
|
279
333
|
|
|
280
|
-
if (
|
|
334
|
+
if (tag.type === ReferenceTag && prevTag.type === NullTag) {
|
|
281
335
|
yield buildWriteEffect(' ');
|
|
282
336
|
}
|
|
283
337
|
|
|
284
|
-
if (
|
|
285
|
-
const effect =
|
|
338
|
+
if (tag.type === 'Effect') {
|
|
339
|
+
const effect = tag.value;
|
|
286
340
|
if (emitEffects && effect.verb === 'write') {
|
|
287
341
|
yield buildWriteEffect(effect.value.text, effect.value.options);
|
|
288
342
|
}
|
|
289
343
|
continue;
|
|
290
344
|
}
|
|
291
345
|
|
|
292
|
-
if (
|
|
293
|
-
const intrinsicValue = getCooked(
|
|
294
|
-
yield buildWriteEffect(printSelfClosingNodeTag(
|
|
346
|
+
if (tag.type === TokenGroup) {
|
|
347
|
+
const intrinsicValue = getCooked(tag.value);
|
|
348
|
+
yield buildWriteEffect(printSelfClosingNodeTag(tag.value[0], intrinsicValue));
|
|
295
349
|
} else {
|
|
296
|
-
yield buildWriteEffect(
|
|
350
|
+
yield buildWriteEffect(printTag(tag));
|
|
297
351
|
}
|
|
298
352
|
|
|
299
|
-
|
|
353
|
+
prevTag = tag;
|
|
300
354
|
}
|
|
301
355
|
|
|
302
356
|
yield buildWriteEffect('\n');
|
|
303
357
|
}
|
|
304
358
|
|
|
305
|
-
export const generateCSTMLStrategy = (
|
|
306
|
-
new StreamIterable(__generateCSTMLStrategy(
|
|
359
|
+
export const generateCSTMLStrategy = (tags, options = {}) =>
|
|
360
|
+
new StreamIterable(__generateCSTMLStrategy(tags, options));
|
|
361
|
+
|
|
362
|
+
const isToken = (tag) => {
|
|
363
|
+
return tag.value.flags.token;
|
|
364
|
+
};
|
|
307
365
|
|
|
308
|
-
export const prettyGroupTokens = (
|
|
366
|
+
export const prettyGroupTokens = (tags) => new StreamIterable(__prettyGroupTokens(tags));
|
|
309
367
|
|
|
310
|
-
function* __prettyGroupTokens(
|
|
368
|
+
function* __prettyGroupTokens(tags) {
|
|
311
369
|
let states = emptyStack.push({ holding: [], broken: false, open: null });
|
|
312
370
|
let state = states.value;
|
|
313
371
|
|
|
314
|
-
const co = new Coroutine(getStreamIterator(
|
|
372
|
+
const co = new Coroutine(getStreamIterator(tags));
|
|
315
373
|
|
|
316
374
|
for (;;) {
|
|
317
375
|
co.advance();
|
|
@@ -322,18 +380,19 @@ function* __prettyGroupTokens(terminals) {
|
|
|
322
380
|
co.current = yield co.current;
|
|
323
381
|
}
|
|
324
382
|
|
|
325
|
-
const
|
|
383
|
+
const tag = co.value;
|
|
326
384
|
const isOpenClose =
|
|
327
|
-
|
|
385
|
+
tag.type === CloseNodeTag ||
|
|
386
|
+
tag.type === OpenNodeTag ||
|
|
387
|
+
tag.type === CloseFragmentTag ||
|
|
388
|
+
tag.type === OpenFragmentTag;
|
|
328
389
|
|
|
329
390
|
if (
|
|
330
|
-
(
|
|
331
|
-
[
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
(terminal.type === 'Literal' ||
|
|
336
|
-
(terminal.type === 'OpenNodeTag' && terminal.value.flags.escape)))
|
|
391
|
+
(tag.type === 'Effect' && tag.value.verb === 'write') ||
|
|
392
|
+
[ReferenceTag, DoctypeTag, GapTag, NullTag, ArrayTag, ShiftTag, OpenFragmentTag].includes(
|
|
393
|
+
tag.type,
|
|
394
|
+
) ||
|
|
395
|
+
(tag.type === OpenNodeTag && tag.value.flags.escape)
|
|
337
396
|
) {
|
|
338
397
|
state.broken = true;
|
|
339
398
|
|
|
@@ -341,52 +400,55 @@ function* __prettyGroupTokens(terminals) {
|
|
|
341
400
|
yield* state.holding;
|
|
342
401
|
state.holding = [];
|
|
343
402
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
yield terminal;
|
|
347
|
-
}
|
|
348
|
-
} else if (!isOpenClose && terminal.type !== 'Effect') {
|
|
349
|
-
state.holding.push(terminal);
|
|
403
|
+
} else if (tag.type === LiteralTag) {
|
|
404
|
+
state.holding.push(tag);
|
|
350
405
|
}
|
|
351
406
|
|
|
352
|
-
if (
|
|
353
|
-
yield
|
|
407
|
+
if (!state.holding.length && !isOpenClose) {
|
|
408
|
+
yield tag;
|
|
354
409
|
}
|
|
355
410
|
|
|
356
|
-
if (
|
|
357
|
-
if (!state.broken && (
|
|
358
|
-
state.holding.push(
|
|
411
|
+
if (tag.type === CloseNodeTag || tag.type === CloseFragmentTag) {
|
|
412
|
+
if (!state.broken && (isToken(state.open) || state.holding.length === 1)) {
|
|
413
|
+
state.holding.push(tag);
|
|
359
414
|
yield buildTokenGroup(state.holding);
|
|
360
415
|
} else {
|
|
361
416
|
if (state.holding.length) {
|
|
362
417
|
yield* state.holding;
|
|
363
418
|
}
|
|
364
|
-
yield
|
|
419
|
+
yield tag;
|
|
365
420
|
}
|
|
366
421
|
|
|
367
422
|
states = states.pop();
|
|
368
423
|
state = states.value;
|
|
369
424
|
}
|
|
370
425
|
|
|
371
|
-
if (
|
|
372
|
-
|
|
426
|
+
if (tag.type === OpenNodeTag || tag.type === OpenFragmentTag) {
|
|
427
|
+
if (tag.type === OpenFragmentTag) {
|
|
428
|
+
states = states.push({ holding: [], broken: false, open: tag });
|
|
429
|
+
yield tag;
|
|
430
|
+
} else {
|
|
431
|
+
states = states.push({ holding: [tag], broken: false, open: tag });
|
|
432
|
+
}
|
|
433
|
+
|
|
373
434
|
state = states.value;
|
|
374
435
|
}
|
|
375
436
|
}
|
|
376
437
|
}
|
|
377
438
|
|
|
378
|
-
function* __generatePrettyCSTMLStrategy(
|
|
439
|
+
function* __generatePrettyCSTMLStrategy(tags, options) {
|
|
379
440
|
let { indent = ' ', emitEffects = false, inline: inlineOption = true } = options;
|
|
380
441
|
|
|
381
|
-
if (!
|
|
442
|
+
if (!tags) {
|
|
382
443
|
yield buildWriteEffect('<//>');
|
|
383
444
|
return;
|
|
384
445
|
}
|
|
385
446
|
|
|
386
|
-
const co = new Coroutine(getStreamIterator(prettyGroupTokens(
|
|
447
|
+
const co = new Coroutine(getStreamIterator(prettyGroupTokens(tags)));
|
|
387
448
|
let indentLevel = 0;
|
|
388
449
|
let first = true;
|
|
389
450
|
let inline = false;
|
|
451
|
+
let ref = null;
|
|
390
452
|
|
|
391
453
|
for (;;) {
|
|
392
454
|
co.advance();
|
|
@@ -397,10 +459,10 @@ function* __generatePrettyCSTMLStrategy(terminals, options) {
|
|
|
397
459
|
co.current = yield co.current;
|
|
398
460
|
}
|
|
399
461
|
|
|
400
|
-
const
|
|
462
|
+
const tag = co.value;
|
|
401
463
|
|
|
402
|
-
if (
|
|
403
|
-
const effect =
|
|
464
|
+
if (tag.type === 'Effect') {
|
|
465
|
+
const effect = tag.value;
|
|
404
466
|
if (emitEffects && effect.verb === 'write') {
|
|
405
467
|
const writeEffect = getEmbeddedExpression(effect.value);
|
|
406
468
|
yield buildWriteEffect(
|
|
@@ -417,13 +479,18 @@ function* __generatePrettyCSTMLStrategy(terminals, options) {
|
|
|
417
479
|
inline =
|
|
418
480
|
inlineOption &&
|
|
419
481
|
inline &&
|
|
420
|
-
|
|
482
|
+
ref &&
|
|
483
|
+
(tag.type === NullTag ||
|
|
484
|
+
tag.type === GapTag ||
|
|
485
|
+
tag.type === ArrayTag ||
|
|
486
|
+
tag.type === TokenGroup);
|
|
421
487
|
|
|
422
488
|
if (!first && !inline) {
|
|
423
489
|
yield buildWriteEffect('\n');
|
|
424
490
|
}
|
|
425
491
|
|
|
426
|
-
if (
|
|
492
|
+
if (tag.type === CloseNodeTag || tag.type === CloseFragmentTag) {
|
|
493
|
+
ref = null;
|
|
427
494
|
if (indentLevel === 0) {
|
|
428
495
|
throw new Error('imbalanced tag stack');
|
|
429
496
|
}
|
|
@@ -437,18 +504,20 @@ function* __generatePrettyCSTMLStrategy(terminals, options) {
|
|
|
437
504
|
yield buildWriteEffect(' ');
|
|
438
505
|
}
|
|
439
506
|
|
|
440
|
-
if (
|
|
441
|
-
|
|
442
|
-
|
|
507
|
+
if (tag.type === TokenGroup) {
|
|
508
|
+
ref = null;
|
|
509
|
+
const intrinsicValue = tag.value[0].value.flags.token ? getCooked(tag.value) : null;
|
|
510
|
+
yield buildWriteEffect(printSelfClosingNodeTag(tag.value[0], intrinsicValue));
|
|
443
511
|
} else {
|
|
444
|
-
yield buildWriteEffect(
|
|
512
|
+
yield buildWriteEffect(printTag(tag));
|
|
445
513
|
}
|
|
446
514
|
|
|
447
|
-
if (
|
|
515
|
+
if (tag.type === ReferenceTag) {
|
|
448
516
|
inline = true;
|
|
517
|
+
ref = tag;
|
|
449
518
|
}
|
|
450
519
|
|
|
451
|
-
if (
|
|
520
|
+
if (tag.type === OpenNodeTag || tag.type === OpenFragmentTag) {
|
|
452
521
|
indentLevel++;
|
|
453
522
|
}
|
|
454
523
|
|
|
@@ -458,32 +527,52 @@ function* __generatePrettyCSTMLStrategy(terminals, options) {
|
|
|
458
527
|
yield buildWriteEffect('\n');
|
|
459
528
|
}
|
|
460
529
|
|
|
461
|
-
export const generatePrettyCSTMLStrategy = (
|
|
462
|
-
return new StreamIterable(__generatePrettyCSTMLStrategy(
|
|
530
|
+
export const generatePrettyCSTMLStrategy = (tags, options = {}) => {
|
|
531
|
+
return new StreamIterable(__generatePrettyCSTMLStrategy(tags, options));
|
|
463
532
|
};
|
|
464
533
|
|
|
465
|
-
export const printPrettyCSTML = (
|
|
466
|
-
return stringFromStream(generateStandardOutput(generatePrettyCSTMLStrategy(
|
|
534
|
+
export const printPrettyCSTML = (tags, options = {}) => {
|
|
535
|
+
return stringFromStream(generateStandardOutput(generatePrettyCSTMLStrategy(tags, options)));
|
|
467
536
|
};
|
|
468
537
|
|
|
469
|
-
export const getCooked = (
|
|
538
|
+
export const getCooked = (tags) => {
|
|
470
539
|
let cooked = '';
|
|
471
540
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
541
|
+
let first = true;
|
|
542
|
+
let foundLast = false;
|
|
543
|
+
let depth = 0;
|
|
544
|
+
|
|
545
|
+
for (const tag of tags) {
|
|
546
|
+
if (foundLast) throw new Error();
|
|
547
|
+
|
|
548
|
+
switch (tag.type) {
|
|
549
|
+
case ReferenceTag: {
|
|
550
|
+
if (depth === 1) {
|
|
551
|
+
throw new Error('cookable nodes must not contain other nodes');
|
|
552
|
+
}
|
|
553
|
+
break;
|
|
476
554
|
}
|
|
477
555
|
|
|
478
|
-
case
|
|
479
|
-
|
|
556
|
+
case OpenFragmentTag:
|
|
557
|
+
case OpenNodeTag: {
|
|
558
|
+
const { flags, attributes } = tag.value;
|
|
559
|
+
|
|
560
|
+
depth++;
|
|
561
|
+
|
|
562
|
+
if (first) {
|
|
563
|
+
if (flags.token) {
|
|
564
|
+
break;
|
|
565
|
+
} else {
|
|
566
|
+
throw new Error(JSON.stringify(flags));
|
|
567
|
+
}
|
|
568
|
+
}
|
|
480
569
|
|
|
481
570
|
if (!(flags.trivia || (flags.escape && attributes.cooked))) {
|
|
482
571
|
throw new Error('cookable nodes must not contain other nodes');
|
|
483
572
|
}
|
|
484
573
|
|
|
485
574
|
if (flags.escape) {
|
|
486
|
-
const { cooked: cookedValue } =
|
|
575
|
+
const { cooked: cookedValue } = tag.value.attributes;
|
|
487
576
|
|
|
488
577
|
if (!cookedValue) throw new Error('cannot cook string: it contains uncooked escapes');
|
|
489
578
|
|
|
@@ -493,8 +582,19 @@ export const getCooked = (terminals) => {
|
|
|
493
582
|
break;
|
|
494
583
|
}
|
|
495
584
|
|
|
496
|
-
case
|
|
497
|
-
|
|
585
|
+
case CloseFragmentTag:
|
|
586
|
+
case CloseNodeTag: {
|
|
587
|
+
if (depth === 1) {
|
|
588
|
+
foundLast = true;
|
|
589
|
+
}
|
|
590
|
+
depth--;
|
|
591
|
+
break;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
case LiteralTag: {
|
|
595
|
+
if (depth === 1) {
|
|
596
|
+
cooked += tag.value;
|
|
597
|
+
}
|
|
498
598
|
break;
|
|
499
599
|
}
|
|
500
600
|
|
|
@@ -502,29 +602,31 @@ export const getCooked = (terminals) => {
|
|
|
502
602
|
throw new Error();
|
|
503
603
|
}
|
|
504
604
|
}
|
|
605
|
+
|
|
606
|
+
first = false;
|
|
505
607
|
}
|
|
506
608
|
|
|
507
609
|
return cooked;
|
|
508
610
|
};
|
|
509
611
|
|
|
510
|
-
export const printSource = (
|
|
612
|
+
export const printSource = (tags) => {
|
|
511
613
|
let printed = '';
|
|
512
614
|
|
|
513
|
-
if (!
|
|
615
|
+
if (!tags) return printed;
|
|
514
616
|
|
|
515
|
-
for (const
|
|
516
|
-
if (
|
|
517
|
-
printed +=
|
|
617
|
+
for (const tag of tags) {
|
|
618
|
+
if (tag.type === LiteralTag) {
|
|
619
|
+
printed += tag.value;
|
|
518
620
|
}
|
|
519
621
|
}
|
|
520
622
|
|
|
521
623
|
return printed;
|
|
522
624
|
};
|
|
523
625
|
|
|
524
|
-
export function* generateSourceTextFor(
|
|
525
|
-
for (const
|
|
526
|
-
if (
|
|
527
|
-
yield*
|
|
626
|
+
export function* generateSourceTextFor(tags) {
|
|
627
|
+
for (const tag of tags) {
|
|
628
|
+
if (tag.type === LiteralTag) {
|
|
629
|
+
yield* tag.value;
|
|
528
630
|
}
|
|
529
631
|
}
|
|
530
632
|
}
|
package/lib/symbols.js
CHANGED
|
@@ -6,4 +6,22 @@ export const fragment = Symbol.for('@bablr/fragment');
|
|
|
6
6
|
|
|
7
7
|
export const null_ = Symbol.for('@bablr/null');
|
|
8
8
|
|
|
9
|
+
export const DoctypeTag = Symbol.for('DoctypeTag');
|
|
10
|
+
export const OpenNodeTag = Symbol.for('OpenNodeTag');
|
|
11
|
+
export const CloseNodeTag = Symbol.for('CloseNodeTag');
|
|
12
|
+
export const OpenFragmentTag = Symbol.for('OpenFragmentTag');
|
|
13
|
+
export const CloseFragmentTag = Symbol.for('CloseFragmentTag');
|
|
14
|
+
export const ReferenceTag = Symbol.for('ReferenceTag');
|
|
15
|
+
export const ShiftTag = Symbol.for('ShiftTag');
|
|
16
|
+
export const GapTag = Symbol.for('GapTag');
|
|
17
|
+
export const NullTag = Symbol.for('NullTag');
|
|
18
|
+
export const ArrayTag = Symbol.for('ArrayTag');
|
|
19
|
+
export const LiteralTag = Symbol.for('LiteralTag');
|
|
20
|
+
|
|
21
|
+
export const EmbeddedNode = Symbol.for('EmbeddedNode');
|
|
22
|
+
export const EmbeddedTag = Symbol.for('EmbeddedTag');
|
|
23
|
+
export const EmbeddedExpression = Symbol.for('EmbeddedExpression');
|
|
24
|
+
|
|
25
|
+
export const TokenGroup = Symbol.for('TokenGroup');
|
|
26
|
+
|
|
9
27
|
export { null_ as null };
|
package/lib/template.js
CHANGED
|
@@ -1,54 +1,93 @@
|
|
|
1
1
|
import * as t from './builders.js';
|
|
2
|
+
import {
|
|
3
|
+
LiteralTag,
|
|
4
|
+
EmbeddedNode,
|
|
5
|
+
ReferenceTag,
|
|
6
|
+
ArrayTag,
|
|
7
|
+
OpenFragmentTag,
|
|
8
|
+
OpenNodeTag,
|
|
9
|
+
DoctypeTag,
|
|
10
|
+
CloseFragmentTag,
|
|
11
|
+
} from './symbols.js';
|
|
12
|
+
import * as btree from './btree.js';
|
|
13
|
+
import { getOpenTag, getRoot } from './tree.js';
|
|
2
14
|
|
|
3
15
|
const { isArray } = Array;
|
|
4
16
|
const { freeze } = Object;
|
|
17
|
+
const { isFinite } = Number;
|
|
18
|
+
|
|
19
|
+
export const interpolateArray = (fragment) => {
|
|
20
|
+
const value = getRoot(fragment);
|
|
5
21
|
|
|
6
|
-
export const interpolateArray = (value) => {
|
|
7
22
|
if (isArray(value)) {
|
|
8
|
-
|
|
23
|
+
if (isFinite(value[0])) {
|
|
24
|
+
return [...btree.traverse(value)];
|
|
25
|
+
} else {
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
9
28
|
} else {
|
|
10
29
|
return [value];
|
|
11
30
|
}
|
|
12
31
|
};
|
|
13
32
|
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
let
|
|
19
|
-
for (
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
|
|
33
|
+
export function* interpolateFragmentChildren(value, ref) {
|
|
34
|
+
const open = getOpenTag(value);
|
|
35
|
+
|
|
36
|
+
if (open.type === OpenFragmentTag) {
|
|
37
|
+
let currentRef = null;
|
|
38
|
+
for (let child of btree.traverse(value.children)) {
|
|
39
|
+
if (
|
|
40
|
+
child.type === DoctypeTag ||
|
|
41
|
+
child.type === OpenFragmentTag ||
|
|
42
|
+
child.type === CloseFragmentTag
|
|
43
|
+
) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (child.type === ArrayTag) {
|
|
48
|
+
// if (notAlreadyInitialized) {
|
|
49
|
+
yield child;
|
|
50
|
+
// }
|
|
51
|
+
} else if (child.type === ReferenceTag) {
|
|
52
|
+
currentRef = child;
|
|
53
|
+
if (child.value.name === '.') {
|
|
54
|
+
yield freeze({ ...ref });
|
|
55
|
+
} else {
|
|
56
|
+
yield child;
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
yield child;
|
|
60
|
+
}
|
|
23
61
|
}
|
|
24
|
-
|
|
62
|
+
} else if (open.type === OpenNodeTag) {
|
|
63
|
+
yield freeze({ ...ref });
|
|
25
64
|
} else {
|
|
26
|
-
|
|
65
|
+
throw new Error();
|
|
27
66
|
}
|
|
28
|
-
}
|
|
67
|
+
}
|
|
29
68
|
|
|
30
|
-
const
|
|
31
|
-
if (!
|
|
32
|
-
throw new Error('Invalid
|
|
69
|
+
const validateTag = (tag) => {
|
|
70
|
+
if (!tag || (tag.type !== LiteralTag && tag.type !== EmbeddedNode)) {
|
|
71
|
+
throw new Error('Invalid tag');
|
|
33
72
|
}
|
|
34
|
-
if (
|
|
73
|
+
if (tag.type === EmbeddedNode && !tag.value.flags.escape) {
|
|
35
74
|
throw new Error();
|
|
36
75
|
}
|
|
37
76
|
};
|
|
38
77
|
|
|
39
78
|
export const interpolateString = (value) => {
|
|
40
|
-
const
|
|
79
|
+
const tags = [];
|
|
41
80
|
if (isArray(value)) {
|
|
42
81
|
for (const element of value) {
|
|
43
|
-
|
|
82
|
+
validateTag(element);
|
|
44
83
|
|
|
45
|
-
|
|
84
|
+
tags.push(element);
|
|
46
85
|
}
|
|
47
86
|
} else {
|
|
48
87
|
// we can't safely interpolate strings here, though I wish we could
|
|
49
|
-
|
|
50
|
-
|
|
88
|
+
validateTag(value);
|
|
89
|
+
tags.push(value);
|
|
51
90
|
}
|
|
52
91
|
|
|
53
|
-
return t.buildNode('String', 'Content',
|
|
92
|
+
return t.buildNode('String', 'Content', tags);
|
|
54
93
|
};
|