@bablr/agast-vm-helpers 0.4.0 → 0.5.1

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.
Files changed (3) hide show
  1. package/lib/builders.js +243 -145
  2. package/lib/index.js +90 -16
  3. package/package.json +2 -2
package/lib/builders.js CHANGED
@@ -1,11 +1,12 @@
1
1
  // import { i } from '@bablr/boot/shorthand.macro';
2
- import { interpolateArrayChildren, interpolateString } from '@bablr/agast-helpers/template';
2
+ import { interpolateFragmentChildren, interpolateString } from '@bablr/agast-helpers/template';
3
3
  import { isNull } from '@bablr/agast-helpers/tree';
4
4
  import { buildLiteralTag as agastBuildLiteralTag } from '@bablr/agast-helpers/builders';
5
5
  import * as t from '@bablr/agast-helpers/shorthand';
6
+ import * as btree from '@bablr/agast-helpers/btree';
6
7
  import * as l from './languages.js';
7
8
 
8
- const { getPrototypeOf } = Object;
9
+ const { getPrototypeOf, freeze } = Object;
9
10
  const { isArray } = Array;
10
11
 
11
12
  const when = (condition, value) => (condition ? value : { *[Symbol.iterator]() {} });
@@ -17,13 +18,39 @@ function* repeat(times, ...values) {
17
18
  for (let i = 0; i < times; i++) for (const value of values) yield value;
18
19
  }
19
20
 
20
- export const buildReferenceTag = (name, isArray) => {
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) => {
21
39
  return t.node(
22
40
  l.CSTML,
23
41
  'ReferenceTag',
24
- [t.ref`name`, ...when(isArray, [t.ref`arrayOperatorToken`]), t.ref`sigilToken`],
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
+ ),
25
50
  {
26
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(),
27
54
  sigilToken: t.s_node(l.CSTML, 'Punctuator', ':'),
28
55
  },
29
56
  );
@@ -42,7 +69,7 @@ export const buildShiftTag = () => {
42
69
  };
43
70
 
