@bablr/helpers 0.20.2 → 0.20.3

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.
@@ -0,0 +1,721 @@
1
+ // import { i } from '@bablr/boot/shorthand.macro';
2
+ import { interpolateFragmentChildren, interpolateString } from '@bablr/agast-helpers/template';
3
+ import { getRoot, isNull } from '@bablr/agast-helpers/tree';
4
+ import { buildLiteralTag as agastBuildLiteralTag } from '@bablr/agast-helpers/builders';
5
+ import * as t from '@bablr/agast-helpers/shorthand';
6
+ import * as btree from '@bablr/agast-helpers/btree';
7
+ import * as l from '@bablr/agast-vm-helpers/languages';
8
+
9
+ const { getPrototypeOf, freeze } = Object;
10
+ const { isArray } = Array;
11
+
12
+ const when = (condition, value) => (condition ? value : { *[Symbol.iterator]() {} });
13
+
14
+ const isString = (val) => typeof val === 'string';
15
+ const isBoolean = (val) => typeof val === 'boolean';
16
+
17
+ function* repeat(times, ...values) {
18
+ for (let i = 0; i < times; i++) for (const value of values) yield value;
19
+ }
20
+
21
+ function* concat(...iterables) {
22
+ for (const iterable of iterables) yield* iterable;
23
+ }
24
+
25
+ export const buildSeparatedListChildren = (list, ref, sep) => {
26
+ const children = [];
27
+ let first = true;
28
+ for (const _ of list) {
29
+ if (!first && sep) {
30
+ children.push(freeze({ ...sep }));
31
+ }
32
+ children.push(freeze({ ...ref }));
33
+ first = false;
34
+ }
35
+ return children;
36
+ };
37
+
38
+ export const buildReferenceTag = (name, isArray, hasGap) => {
39
+ return t.node(
40
+ l.CSTML,
41
+ 'ReferenceTag',
42
+ btree.fromValues(
43
+ concat(
44
+ [t.ref`name`],
45
+ when(isArray, [t.ref`arrayOperatorToken`]),
46
+ when(hasGap, [t.ref`hasGapToken`]),
47
+ [t.ref`sigilToken`],
48
+ ),
49
+ ),
50
+ {
51
+ name: buildIdentifier(name),
52
+ arrayOperatorToken: isArray ? t.s_node(l.CSTML, 'Punctuator', '[]') : t.null_node(),
53
+ hasGapToken: hasGap ? t.s_node(l.CSTML, 'Punctuator', '$') : t.null_node(),
54
+ sigilToken: t.s_node(l.CSTML, 'Punctuator', ':'),
55
+ },
56
+ );
57
+ };
58
+
59
+ export const buildGapTag = () => {
60
+ return t.node(l.CSTML, 'GapTag', [t.ref`sigilToken`], {
61
+ sigilToken: t.s_node(l.CSTML, 'Punctuator', '<//>'),
62
+ });
63
+ };
64
+
65
+ export const buildShiftTag = () => {
66
+ return t.node(l.CSTML, 'ShiftTag', [t.ref`sigilToken`], {
67
+ sigilToken: t.s_node(l.CSTML, 'Punctuator', '^^^'),
68
+ });
69
+ };
70
+
71
+ export const buildFlags = (flags = {}) => {
72
+ const { token = null, escape = null, trivia = null, expression = null, hasGap = null } = flags;
73
+
74
+ if ((trivia && escape) || (expression && (trivia || escape))) {
75
+ throw new Error('invalid flags');
76
+ }
77
+
78
+ return t.node(
79
+ l.CSTML,
80
+ 'Flags',
81
+ btree.fromValues(
82
+ concat(
83
+ when(trivia, [t.ref`triviaToken`]),
84
+ when(token, [t.ref`tokenToken`]),
85
+ when(escape, [t.ref`escapeToken`]),
86
+ when(expression, [t.ref`expressionToken`]),
87
+ when(hasGap, [t.ref`hasGapToken`]),
88
+ ),
89
+ ),
90
+ {
91
+ triviaToken: trivia ? t.s_node(l.CSTML, 'Punctuator', '#') : t.null_node(),
92
+ tokenToken: token ? t.s_node(l.CSTML, 'Punctuator', '*') : t.null_node(),
93
+ escapeToken: escape ? t.s_node(l.CSTML, 'Punctuator', '@') : t.null_node(),
94
+ expressionToken: expression ? t.s_node(l.CSTML, 'Punctuator', '+') : t.null_node(),
95
+ hasGapToken: hasGap ? t.s_node(l.CSTML, 'Punctuator', '$') : t.null_node(),
96
+ },
97
+ );
98
+ };
99
+
100
+ export const buildSpamMatcher = (type = null, value = null, attributes = {}) => {
101
+ return buildFullyQualifiedSpamMatcher({}, null, type, value, attributes);
102
+ };
103
+
104
+ export const buildFullyQualifiedSpamMatcher = (
105
+ flags,
106
+ language,
107
+ type,
108
+ intrinsicValue,
109
+ attributes = {},
110
+ ) => {
111
+ const attributes_ = buildAttributes(attributes);
112
+
113
+ let language_;
114
+
115
+ if (isString(language)) {
116
+ language_ = language;
117
+ } else {
118
+ let lArr = isString(language) ? language : language ? [...language] : [];
119
+
120
+ language_ = lArr.length === 0 ? null : lArr;
121
+ }
122
+
123
+ return t.node(l.Spamex, 'NodeMatcher', [t.ref`open`], {
124
+ open: t.node(
125
+ l.Spamex,
126
+ 'NodeMatcher',
127
+ btree.fromValues(
128
+ concat(
129
+ [t.ref`openToken`, t.ref`flags`],
130
+ when(language_, [t.ref`language`, t.ref`languageSeparator`]),
131
+ when(type, [t.ref`type`]),
132
+ when(intrinsicValue, [t.embedded(buildSpace()), t.ref`intrinsicValue`]),
133
+ when(attributes_.length, [t.embedded(buildSpace())]),
134
+ interpolateFragmentChildren(attributes_, t.ref`attributes[]`),
135
+ when(!type, [t.embedded(buildSpace())]),
136
+ [t.ref`selfClosingTagToken`, t.ref`closeToken`],
137
+ ),
138
+ ),
139
+ {
140
+ openToken: t.s_node(l.CSTML, 'Punctuator', '<'),
141
+ flags: buildFlags(flags),
142
+ language: language_ ? buildLanguage(language_) : t.null_node(),
143
+ languageSeparator: language_ && type ? t.s_node(l.CSTML, 'Punctuator', ':') : t.null_node(),
144
+ type: type ? buildIdentifier(type) : t.null_node(),
145
+ intrinsicValue: intrinsicValue ? buildString(intrinsicValue) : t.null_node(),
146
+ attributes: attributes_.properties['.'],
147
+ selfClosingTagToken: t.s_node(l.CSTML, 'Punctuator', '/'),
148
+ closeToken: t.s_node(l.CSTML, 'Punctuator', '>'),
149
+ },
150
+ ),
151
+ });
152
+ };
153
+
154
+ export const buildNodeOpenTag = (flags, language, type = null, attributes = {}) => {
155
+ const attributes_ = buildAttributes(attributes);
156
+
157
+ let language_ = !language || language.length === 0 ? null : language;
158
+
159
+ return t.node(
160
+ l.CSTML,
161
+ 'OpenNodeTag',
162
+ btree.fromValues(
163
+ concat(
164
+ [t.ref`openToken`, t.ref`flags`],
165
+ when(language_, [t.ref`language`, t.ref`languageSeparator`]),
166
+ when(type, [t.ref`type`]),
167
+ when(attributes_.length, [t.embedded(buildSpace())]),
168
+ interpolateFragmentChildren(attributes_, t.ref`attributes[]`),
169
+ [t.ref`closeToken`],
170
+ ),
171
+ ),
172
+ {
173
+ openToken: t.s_node(l.CSTML, 'Punctuator', '<'),
174
+ flags: buildFlags(flags),
175
+ language: language_ && type ? buildLanguage(language_) : t.null_node(),
176
+ languageSeparator: language_ && type ? t.s_node(l.CSTML, 'Punctuator', ':') : t.null_node(),
177
+ type: type ? buildIdentifier(type) : t.null_node(),
178
+ attributes: attributes_.properties['.'],
179
+ closeToken: t.s_node(l.CSTML, 'Punctuator', '>'),
180
+ },
181
+ );
182
+ };
183
+
184
+ export const buildDoctypeTag = (attributes) => {
185
+ const attributes_ = buildAttributes(attributes);
186
+
187
+ return t.node(
188
+ l.CSTML,
189
+ 'DoctypeTag',
190
+ btree.fromValues(
191
+ concat(
192
+ [t.ref`openToken`, t.ref`version`, t.ref`versionSeparator`, t.ref`doctype`],
193
+ when(attributes_.length, [t.embedded(buildSpace())]),
194
+ interpolateFragmentChildren(attributes_, t.ref`attributes[]`),
195
+ [t.ref`closeToken`],
196
+ ),
197
+ ),
198
+ {
199
+ openToken: t.s_node(l.CSTML, 'Punctuator', '<!'),
200
+ version: t.s_node(l.CSTML, 'PositiveInteger', '0'),
201
+ versionSeparator: t.s_node(l.CSTML, 'Punctuator', ':'),
202
+ doctype: t.s_node(l.CSTML, 'Keyword', 'cstml'),
203
+ attributes: attributes_.properties['.'],
204
+ closeToken: t.s_node(l.CSTML, 'Punctuator', '>'),
205
+ },
206
+ );
207
+ };
208
+
209
+ export const buildIdentifierPath = (path) => {
210
+ const path_ = isString(path) ? [path] : [...path];
211
+ const segments = path_.map((name) => buildIdentifier(name));
212
+ const separators = path_.slice(0, -1).map((_) => t.s_node(l.CSTML, 'Punctuator', '.'));
213
+
214
+ if (!path_.length) {
215
+ return null;
216
+ }
217
+
218
+ return t.node(
219
+ l.CSTML,
220
+ 'IdentifierPath',
221
+ btree.fromValues(
222
+ concat(
223
+ [t.ref`segments[]`, t.arr()],
224
+ repeat(segments.length - 1, t.ref`segments[]`, t.ref`separators[]`),
225
+ [t.ref`segments[]`],
226
+ ),
227
+ ),
228
+ {
229
+ segments,
230
+ separators,
231
+ },
232
+ );
233
+ };
234
+
235
+ export const buildLanguage = (language) => {
236
+ return language && isString(language) && language.startsWith('https://')
237
+ ? buildString(language)
238
+ : buildIdentifierPath(language);
239
+ };
240
+
241
+ export const buildNodeCloseTag = (type, language) => {
242
+ return t.node(
243
+ l.CSTML,
244
+ 'CloseNodeTag',
245
+ btree.fromValues(
246
+ concat(
247
+ [t.ref`openToken`],
248
+ when(language, [t.ref`language`]),
249
+ when(type && language, [t.ref`languageSeparator`]),
250
+ when(type, [t.ref`type`]),
251
+ [t.ref`closeToken`],
252
+ ),
253
+ ),
254
+ {
255
+ openToken: t.s_node(l.CSTML, 'Punctuator', '</'),
256
+ language: language ? buildLanguage(language) : t.null_node(),
257
+ languageSeparator: language && type ? t.s_node(l.CSTML, 'Punctuator', ':') : t.null_node(),
258
+ type: type ? buildIdentifier(type) : t.null_node(),
259
+ closeToken: t.s_node(l.CSTML, 'Punctuator', '>'),
260
+ },
261
+ );
262
+ };
263
+
264
+ export const buildLiteralTag = (value) => {
265
+ return t.node(l.CSTML, 'LiteralTag', [t.ref`value`], { value });
266
+ };
267
+
268
+ export const buildTerminalProps = (matcher) => {
269
+ const { attributes, value } = matcher.properties;
270
+
271
+ return buildObject({ value, attributes });
272
+ };
273
+
274
+ export const buildSpace = () => {
275
+ return t.t_node(l.Comment, null, [t.embedded(t.t_node(l.Space, 'Space', [t.lit(' ')]))]);
276
+ };
277
+
278
+ export const buildIdentifier = (name) => {
279
+ return t.s_node(l.Instruction, 'Identifier', name);
280
+ };
281
+
282
+ export const buildKeyword = (name) => {
283
+ return t.s_node(l.Instruction, 'Identifier', name);
284
+ };
285
+
286
+ export const buildCall = (verb, ...args) => {
287
+ return t.node(l.Instruction, 'Call', [t.ref`verb`, t.ref`arguments`], {
288
+ verb: buildIdentifier(verb),
289
+ arguments: buildTuple(args),
290
+ });
291
+ };
292
+
293
+ export const buildProperty = (key, value) => {
294
+ return t.node(
295
+ l.Instruction,
296
+ 'Property',
297
+ btree.from(t.ref`key`, t.ref`mapOperator`, t.embedded(buildSpace()), t.ref`value`),
298
+ {
299
+ key: buildIdentifier(key),
300
+ mapOperator: t.s_node(l.Instruction, 'Punctuator', ':'),
301
+ value: buildExpression(value),
302
+ },
303
+ );
304
+ };
305
+
306
+ const escapables = {
307
+ '\r': 'r',
308
+ '\n': 'n',
309
+ '\t': 't',
310
+ '\0': '0',
311
+ };
312
+
313
+ export const buildDigit = (value) => {
314
+ return t.s_node(l.CSTML, 'Digit', value);
315
+ };
316
+
317
+ export const buildInteger = (value, base = 10) => {
318
+ const digits = value.toString(base).split('');
319
+
320
+ return t.node(
321
+ l.CSTML,
322
+ 'Integer',
323
+ btree.fromValues(
324
+ concat(
325
+ [t.ref`digits[]`, t.arr()],
326
+ digits.map(() => t.ref`digits[]`),
327
+ ),
328
+ ),
329
+ {
330
+ digits: digits.map((digit) => buildDigit(digit)),
331
+ },
332
+ );
333
+ };
334
+
335
+ export const buildInfinity = (value) => {
336
+ let sign;
337
+ if (value === Infinity) {
338
+ sign = '+';
339
+ } else if (value === -Infinity) {
340
+ sign = '-';
341
+ } else {
342
+ throw new Error();
343
+ }
344
+
345
+ return t.node(l.CSTML, 'Infinity', [t.ref`sign`, t.ref`value`], {
346
+ sign: t.s_node(l.CSTML, 'Punctuator', sign),
347
+ value: t.s_node(l.CSTML, 'Keyword', 'Infinity'),
348
+ });
349
+ };
350
+
351
+ export const buildNumber = (value) => {
352
+ if (Number.isFinite(value)) {
353
+ return buildInteger(value);
354
+ } else {
355
+ return buildInfinity(value);
356
+ }
357
+ };
358
+
359
+ export const buildString = (value) => {
360
+ const pieces = isArray(value) ? value : [value];
361
+ const tags = [];
362
+ let lit = '';
363
+
364
+ if (pieces.length === 1 && pieces[0] === "'") {
365
+ return t.node(
366
+ l.CSTML,
367
+ 'String',
368
+ btree.from(t.ref`openToken`, t.ref`content`, t.ref`closeToken`),
369
+ {
370
+ openToken: t.s_node(l.CSTML, 'Punctuator', '"'),
371
+ content: interpolateString(agastBuildLiteralTag(value)),
372
+ closeToken: t.s_node(l.CSTML, 'Punctuator', '"'),
373
+ },
374
+ );
375
+ }
376
+
377
+ for (const piece of pieces) {
378
+ if (isString(piece)) {
379
+ const value = piece;
380
+
381
+ for (const chr of value) {
382
+ if (
383
+ chr === '\\' ||
384
+ chr === "'" ||
385
+ chr === '\n' ||
386
+ chr === '\r' ||
387
+ chr === '\t' ||
388
+ chr === '\0' ||
389
+ chr.charCodeAt(0) < 32
390
+ ) {
391
+ if (lit) {
392
+ tags.push(agastBuildLiteralTag(lit));
393
+ lit = '';
394
+ }
395
+
396
+ let value;
397
+
398
+ if (escapables[chr]) {
399
+ value = t.node(l.CSTML, 'EscapeCode', [t.ref`sigilToken`], {
400
+ sigilToken: buildKeyword(escapables[chr]),
401
+ digits: t.null_node(),
402
+ });
403
+ } else if (chr.charCodeAt(0) < 32) {
404
+ const hexDigits = chr.charCodeAt(0).toString(16).padStart(4, '0');
405
+ value = t.node(
406
+ l.CSTML,
407
+ 'EscapeCode',
408
+ btree.fromValues(
409
+ concat(
410
+ [t.ref`sigilToken`, t.ref`digits[]`, t.arr()],
411
+ [...hexDigits].map((d) => t.ref`digits[]`),
412
+ ),
413
+ ),
414
+ {
415
+ sigilToken: buildKeyword('u'),
416
+ digits: [...hexDigits].map((digit) => buildDigit(digit)),
417
+ },
418
+ );
419
+ } else {
420
+ value = buildKeyword(chr);
421
+ }
422
+
423
+ tags.push(
424
+ t.buildEmbeddedNode(
425
+ t.e_node(
426
+ l.CSTML,
427
+ 'EscapeSequence',
428
+ [t.ref`escape`, t.ref`value`],
429
+ {
430
+ escape: t.s_node(l.CSTML, 'Punctuator', '\\'),
431
+ value,
432
+ },
433
+ { cooked: chr },
434
+ ),
435
+ ),
436
+ );
437
+ } else {
438
+ lit += chr;
439
+ }
440
+ }
441
+ } else {
442
+ tags.push(agastBuildLiteralTag(lit));
443
+ lit = '';
444
+
445
+ if (piece == null) {
446
+ throw new Error('not implemented');
447
+ } else if (isString(piece.type)) {
448
+ tags.push(piece);
449
+ } else {
450
+ throw new Error();
451
+ }
452
+ }
453
+ }
454
+
455
+ if (lit) tags.push(agastBuildLiteralTag(lit));
456
+ lit = '';
457
+
458
+ return t.node(
459
+ l.CSTML,
460
+ 'String',
461
+ btree.from(t.ref`openToken`, t.ref`content`, t.ref`closeToken`),
462
+ {
463
+ openToken: t.s_node(l.CSTML, 'Punctuator', "'"),
464
+ content: interpolateString(tags),
465
+ closeToken: t.s_node(l.CSTML, 'Punctuator', "'"),
466
+ },
467
+ );
468
+ };
469
+
470
+ export const buildBoolean = (value) => {
471
+ return t.node(l.Instruction, 'Boolean', [t.ref`sigilToken`], {
472
+ sigilToken: t.s_node(l.Instruction, 'Keyword', value ? 'true' : 'false'),
473
+ });
474
+ };
475
+
476
+ export const buildNullTag = () => {
477
+ return t.node(l.Instruction, 'Null', [t.ref`sigilToken`], {
478
+ sigilToken: t.s_node(l.Instruction, 'Keyword', 'null'),
479
+ });
480
+ };
481
+
482
+ export const buildArray = (elements) => {
483
+ const elements_ = buildArrayElements(elements);
484
+ return t.node(
485
+ l.Instruction,
486
+ 'Array',
487
+ btree.fromValues(
488
+ concat([t.ref`openToken`], interpolateFragmentChildren(elements_, t.ref`elements[]`), [
489
+ t.ref`closeToken`,
490
+ ]),
491
+ ),
492
+ {
493
+ openToken: t.s_node(l.Instruction, 'Punctuator', '['),
494
+ elements: elements_.properties['.'],
495
+ closeToken: t.s_node(l.Instruction, 'Punctuator', ']'),
496
+ },
497
+ );
498
+ };
499
+
500
+ export const buildArrayElements = (values) => {
501
+ return buildSpaceSeparatedList(values.map((value) => buildExpression(value)));
502
+ };
503
+
504
+ export const buildTupleValues = buildArrayElements;
505
+
506
+ export const buildTuple = (values) => {
507
+ const values_ = buildTupleValues(values);
508
+ return t.node(
509
+ l.Instruction,
510
+ 'Tuple',
511
+ btree.fromValues(
512
+ concat([t.ref`openToken`], interpolateFragmentChildren(values_, t.ref`values[]`), [
513
+ t.ref`closeToken`,
514
+ ]),
515
+ ),
516
+ {
517
+ openToken: t.s_node(l.Instruction, 'Punctuator', '('),
518
+ values: values_.properties['.'],
519
+ closeToken: t.s_node(l.Instruction, 'Punctuator', ')'),
520
+ },
521
+ );
522
+ };
523
+
524
+ export const buildSpaceSeparatedList = (values) => {
525
+ return t.frag(
526
+ btree.fromValues(
527
+ concat(
528
+ [t.ref`.`, t.arr()],
529
+ buildSeparatedListChildren(values, t.ref`.`, t.embedded(buildSpace())),
530
+ ),
531
+ ),
532
+ {
533
+ ['.']: values,
534
+ },
535
+ );
536
+ };
537
+
538
+ export const buildObjectProperties = (properties) => {
539
+ return buildSpaceSeparatedList(
540
+ Object.entries(properties).map(({ 0: key, 1: value }) => buildProperty(key, value)),
541
+ );
542
+ };
543
+
544
+ export const buildObject = (properties) => {
545
+ const properties_ = buildObjectProperties(properties);
546
+ return t.node(
547
+ l.Instruction,
548
+ 'Object',
549
+ btree.fromValues(
550
+ concat([t.ref`openToken`], interpolateFragmentChildren(properties_, t.ref`properties[]`), [
551
+ t.ref`closeToken`,
552
+ ]),
553
+ ),
554
+ {
555
+ openToken: t.s_node(l.Instruction, 'Punctuator', '{'),
556
+ properties: properties_.properties['.'],
557
+ closeToken: t.s_node(l.Instruction, 'Punctuator', '}'),
558
+ },
559
+ {},
560
+ );
561
+ };
562
+
563
+ export const buildMappingAttribute = (key, value) => {
564
+ return t.node(
565
+ l.CSTML,
566
+ 'MappingAttribute',
567
+ btree.from(t.ref`key`, t.ref`mapOperator`, t.ref`value`),
568
+ {
569
+ key: buildIdentifier(key),
570
+ mapOperator: t.s_node(l.CSTML, 'Punctuator', '='),
571
+ value: buildExpression(value),
572
+ },
573
+ );
574
+ };
575
+
576
+ export const buildBooleanAttribute = (key, value) => {
577
+ return t.node(l.CSTML, 'BooleanAttribute', [...when(!value, [t.ref`negateToken`]), t.ref`key`], {
578
+ negateToken: !value ? t.s_node(l.CSTML, 'Puncutator', '!') : t.null_node(),
579
+ key: buildIdentifier(key),
580
+ });
581
+ };
582
+
583
+ export const buildAttribute = (key, value) => {
584
+ return isBoolean(value) ? buildBooleanAttribute(key, value) : buildMappingAttribute(key, value);
585
+ };
586
+
587
+ export const buildPattern = (alternatives, flags) => {
588
+ return t.node(
589
+ l.Regex,
590
+ 'Pattern',
591
+ btree.fromValues(
592
+ concat([t.ref`openToken`], interpolateFragmentChildren(alternatives, t.ref`alternatives[]`), [
593
+ t.ref`closeToken`,
594
+ t.ref`flags`,
595
+ ]),
596
+ ),
597
+ {
598
+ openToken: t.s_node(l.Regex, 'Punctuator', '/'),
599
+ separators: alternatives.properties.separators,
600
+ alternatives: alternatives.properties['.'],
601
+ closeToken: t.s_node(l.Regex, 'Punctuator', '/'),
602
+ flags: buildFlags(flags),
603
+ },
604
+ );
605
+ };
606
+
607
+ export const buildAlternative = (elements) => {
608
+ const elementsArray = getRoot(elements);
609
+ return t.node(
610
+ l.Regex,
611
+ 'Alternative',
612
+ btree.fromValues(interpolateFragmentChildren(elements, t.ref`elements[]`), {
613
+ elements: elementsArray,
614
+ }),
615
+ { elements: elementsArray },
616
+ );
617
+ };
618
+
619
+ export const buildAlternatives = (alternatives = {}) => {
620
+ return t.frag(
621
+ btree.fromValues(
622
+ concat(
623
+ [t.ref`.[]`, t.arr(), t.ref`separators[]`, t.arr()],
624
+ buildSeparatedListChildren(alternatives, t.ref`.[]`, t.ref`separators[]`),
625
+ ),
626
+ ),
627
+ {
628
+ ['.']: alternatives,
629
+ separators: alternatives.slice(0, -1).map((alt) => t.s_node(l.Regex, 'Punctuator', '|')),
630
+ },
631
+ );
632
+ };
633
+
634
+ export const buildElements = (elements) => {
635
+ return t.frag(
636
+ btree.fromValues(
637
+ concat(
638
+ [t.ref`.[]`, t.arr()],
639
+ elements.map((el) => t.ref`.[]`),
640
+ ),
641
+ ),
642
+ {
643
+ ['.']: elements,
644
+ },
645
+ );
646
+ };
647
+
648
+ export const buildExpression = (expr) => {
649
+ if (isNull(expr)) return buildNullTag();
650
+
651
+ switch (typeof expr) {
652
+ case 'boolean':
653
+ return buildBoolean(expr);
654
+ case 'string':
655
+ return buildString(expr);
656
+ case 'number':
657
+ return buildInteger(expr);
658
+ case 'object': {
659
+ switch (getPrototypeOf(expr)) {
660
+ case Array.prototype:
661
+ return buildArray(expr);
662
+ case Object.prototype:
663
+ if (expr.type && expr.language && expr.children && expr.properties) {
664
+ return expr;
665
+ }
666
+ return buildObject(expr);
667
+ default:
668
+ throw new Error();
669
+ }
670
+ }
671
+ default:
672
+ throw new Error();
673
+ }
674
+ };
675
+
676
+ export const buildAttributes = (attributes = {}) => {
677
+ const attributes_ = Object.entries(attributes).map(({ 0: key, 1: value }) =>
678
+ buildAttribute(key, value),
679
+ );
680
+
681
+ return t.frag(
682
+ btree.fromValues(
683
+ [t.ref`.[]`, t.arr()],
684
+ buildSeparatedListChildren(attributes_, t.ref`.[]`, t.embedded(buildSpace())),
685
+ ),
686
+ {
687
+ ['.']: attributes_,
688
+ },
689
+ );
690
+ };
691
+
692
+ export const buildNodeMatcher = (flags, language, type, attributes) => {
693
+ let language_ = !language || language.length === 0 ? null : language;
694
+
695
+ const flags_ = buildFlags(flags);
696
+
697
+ return t.node(
698
+ l.Spamex,
699
+ 'NodeMatcher',
700
+ btree.fromValues(
701
+ concat(
702
+ [t.ref`openToken`],
703
+ when(flags_, [t.ref`flags`]),
704
+ when(language_, [t.ref`language`, t.ref`languageSeparator`]),
705
+ [t.ref`type`],
706
+ when(attributes.length, [t.embedded(buildSpace())]),
707
+ [...btree.traverse(attributes.children)].slice(1, -1),
708
+ [t.ref`closeToken`],
709
+ ),
710
+ ),
711
+ {
712
+ openToken: t.s_node(l.CSTML, 'Punctuator', '<'),
713
+ language: buildLanguage(language_),
714
+ languageSeparator: language_ && type ? t.s_node(l.CSTML, 'Punctuator', ':') : t.null_node(),
715
+ flags: flags_,
716
+ type: buildIdentifier(type),
717
+ attributes: attributes.properties.attributes,
718
+ closeToken: t.s_node(l.CSTML, 'Punctuator', '>'),
719
+ },
720
+ );
721
+ };
package/lib/decorators.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { printType } from '@bablr/agast-helpers/print';
1
2
  import * as sym from './symbols.js';
2
3
 
3
4
  export const AllowEmpty = (desc, context) => {
@@ -12,23 +13,26 @@ export const AllowEmpty = (desc, context) => {
12
13
  });
13
14
  };
14
15
 
15
- export const CoveredBy = (type) => (desc, context) => {
16
- context.addInitializer(function () {
17
- let covers = this.covers;
16
+ export const CoveredBy = (type) => {
17
+ if (!/^[a-zA-Z]+$/.test(printType(type))) throw new Error();
18
+ return (desc, context) => {
19
+ context.addInitializer(function () {
20
+ let covers = this.covers;
18
21
 
19
- if (!covers) {
20
- covers = this.covers = new Map();
21
- }
22
+ if (!covers) {
23
+ covers = this.covers = new Map();
24
+ }
22
25
 
23
- let coveredTypes = covers.get(type);
26
+ let coveredTypes = covers.get(type);
24
27
 
25
- if (!coveredTypes) {
26
- coveredTypes = new Set();
27
- covers.set(type, coveredTypes);
28
- }
28
+ if (!coveredTypes) {
29
+ coveredTypes = new Set();
30
+ covers.set(type, coveredTypes);
31
+ }
29
32
 
30
- coveredTypes.add(context.name);
31
- });
33
+ coveredTypes.add(context.name);
34
+ });
35
+ };
32
36
  };
33
37
 
34
38
  export const InjectFrom = (obj) => (_stub, context) => {
package/lib/index.js ADDED
@@ -0,0 +1,12 @@
1
+ export * as builders from './builders.js';
2
+ export * as decorators from './decorators.js';
3
+ export * as enhancers from './enhancers.js';
4
+ export * as grammar from './grammar.js';
5
+ export * as object from './object.js';
6
+ export * as productions from './productions.js';
7
+ export * as shorthand from './shorthand.js';
8
+ export * as source from './source.js';
9
+ export * as stream from './stream.js';
10
+ export * as symbols from './symbols.js';
11
+ export * as tree from './tree.js';
12
+ export * as trivia from './trivia.js';
@@ -19,7 +19,7 @@ export function* List({
19
19
  } = ctx.unbox(props);
20
20
  yield _t.node(_l.Instruction, "Call", [_t.ref`verb`, _t.ref`arguments`], {
21
21
  verb: _t.s_node(_l.Instruction, "Identifier", "eat"),
22
- arguments: _t.node(_l.Instruction, "Tuple", [_t.ref`openToken`, _t.ref`values[]`, _t.arr(), _t.ref`values[]`, _t.embedded(_t.node(_l.Instruction, "Space", [_t.lit(" ")], {}, {})), _t.ref`values[]`, _t.ref`closeToken`], {
22
+ arguments: _t.node(_l.Instruction, "Tuple", [9, [[_t.ref`openToken`, _t.ref`values[]`, _t.arr(), _t.ref`values[]`], [_t.ref`separators[]`, _t.arr(), _t.ref`separators[]`, _t.ref`values[]`, _t.ref`closeToken`]]], {
23
23
  openToken: _t.s_node(_l.Instruction, "Punctuator", "("),
24
24
  values: [_t.node(_l.Instruction, "Array", [_t.ref`openToken`, _t.ref`closeToken`], {
25
25
  openToken: _t.s_node(_l.Instruction, "Punctuator", "["),
@@ -29,6 +29,7 @@ export function* List({
29
29
  content: _t.node(_l.CSTML, "Content", [_t.lit("separators[]")], {}, {}),
30
30
  closeToken: _t.s_node(_l.CSTML, "Punctuator", "'")
31
31
  }, {})],
32
+ separators: [_t.node(_l.Instruction, "Separator", [_t.embedded(_t.node(_l.Instruction, "Space", [_t.lit(" ")], {}, {}))], {}, {})],
32
33
  closeToken: _t.s_node(_l.Instruction, "Punctuator", ")")
33
34
  }, {})
34
35
  }, {});
@@ -47,13 +48,14 @@ export function* List({
47
48
  if (it || allowTrailingSeparator) {
48
49
  sep = yield _t.node(_l.Instruction, "Call", [_t.ref`verb`, _t.ref`arguments`], {
49
50
  verb: _t.s_node(_l.Instruction, "Identifier", "eatMatch"),
50
- arguments: _t.node(_l.Instruction, "Tuple", [_t.ref`openToken`, _t.ref`values[]`, _t.arr(), ..._interpolateFragmentChildren(separator, _t.ref`values[]`), _t.embedded(_t.node(_l.Instruction, "Space", [_t.lit(" ")], {}, {})), _t.ref`values[]`, _t.ref`closeToken`], {
51
+ arguments: _t.node(_l.Instruction, "Tuple", [9, [[_t.ref`openToken`, _t.ref`values[]`, _t.arr(), ..._interpolateFragmentChildren(separator, _t.ref`values[]`)], [_t.ref`separators[]`, _t.arr(), _t.ref`separators[]`, _t.ref`values[]`, _t.ref`closeToken`]]], {
51
52
  openToken: _t.s_node(_l.Instruction, "Punctuator", "("),
52
53
  values: [..._interpolateArray(separator), _t.node(_l.CSTML, "String", [_t.ref`openToken`, _t.ref`content`, _t.ref`closeToken`], {
53
54
  openToken: _t.s_node(_l.CSTML, "Punctuator", "'"),
54
55
  content: _t.node(_l.CSTML, "Content", [_t.lit("separators[]")], {}, {}),
55
56
  closeToken: _t.s_node(_l.CSTML, "Punctuator", "'")
56
57
  }, {})],
58
+ separators: [_t.node(_l.Instruction, "Separator", [_t.embedded(_t.node(_l.Instruction, "Space", [_t.lit(" ")], {}, {}))], {}, {})],
57
59
  closeToken: _t.s_node(_l.Instruction, "Punctuator", ")")
58
60
  }, {})
59
61
  }, {});
package/lib/shorthand.js CHANGED
@@ -1 +1,3 @@
1
- export { i, re, spam } from '@bablr/boot';
1
+ import boot from '@bablr/boot';
2
+
3
+ export const { i, re, spam } = boot;
package/package.json CHANGED
@@ -1,25 +1,24 @@
1
1
  {
2
2
  "name": "@bablr/helpers",
3
3
  "description": "Command helpers for use in writing BABLR grammars",
4
- "version": "0.20.2",
4
+ "version": "0.20.3",
5
5
  "author": "Conrad Buck<conartist6@gmail.com>",
6
6
  "type": "module",
7
- "files": [
8
- "lib"
9
- ],
7
+ "files": ["lib"],
10
8
  "exports": {
9
+ "./builders": "./lib/builders.js",
11
10
  "./decorators": "./lib/decorators.js",
12
11
  "./enhancers": "./lib/enhancers.js",
13
12
  "./grammar": "./lib/grammar.js",
14
13
  "./object": "./lib/object.js",
15
- "./path": "./lib/path.js",
16
14
  "./productions": "./lib/productions.js",
17
15
  "./shorthand": "./lib/shorthand.js",
18
16
  "./source": "./lib/source.js",
19
17
  "./stream": "./lib/stream.js",
20
18
  "./symbols": "./lib/symbols.js",
21
19
  "./tree": "./lib/tree.js",
22
- "./trivia": "./lib/trivia.js"
20
+ "./trivia": "./lib/trivia.js",
21
+ ".": "./lib/index.js"
23
22
  },
24
23
  "sideEffects": false,
25
24
  "scripts": {