44
71
  export const buildFlags = (flags = {}) => {
45
- const { intrinsic = null, token = null, escape = null, trivia = null, expression = null } = flags;
72
+ const { token = null, escape = null, trivia = null, expression = null, hasGap = null } = flags;
46
73
 
47
74
  if ((trivia && escape) || (expression && (trivia || escape))) {
48
75
  throw new Error('invalid flags');
@@ -51,19 +78,21 @@ export const buildFlags = (flags = {}) => {
51
78
  return t.node(
52
79
  l.CSTML,
53
80
  'Flags',
54
- [
55
- ...when(trivia, [t.ref`triviaToken`]),
56
- ...when(intrinsic, [t.ref`intrinsicToken`]),
57
- ...when(token, [t.ref`tokenToken`]),
58
- ...when(escape, [t.ref`escapeToken`]),
59
- ...when(expression, [t.ref`expressionToken`]),
60
- ],
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
+ ),
61
90
  {
62
91
  triviaToken: trivia ? t.s_node(l.CSTML, 'Punctuator', '#') : t.null_node(),
63
- intrinsicToken: intrinsic ? t.s_node(l.CSTML, 'Punctuator', '~') : t.null_node(),
64
92
  tokenToken: token ? t.s_node(l.CSTML, 'Punctuator', '*') : t.null_node(),
65
93
  escapeToken: escape ? t.s_node(l.CSTML, 'Punctuator', '@') : t.null_node(),
66
94
  expressionToken: expression ? t.s_node(l.CSTML, 'Punctuator', '+') : t.null_node(),
95
+ hasGapToken: hasGap ? t.s_node(l.CSTML, 'Punctuator', '$') : t.null_node(),
67
96
  },
68
97
  );
69
98
  };
@@ -95,20 +124,18 @@ export const buildFullyQualifiedSpamMatcher = (
95
124
  open: t.node(
96
125
  l.Spamex,
97
126
  'NodeMatcher',
98
- [
99
- t.ref`openToken`,
100
- t.ref`flags`,
101
- ...when(language_, [t.ref`language`, t.ref`languageSeparator`]),
102
- ...when(type, [t.ref`type`]),
103
- ...when(intrinsicValue, [t.embedded(buildSpace()), t.ref`intrinsicValue`]),
104
- ...when(attributes_.length, [t.embedded(buildSpace())]),
105
- t.ref`attributes[]`,
106
- t.arr(),
107
- ...interpolateArrayChildren(attributes_, t.ref`attributes[]`, t.embedded(buildSpace())),
108
- ...when(!type, [t.embedded(buildSpace())]),
109
- t.ref`selfClosingTagToken`,
110
- t.ref`closeToken`,
111
- ],
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
+ ),
112
139
  {
113
140
  openToken: t.s_node(l.CSTML, 'Punctuator', '<'),
114
141
  flags: buildFlags(flags),
@@ -116,7 +143,7 @@ export const buildFullyQualifiedSpamMatcher = (
116
143
  languageSeparator: language_ && type ? t.s_node(l.CSTML, 'Punctuator', ':') : t.null_node(),
117
144
  type: type ? buildIdentifier(type) : t.null_node(),
118
145
  intrinsicValue: intrinsicValue ? buildString(intrinsicValue) : t.null_node(),
119
- attributes: attributes_,
146
+ attributes: attributes_.properties['.'],
120
147
  selfClosingTagToken: t.s_node(l.CSTML, 'Punctuator', '/'),
121
148
  closeToken: t.s_node(l.CSTML, 'Punctuator', '>'),
122
149
  },
@@ -132,24 +159,23 @@ export const buildNodeOpenTag = (flags, language, type = null, attributes = {})
132
159
  return t.node(
133
160
  l.CSTML,
134
161
  'OpenNodeTag',
135
- [
136
- t.ref`openToken`,
137
- t.ref`flags`,
138
- ...when(language_, [t.ref`language`, t.ref`languageSeparator`]),
139
- ...when(type, [t.ref`type`]),
140
- ...when(attributes_.length, [t.embedded(buildSpace())]),
141
- t.ref`attributes[]`,
142
- t.arr(),
143
- ...interpolateArrayChildren(attributes_, t.ref`attributes[]`, t.embedded(buildSpace())),
144
- t.ref`closeToken`,
145
- ],
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
+ ),
146
172
  {
147
173
  openToken: t.s_node(l.CSTML, 'Punctuator', '<'),
148
174
  flags: buildFlags(flags),
149
175
  language: language_ && type ? buildLanguage(language_) : t.null_node(),
150
176
  languageSeparator: language_ && type ? t.s_node(l.CSTML, 'Punctuator', ':') : t.null_node(),
151
177
  type: type ? buildIdentifier(type) : t.null_node(),
152
- attributes: attributes_,
178
+ attributes: attributes_.properties['.'],
153
179
  closeToken: t.s_node(l.CSTML, 'Punctuator', '>'),
154
180
  },
155
181
  );
@@ -161,23 +187,20 @@ export const buildDoctypeTag = (attributes) => {
161
187
  return t.node(
162
188
  l.CSTML,
163
189
  'DoctypeTag',
164
- [
165
- t.ref`openToken`,
166
- t.ref`version`,
167
- t.ref`versionSeparator`,
168
- t.ref`doctype`,
169
- ...when(attributes_.length, [t.embedded(buildSpace())]),
170
- t.ref`attributes[]`,
171
- t.arr(),
172
- ...interpolateArrayChildren(attributes_, t.ref`attributes[]`, t.embedded(buildSpace())),
173
- t.ref`closeToken`,
174
- ],
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
+ ),
175
198
  {
176
199
  openToken: t.s_node(l.CSTML, 'Punctuator', '<!'),
177
200
  version: t.s_node(l.CSTML, 'PositiveInteger', '0'),
178
201
  versionSeparator: t.s_node(l.CSTML, 'Punctuator', ':'),
179
202
  doctype: t.s_node(l.CSTML, 'Keyword', 'cstml'),
180
- attributes: attributes_,
203
+ attributes: attributes_.properties['.'],
181
204
  closeToken: t.s_node(l.CSTML, 'Punctuator', '>'),
182
205
  },
183
206
  );
@@ -195,11 +218,13 @@ export const buildIdentifierPath = (path) => {
195
218
  return t.node(
196
219
  l.CSTML,
197
220
  'IdentifierPath',
198
- [
199
- t.ref`segments[]`,
200
- t.arr(),
201
- ...repeat(segments.length, t.ref`segments[]`, t.ref`separators`),
202
- ].slice(0, -1),
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
+ ),
203
228
  {
204
229
  segments,
205
230
  separators,
@@ -217,13 +242,15 @@ export const buildNodeCloseTag = (type, language) => {
217
242
  return t.node(
218
243
  l.CSTML,
219
244
  'CloseNodeTag',
220
- [
221
- t.ref`openToken`,
222
- ...when(language, [t.ref`language`]),
223
- ...when(type && language, [t.ref`languageSeparator`]),
224
- ...when(type, [t.ref`type`]),
225
- t.ref`closeToken`,
226
- ],
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
+ ),
227
254
  {
228
255
  openToken: t.s_node(l.CSTML, 'Punctuator', '</'),
229
256
  language: language ? buildLanguage(language) : t.null_node(),
@@ -267,7 +294,7 @@ export const buildProperty = (key, value) => {
267
294
  return t.node(
268
295
  l.Instruction,
269
296
  'Property',
270
- [t.ref`key`, t.ref`mapOperator`, t.embedded(buildSpace()), t.ref`value`],
297
+ btree.from(t.ref`key`, t.ref`mapOperator`, t.embedded(buildSpace()), t.ref`value`),
271
298
  {
272
299
  key: buildIdentifier(key),
273
300
  mapOperator: t.s_node(l.Instruction, 'Punctuator', ':'),
@@ -293,7 +320,12 @@ export const buildInteger = (value, base = 10) => {
293
320
  return t.node(
294
321
  l.CSTML,
295
322
  'Integer',
296
- [t.ref`digits[]`, t.arr(), ...digits.map((d) => t.ref`digits[]`)],
323
+ btree.fromValues(
324
+ concat(
325
+ [t.ref`digits[]`, t.arr()],
326
+ digits.map(() => t.ref`digits[]`),
327
+ ),
328
+ ),
297
329
  {
298
330
  digits: digits.map((digit) => buildDigit(digit)),
299
331
  },
@@ -330,11 +362,16 @@ export const buildString = (value) => {
330
362
  let lit = '';
331
363
 
332
364
  if (pieces.length === 1 && pieces[0] === "'") {
333
- return t.node(l.CSTML, 'String', [t.ref`openToken`, t.ref`content`, t.ref`closeToken`], {
334
- openToken: t.s_node(l.CSTML, 'Punctuator', '"'),
335
- content: interpolateString(agastBuildLiteralTag(value)),
336
- closeToken: t.s_node(l.CSTML, 'Punctuator', '"'),
337
- });
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
+ );
338
375
  }
339
376
 
340
377
  for (const piece of pieces) {
@@ -368,12 +405,12 @@ export const buildString = (value) => {
368
405
  value = t.node(
369
406
  l.CSTML,
370
407
  'EscapeCode',
371
- [
372
- t.ref`sigilToken`,
373
- t.ref`digits[]`,
374
- t.arr(),
375
- ...[...hexDigits].map((d) => t.ref`digits[]`),
376
- ],
408
+ btree.fromValues(
409
+ concat(
410
+ [t.ref`sigilToken`, t.ref`digits[]`, t.arr()],
411
+ [...hexDigits].map((d) => t.ref`digits[]`),
412
+ ),
413
+ ),
377
414
  {
378
415
  sigilToken: buildKeyword('u'),
379
416
  digits: [...hexDigits].map((digit) => buildDigit(digit)),
@@ -418,11 +455,16 @@ export const buildString = (value) => {
418
455
  if (lit) tags.push(agastBuildLiteralTag(lit));
419
456
  lit = '';
420
457
 
421
- return t.node(l.CSTML, 'String', [t.ref`openToken`, t.ref`content`, t.ref`closeToken`], {
422
- openToken: t.s_node(l.CSTML, 'Punctuator', "'"),
423
- content: interpolateString(tags),
424
- closeToken: t.s_node(l.CSTML, 'Punctuator', "'"),
425
- });
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
+ );
426
468
  };
427
469
 
428
470
  export const buildBoolean = (value) => {
@@ -438,75 +480,80 @@ export const buildNullTag = () => {
438
480
  };
439
481
 
440
482
  export const buildArray = (elements) => {
483
+ const elements_ = buildArrayElements(elements);
441
484
  return t.node(
442
485
  l.Instruction,
443
486
  'Array',
444
- [
445
- t.ref`openToken`,
446
- t.ref`elements[]`,
447
- t.arr(),
448
- ...interpolateArrayChildren(
449
- elements,
450
- t.ref`elements[]`,
451
- t.embedded(
452
- t.t_node(l.Comment, null, [t.embedded(t.t_node('Space', 'Space', [t.lit(' ')]))]),
453
- ),
454
- ),
455
- t.ref`closeToken`,
456
- ],
487
+ btree.fromValues(
488
+ concat([t.ref`openToken`], interpolateFragmentChildren(elements_, t.ref`elements[]`), [
489
+ t.ref`closeToken`,
490
+ ]),
491
+ ),
457
492
  {
458
493
  openToken: t.s_node(l.Instruction, 'Punctuator', '['),
459
- elements,
494
+ elements: elements_.properties['.'],
460
495
  closeToken: t.s_node(l.Instruction, 'Punctuator', ']'),
461
496
  },
462
497
  );
463
498
  };
464
499
 
500
+ export const buildArrayElements = (values) => {
501
+ return buildSpaceSeparatedList(values.map((value) => buildExpression(value)));
502
+ };
503
+
504
+ export const buildTupleValues = buildArrayElements;
505
+
465
506
  export const buildTuple = (values) => {
507
+ const values_ = buildTupleValues(values);
466
508
  return t.node(
467
509
  l.Instruction,
468
510
  'Tuple',
469
- [
470
- t.ref`openToken`,
471
- t.ref`values[]`,
472
- t.arr(),
473
- ...interpolateArrayChildren(
474
- values,
475
- t.ref`values[]`,
476
- t.embedded(
477
- t.t_node(l.Comment, null, [t.embedded(t.t_node('Space', 'Space', [t.lit(' ')]))]),
478
- ),
479
- ),
480
- t.ref`closeToken`,
481
- ],
511
+ btree.fromValues(
512
+ concat([t.ref`openToken`], interpolateFragmentChildren(values_, t.ref`values[]`), [
513
+ t.ref`closeToken`,
514
+ ]),
515
+ ),
482
516
  {
483
517
  openToken: t.s_node(l.Instruction, 'Punctuator', '('),
484
- values,
518
+ values: values_.properties['.'],
485
519
  closeToken: t.s_node(l.Instruction, 'Punctuator', ')'),
486
520
  },
487
521
  );
488
522
  };
489
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
+
490
544
  export const buildObject = (properties) => {
545
+ const properties_ = buildObjectProperties(properties);
491
546
  return t.node(
492
547
  l.Instruction,
493
548
  'Object',
494
- [
495
- t.ref`openToken`,
496
- t.ref`properties[]`,
497
- t.arr(),
498
- ...interpolateArrayChildren(
499
- Object.entries(properties).map(([key, value]) => buildProperty(key, value)),
500
- t.ref`properties[]`,
501
- t.embedded(
502
- t.t_node(l.Comment, null, [t.embedded(t.t_node('Space', 'Space', [t.lit(' ')]))]),
503
- ),
504
- ),
505
- t.ref`closeToken`,
506
- ],
549
+ btree.fromValues(
550
+ concat([t.ref`openToken`], interpolateFragmentChildren(properties_, t.ref`properties[]`), [
551
+ t.ref`closeToken`,
552
+ ]),
553
+ ),
507
554
  {
508
555
  openToken: t.s_node(l.Instruction, 'Punctuator', '{'),
509
- properties: Object.entries(properties).map(([key, value]) => buildProperty(key, value)),
556
+ properties: properties_.properties['.'],
510
557
  closeToken: t.s_node(l.Instruction, 'Punctuator', '}'),
511
558
  },
512
559
  {},
@@ -514,11 +561,16 @@ export const buildObject = (properties) => {
514
561
  };
515
562
 
516
563
  export const buildMappingAttribute = (key, value) => {
517
- return t.node(l.CSTML, 'MappingAttribute', [t.ref`key`, t.ref`mapOperator`, t.ref`value`], {
518
- key: buildIdentifier(key),
519
- mapOperator: t.s_node(l.CSTML, 'Punctuator', '='),
520
- value: buildExpression(value),
521
- });
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
+ );
522
574
  };
523
575
 
524
576
  export const buildBooleanAttribute = (key, value) => {
@@ -532,6 +584,42 @@ export const buildAttribute = (key, value) => {
532
584
  return isBoolean(value) ? buildBooleanAttribute(key, value) : buildMappingAttribute(key, value);
533
585
  };
534
586
 
587
+ export const buildPattern = (alternatives, flags) => {
588
+ return t.node(
589
+ l.Regex,
590
+ 'Pattern',
591
+ btree.fromValues(
592
+ concat(
593
+ [t.ref`openToken`],
594
+ buildSeparatedListChildren(alternatives, t.ref`alternatives[]`, t.ref`separators[]`),
595
+ [t.ref`closeToken`, t.ref`flags`],
596
+ ),
597
+ ),
598
+ {
599
+ openToken: t.s_node(l.Regex, 'Punctuator', '/'),
600
+ alternatives,
601
+ separators: alternatives.slice(0, -1).map((alt) => t.s_node(l.Regex, 'Punctuator', '|')),
602
+ closeToken: t.s_node(l.Regex, 'Punctuator', '/'),
603
+ flags: buildFlags(flags),
604
+ },
605
+ );
606
+ };
607
+
608
+ export const buildAlternative = (elements) => {
609
+ return t.node(
610
+ l.Regex,
611
+ 'Pattern',
612
+ btree.fromValues(
613
+ concat([t.ref`openToken`], buildSeparatedListChildren(elements, t.ref`elements[]`), [
614
+ t.ref`closeToken`,
615
+ ]),
616
+ ),
617
+ {
618
+ elements,
619
+ },
620
+ );
621
+ };
622
+
535
623
  export const buildExpression = (expr) => {
536
624
  if (isNull(expr)) return buildNullTag();
537
625
 
@@ -561,12 +649,22 @@ export const buildExpression = (expr) => {
561
649
  };
562
650
 
563
651
  export const buildAttributes = (attributes = {}) => {
564
- return Object.entries(attributes).map(({ 0: key, 1: value }) => buildAttribute(key, value));
565
- };
652
+ const attributes_ = Object.entries(attributes).map(({ 0: key, 1: value }) =>
653
+ buildAttribute(key, value),
654
+ );
566
655
 
567
- export const buildNodeMatcher = (flags, language, type, attributes = {}) => {
568
- const attributes_ = buildAttributes(attributes);
656
+ return t.frag(
657
+ btree.fromValues(
658
+ [t.ref`.[]`, t.arr()],
659
+ buildSeparatedListChildren(attributes_, t.ref`.[]`, t.embedded(buildSpace())),
660
+ ),
661
+ {
662
+ ['.']: attributes_,
663
+ },
664
+ );
665
+ };
569
666
 
667
+ export const buildNodeMatcher = (flags, language, type, attributes) => {
570
668
  let language_ = !language || language.length === 0 ? null : language;
571
669
 
572
670
  const flags_ = buildFlags(flags);
@@ -574,24 +672,24 @@ export const buildNodeMatcher = (flags, language, type, attributes = {}) => {
574
672
  return t.node(
575
673
  l.Spamex,
576
674
  'NodeMatcher',
577
- [
578
- t.ref`openToken`,
579
- ...when(flags_, [t.ref`flags`]),
580
- ...when(language_, [t.ref`language`, t.ref`languageSeparator`]),
581
- t.ref`type`,
582
- ...when(attributes_.length, [t.embedded(buildSpace())]),
583
- t.ref`attributes[]`,
584
- t.arr(),
585
- ...interpolateArrayChildren(attributes_, t.ref`attributes[]`, t.embedded(buildSpace())),
586
- t.ref`closeToken`,
587
- ],
675
+ btree.fromValues(
676
+ concat(
677
+ [t.ref`openToken`],
678
+ when(flags_, [t.ref`flags`]),
679
+ when(language_, [t.ref`language`, t.ref`languageSeparator`]),
680
+ [t.ref`type`],
681
+ when(attributes.length, [t.embedded(buildSpace())]),
682
+ [...btree.traverse(attributes.children)].slice(1, -1),
683
+ [t.ref`closeToken`],
684
+ ),
685
+ ),
588
686
  {
589
687
  openToken: t.s_node(l.CSTML, 'Punctuator', '<'),
590
688
  language: buildLanguage(language_),
591
689
  languageSeparator: language_ && type ? t.s_node(l.CSTML, 'Punctuator', ':') : t.null_node(),
592
690
  flags: flags_,
593
691
  type: buildIdentifier(type),
594
- attributes: attributes_,
692
+ attributes: attributes.properties.attributes,
595
693
  closeToken: t.s_node(l.CSTML, 'Punctuator', '>'),
596
694
  },
597
695
  );
package/lib/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import { sourceTextFor, getCooked, isNull, nodeFlags } from '@bablr/agast-helpers/tree';
2
2
  import * as btree from '@bablr/agast-helpers/btree';
3
3
  import * as sym from '@bablr/agast-helpers/symbols';
4
- import { buildNodeCloseTag, buildLiteralTag } from '@bablr/agast-helpers/builders';
4
+ import { buildNodeCloseTag, buildLiteralTag, buildDoctypeTag } from '@bablr/agast-helpers/builders';
5
5
  import {
6
6
  DoctypeTag,
7
7
  OpenNodeTag,
8
+ OpenFragmentTag,
9
+ CloseFragmentTag,
8
10
  CloseNodeTag,
9
11
  ReferenceTag,
10
12
  ShiftTag,
@@ -42,14 +44,14 @@ export const shouldBranch = (effects) => {
42
44
  };
43
45
 
44
46
  const reifyFlags = (flags) => {
45
- let { triviaToken, escapeToken, tokenToken, expressionToken, intrinsicToken } = flags.properties;
47
+ let { triviaToken, escapeToken, tokenToken, expressionToken, hasGapToken } = flags.properties;
46
48
 
47
49
  return {
48
50
  token: !!reifyExpression(tokenToken),
49
51
  escape: !!reifyExpression(escapeToken),
50
52
  trivia: !!reifyExpression(triviaToken),
51
- intrinsic: !!reifyExpression(intrinsicToken),
52
53
  expression: !!reifyExpression(expressionToken),
54
+ hasGap: !!reifyExpression(hasGapToken),
53
55
  };
54
56
  };
55
57
 
@@ -84,22 +86,49 @@ export const reifyProperties = (properties = []) => {
84
86
  return built;
85
87
  };
86
88
 
89
+ export const buildFragmentChildren = (node) => {
90
+ let { open, children = [], close } = node.properties;
91
+
92
+ let built = [];
93
+
94
+ open = reifyExpression(open);
95
+ close = reifyExpression(close);
96
+
97
+ built = btree.push(built, open);
98
+
99
+ for (const child of btree.traverse(children)) {
100
+ if (child.type !== 'Property') throw new Error('umimplemented');
101
+
102
+ let { reference } = child.properties;
103
+
104
+ reference = reifyExpression(reference);
105
+
106
+ built = btree.push(built, reference);
107
+ }
108
+
109
+ built = btree.push(built, close);
110
+
111
+ return built;
112
+ };
113
+
87
114
  export const buildChildren = (node) => {
88
115
  let { open, children = [], close } = node.properties;
89
116
 
90
117
  const selfClosing = !!open.properties.selfClosingTagToken;
91
118
  const { intrinsicValue } = open.properties;
92
- const built = [];
119
+ let built = [];
93
120
 
94
121
  open = reifyExpression(open);
95
122
  close = reifyExpression(close);
96
123
 
97
124
  if (selfClosing) {
98
- built.push(open);
99
- if (intrinsicValue) built.push(buildLiteralTag(intrinsicValue));
100
- built.push(buildNodeCloseTag());
125
+ built = btree.push(built, open);
126
+ if (intrinsicValue) {
127
+ built = btree.push(built, buildLiteralTag(intrinsicValue));
128
+ }
129
+ built = btree.push(built, buildNodeCloseTag());
101
130
  } else {
102
- built.push(open);
131
+ built = btree.push(built, open);
103
132
  for (const child of btree.traverse(children)) {
104
133
  if (child.type !== 'Property') throw new Error('umimplemented');
105
134
 
@@ -107,10 +136,10 @@ export const buildChildren = (node) => {
107
136
 
108
137
  reference = reifyExpression(reference);
109
138
 
110
- built.push(reference);
139
+ built = btree.push(built, reference);
111
140
  }
112
141
 
113
- built.push(close);
142
+ built = btree.push(built, close);
114
143
  }
115
144
 
116
145
  return built;
@@ -121,8 +150,12 @@ export const reifyExpression = (node) => {
121
150
 
122
151
  if (!node || node.type === sym.null) return null;
123
152
 
153
+ if (!node.type) {
154
+ node = node.properties['.'];
155
+ }
156
+
124
157
  if (node.language === 'https://bablr.org/languages/core/en/cstml') {
125
- switch (node.type) {
158
+ switch (node.type?.description || node.type) {
126
159
  case 'Document': {
127
160
  let { doctype, tree } = node.properties;
128
161
 
@@ -130,13 +163,17 @@ export const reifyExpression = (node) => {
130
163
  tree = reifyExpression(tree);
131
164
 
132
165
  let { attributes } = doctype.value;
133
- let { children, properties } = tree;
166
+ let { properties } = tree;
134
167
 
135
168
  return {
136
169
  flags: nodeFlags,
137
170
  language: attributes['bablr-language'],
138
- type: sym.fragment,
139
- children,
171
+ type: null,
172
+ children: btree.addAt(
173
+ 0,
174
+ buildFragmentChildren(node.properties.tree),
175
+ buildDoctypeTag(attributes),
176
+ ),
140
177
  properties,
141
178
  attributes,
142
179
  };
@@ -161,6 +198,25 @@ export const reifyExpression = (node) => {
161
198
  };
162
199
  }
163
200
 
201
+ case 'Fragment': {
202
+ let { open, children } = node.properties;
203
+
204
+ open = reifyExpression(open);
205
+
206
+ let { flags } = open.value;
207
+
208
+ const properties = reifyProperties(children);
209
+
210
+ return {
211
+ flags,
212
+ language: null,
213
+ type: null,
214
+ children: buildChildren(node),
215
+ properties,
216
+ attributes: {},
217
+ };
218
+ }
219
+
164
220
  case 'DoctypeTag': {
165
221
  let { doctype, version, attributes } = node.properties;
166
222
  return {
@@ -174,11 +230,14 @@ export const reifyExpression = (node) => {
174
230
  }
175
231
 
176
232
  case 'ReferenceTag': {
177
- let { name, arrayOperatorToken } = node.properties;
233
+ let { name, arrayOperatorToken, hasGapToken } = node.properties;
178
234
 
179
235
  name = reifyExpression(name);
180
236
 
181
- return { type: ReferenceTag, value: { name, isArray: !isNull(arrayOperatorToken) } };
237
+ return {
238
+ type: ReferenceTag,
239
+ value: { name, isArray: !isNull(arrayOperatorToken), hasGap: !isNull(hasGapToken) },
240
+ };
182
241
  }
183
242
 
184
243
  case 'LiteralTag': {
@@ -216,6 +275,21 @@ export const reifyExpression = (node) => {
216
275
  return { type: CloseNodeTag, value: { language, type } };
217
276
  }
218
277
 
278
+ case 'OpenFragmentTag': {
279
+ let { flags } = node.properties;
280
+
281
+ flags = reifyFlags(flags);
282
+
283
+ return {
284
+ type: OpenFragmentTag,
285
+ value: { flags },
286
+ };
287
+ }
288
+
289
+ case 'CloseFragmentTag': {
290
+ return { type: CloseFragmentTag, value: undefined };
291
+ }
292
+
219
293
  case 'Integer': {
220
294
  let { digits } = node.properties;
221
295
  return parseInt(digits.map((digit) => getCooked(digit)).join(''), 10);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bablr/agast-vm-helpers",
3
3
  "description": "Helper functions for working with the BABLR VM",
4
- "version": "0.4.0",
4
+ "version": "0.5.1",
5
5
  "author": "Conrad Buck<conartist6@gmail.com>",
6
6
  "type": "module",
7
7
  "files": [
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "sideEffects": false,
20
20
  "dependencies": {
21
- "@bablr/agast-helpers": "0.4.0"
21
+ "@bablr/agast-helpers": "^0.5.0"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#49f5952efed27f94ee9b94340eb1563c440bf64e",