@bablr/helpers 0.24.0 → 0.25.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/builders.js CHANGED
@@ -1,15 +1,11 @@
1
1
  import { interpolateFragment, buildFilledGapFunction } from '@bablr/agast-helpers/template';
2
- import {
3
- buildNullNode,
4
- get,
5
- getRootArray,
6
- treeFromStreamSync as treeFromStream,
7
- } from '@bablr/agast-helpers/tree';
2
+ import { get, isEmpty, treeFromStreamSync as treeFromStream } from '@bablr/agast-helpers/tree';
8
3
  import { buildLiteralTag as agastBuildLiteralTag } from '@bablr/agast-helpers/builders';
9
4
  import * as t from '@bablr/agast-helpers/shorthand';
10
5
  import * as Tags from '@bablr/agast-helpers/tags';
11
6
  import * as l from '@bablr/agast-vm-helpers/languages';
12
7
  import { concat } from '@bablr/agast-vm-helpers/iterable';
8
+ import { buildNullNode } from '@bablr/agast-helpers/path';
13
9
 
14
10
  const { freeze } = Object;
15
11
  const { isArray } = Array;
@@ -18,7 +14,7 @@ const when = (condition, value) => (condition ? value : { *[Symbol.iterator]() {
18
14
 
19
15
  const isString = (val) => typeof val === 'string';
20
16
 
21
- export const buildReferenceTag = (name, isArray = false, flags = t.referenceFlags) => {
17
+ export const buildReferenceTag = (name, flags = t.referenceFlags) => {
22
18
  let expressions = [];
23
19
  const gap = buildFilledGapFunction(expressions);
24
20
 
@@ -27,14 +23,102 @@ export const buildReferenceTag = (name, isArray = false, flags = t.referenceFlag
27
23
  t.nodeOpen(t.nodeFlags, 'ReferenceTag'),
28
24
  t.ref`name`,
29
25
  gap(name ? buildIdentifier(name) : buildNullNode()),
30
- t.ref`openIndexToken`,
31
- gap(isArray ? buildToken('Punctuator', '[') : buildNullNode()),
32
- t.ref`closeIndexToken`,
33
- gap(isArray ? buildToken('Punctuator', ']') : buildNullNode()),
34
26
  t.ref`flags`,
35
27
  gap(flags ? buildReferenceFlags(flags) : buildNullNode()),
36
28
  t.ref`sigilToken`,
37
- gap(buildToken('Punctuator', ':')),
29
+ gap(buildToken(null, ':')),
30
+ t.nodeClose(),
31
+ ],
32
+ { expressions },
33
+ );
34
+ };
35
+
36
+ export const buildBindingMatcher = (segments) => {
37
+ let expressions = [];
38
+ const gap = buildFilledGapFunction(expressions);
39
+
40
+ return treeFromStream(
41
+ concat(
42
+ [t.nodeOpen(t.nodeFlags, 'BindingMatcher')],
43
+ segments.flatMap((segment) => [t.ref`segments[]`, gap(buildBindingSegment(segment))]),
44
+ [t.nodeClose()],
45
+ ),
46
+ { expressions },
47
+ );
48
+ };
49
+
50
+ export const buildBindingMatchers = (matchers) => {
51
+ let expressions = [];
52
+ const gap = buildFilledGapFunction(expressions);
53
+
54
+ return treeFromStream(
55
+ concat(
56
+ [t.fragOpen()],
57
+ matchers.flatMap((matcher) => [
58
+ t.ref`bindingMatchers[]`,
59
+ gap(buildBindingMatcher(matcher.segments)),
60
+ ]),
61
+ [t.nodeClose()],
62
+ ),
63
+ { expressions },
64
+ );
65
+ };
66
+
67
+ export const buildBoundNodeMatcher = (bindingMatchers, valueMatcher) => {
68
+ if (valueMatcher.value.name.description === 'BoundNodeMatcher') throw new Error();
69
+ const expressions = [];
70
+ const gap = buildFilledGapFunction(expressions);
71
+
72
+ return treeFromStream(
73
+ (function* () {
74
+ yield t.nodeOpen(t.nodeFlags, 'BoundNodeMatcher');
75
+ if (bindingMatchers.length) {
76
+ yield* interpolateFragment(
77
+ buildBindingMatchers(bindingMatchers),
78
+ t.ref`bindingMatchers`,
79
+ expressions,
80
+ );
81
+ }
82
+ yield t.ref`nodeMatcher`;
83
+ yield gap(valueMatcher);
84
+ yield t.nodeClose();
85
+ })(),
86
+ { expressions },
87
+ );
88
+ };
89
+
90
+ export const buildPropertyMatcher = (refMatcher, valueMatcher) => {
91
+ const expressions = [];
92
+ const gap = buildFilledGapFunction(expressions);
93
+
94
+ return treeFromStream(
95
+ (function* () {
96
+ yield t.nodeOpen(t.nodeFlags, 'PropertyMatcher');
97
+ yield t.ref`refMatcher`;
98
+ yield gap(refMatcher || buildNullNode());
99
+ yield t.ref`valueMatcher`;
100
+ yield gap(valueMatcher || buildNullNode());
101
+ yield t.nodeClose();
102
+ })(),
103
+ { expressions },
104
+ );
105
+ };
106
+
107
+ export const buildBindingSegment = (segment) => {
108
+ let expressions = [];
109
+ const gap = buildFilledGapFunction(expressions);
110
+
111
+ let { type, name } = segment;
112
+
113
+ return treeFromStream(
114
+ [
115
+ t.nodeOpen(t.nodeFlags, 'BindingSegment'),
116
+ t.ref`openToken`,
117
+ gap(buildToken(null, ':')),
118
+ t.ref`path`,
119
+ gap(type ? buildToken(null, type) : buildIdentifier(name)),
120
+ t.ref`closeToken`,
121
+ gap(buildToken(null, ':')),
38
122
  t.nodeClose(),
39
123
  ],
40
124
  { expressions },
@@ -47,9 +131,9 @@ export const buildGapTag = () => {
47
131
 
48
132
  return treeFromStream(
49
133
  [
50
- t.nodeOpen(t.nodeFlags, 'ShiftTag'),
134
+ t.nodeOpen(t.nodeFlags, 'GapTag'),
51
135
  t.ref`sigilToken`,
52
- gap(buildToken('Punctuator', '<//>')),
136
+ gap(buildToken(null, '<//>')),
53
137
  t.nodeClose(),
54
138
  ],
55
139
  { expressions },
@@ -64,7 +148,7 @@ export const buildShiftTag = () => {
64
148
  [
65
149
  t.nodeOpen(t.nodeFlags, 'ShiftTag'),
66
150
  t.ref`sigilToken`,
67
- gap(buildToken('Punctuator', '^^^')),
151
+ gap(buildToken(null, '^^^')),
68
152
  t.nodeClose(),
69
153
  ],
70
154
  { expressions },
@@ -72,17 +156,21 @@ export const buildShiftTag = () => {
72
156
  };
73
157
 
74
158
  export const buildReferenceFlags = (flags = t.referenceFlags) => {
75
- const { expression = null, hasGap = null } = flags;
159
+ const { array, expression, intrinsic, hasGap } = flags;
76
160
  let expressions = [];
77
161
  const gap = buildFilledGapFunction(expressions);
78
162
 
79
163
  return treeFromStream(
80
164
  [
81
165
  t.nodeOpen(t.nodeFlags, 'ReferenceFlags'),
166
+ t.ref`arrayToken`,
167
+ gap(array ? buildToken(null, '[]') : buildNullNode()),
82
168
  t.ref`expressionToken`,
83
- gap(expression ? buildToken('Punctuator', '+') : buildNullNode()),
169
+ gap(expression ? buildToken(null, '+') : buildNullNode()),
170
+ t.ref`intrinsicToken`,
171
+ gap(intrinsic ? buildToken(null, '*') : buildNullNode()),
84
172
  t.ref`hasGapToken`,
85
- gap(hasGap ? buildToken('Punctuator', '$') : buildNullNode()),
173
+ gap(hasGap ? buildToken(null, '$') : buildNullNode()),
86
174
  t.nodeClose(),
87
175
  ],
88
176
  { expressions },
@@ -90,84 +178,113 @@ export const buildReferenceFlags = (flags = t.referenceFlags) => {
90
178
  };
91
179
 
92
180
  export const buildNodeFlags = (flags = t.nodeFlags) => {
93
- const { token = null, hasGap = null, fragment = null, cover = null } = flags;
181
+ const { token = null, hasGap = null } = flags;
94
182
  let expressions = [];
95
183
  const gap = buildFilledGapFunction(expressions);
96
184
 
185
+ let flags_ = { token, hasGap };
186
+ let attributes = flags_;
187
+
97
188
  return treeFromStream(
98
189
  [
99
- t.nodeOpen(t.nodeFlags, 'NodeFlags'),
190
+ t.nodeOpen(t.nodeFlags, 'NodeFlags', null, attributes),
100
191
  t.ref`tokenToken`,
101
- gap(token ? buildToken('Punctuator', '*') : buildNullNode()),
192
+ gap(token ? buildToken(null, '*') : buildNullNode()),
102
193
  t.ref`hasGapToken`,
103
- gap(hasGap ? buildToken('Punctuator', '$') : buildNullNode()),
104
- t.ref`fragmentToken`,
105
- gap(fragment ? buildToken('Punctuator', '_') : buildNullNode()),
106
- t.ref`multiFragmentToken`,
107
- gap(!cover ? buildToken('Punctuator', '_') : buildNullNode()),
194
+ gap(hasGap ? buildToken(null, '$') : buildNullNode()),
108
195
  t.nodeClose(),
109
196
  ],
110
197
  { expressions },
111
198
  );
112
199
  };
113
200
 
114
- export const buildSpamMatcher = (type = null, value = null, attributes = null) => {
115
- return buildOpenNodeMatcher(buildNodeFlags(t.nodeFlags), null, type, value, attributes);
201
+ export const buildSpamMatcher = (name = null, value = null, attributes = null) => {
202
+ return buildTreeNodeMatcherOpen(buildNodeFlags(t.nodeFlags), null, name, value, attributes);
116
203
  };
117
204
 
118
- export const buildOpenNodeMatcher = (flags, type, literalValue, attributes = null) => {
205
+ export const buildTreeNodeMatcherOpen = (flags, type, name, literalValue, attributes = null) => {
119
206
  const expressions = [];
120
207
  const gap = buildFilledGapFunction(expressions);
121
208
 
122
- if (!type) throw new Error();
209
+ if (!type && !name) throw new Error();
123
210
 
124
211
  return treeFromStream(
125
212
  (function* () {
126
- yield t.nodeOpen(t.nodeFlags, 'OpenNodeMatcher');
213
+ yield t.nodeOpen(t.nodeFlags, 'TreeNodeMatcherOpen');
127
214
  yield t.ref`openToken`;
128
- yield gap(buildToken('Punctuator', '<'));
215
+ yield gap(buildToken(null, '<'));
129
216
  yield t.ref`flags`;
130
217
  yield gap(flags);
131
218
  yield t.ref`type`;
132
- yield gap(typeof type === 'string' ? buildIdentifier(type) : type);
219
+ yield gap(typeof type === 'string' ? buildToken(null, type) : type);
220
+ yield t.ref`name`;
221
+ yield gap(typeof name === 'string' ? buildIdentifier(name) : name);
133
222
 
134
- yield* when(literalValue, [t.ref`#`, ...buildSpace().tags]);
223
+ yield* when(literalValue, [t.ref`#`, ...Tags.traverse(buildSpace().value.tags)]);
135
224
 
136
225
  yield t.ref`literalValue`;
137
226
  yield gap(literalValue ? buildString(literalValue) : buildNullNode());
138
227
 
139
- let rootArr = getRootArray(attributes);
140
-
141
- if (rootArr.length) {
228
+ if (!isEmpty(attributes)) {
142
229
  yield t.ref`#`;
143
- yield* buildSpace().tags;
230
+ yield* buildSpace().value.tags;
144
231
  yield* interpolateFragment(attributes, t.ref`attributes[]`, expressions);
145
232
  }
146
233
 
147
234
  yield t.ref`selfClosingToken`;
148
- yield gap(buildToken('Punctuator', '/'));
235
+ yield gap(buildToken(null, '/'));
149
236
  yield t.ref`closeToken`;
150
- yield gap(buildToken('Punctuator', '>'));
237
+ yield gap(buildToken(null, '>'));
151
238
  yield t.nodeClose();
152
239
  })(),
153
240
  { expressions },
154
241
  );
155
242
  };
156
243
 
157
- export const buildBasicNodeMatcher = (open) => {
244
+ export const buildTreeNodeMatcher = (open, children) => {
158
245
  const expressions = [];
159
246
  const gap = buildFilledGapFunction(expressions);
160
247
 
248
+ let children_ = children;
249
+
250
+ if (children_) {
251
+ throw new Error('not implemented');
252
+ // if (isArray(children_)) {
253
+ // children_ = buildTreeNodeMatcherChildren(children_);
254
+ // }
255
+ }
256
+
161
257
  return treeFromStream(
162
- [t.nodeOpen(t.nodeFlags, 'BasicNodeMatcher'), t.ref`open`, gap(open), t.nodeClose()],
258
+ (function* () {
259
+ yield t.nodeOpen(t.nodeFlags, 'TreeNodeMatcher');
260
+ yield t.ref`open`;
261
+ yield gap(open);
262
+ if (!isEmpty(children_)) {
263
+ yield* interpolateFragment(children_, t.ref`children[]`, expressions);
264
+ }
265
+ yield t.nodeClose();
266
+ })(),
163
267
  { expressions },
164
268
  );
165
269
  };
166
270
 
167
- export const buildReferenceMatcher = (type, name, isArray, flags) => {
271
+ export const buildReferenceMatcher = (type = '.', name, flags) => {
168
272
  const expressions = [];
169
273
  const gap = buildFilledGapFunction(expressions);
170
274
 
275
+ if (isString(flags)) {
276
+ let array = flags.includes('[');
277
+ let intrinsic = flags.includes('*');
278
+ let hasGap = flags.includes('$');
279
+ let expression = flags.includes('+');
280
+
281
+ return buildReferenceMatcher(
282
+ type,
283
+ name,
284
+ buildReferenceFlags({ array, intrinsic, hasGap, expression }),
285
+ );
286
+ }
287
+
171
288
  return treeFromStream(
172
289
  (function* () {
173
290
  yield t.nodeOpen(t.nodeFlags, 'ReferenceMatcher');
@@ -175,56 +292,31 @@ export const buildReferenceMatcher = (type, name, isArray, flags) => {
175
292
  yield gap(type && buildKeyword(type));
176
293
  yield t.ref`name`;
177
294
  yield gap(name && buildIdentifier(name));
178
- yield* (function* () {
179
- if (isArray) {
180
- yield t.ref`openIndexToken`;
181
- yield gap(buildToken('Punctuator', '['));
182
- yield t.ref`closeIndexToken`;
183
- yield gap(buildToken('Punctuator', ']'));
184
- }
185
- })();
186
295
  yield t.ref`flags`;
187
296
  yield gap(flags);
188
297
  yield t.ref`sigilToken`;
189
- yield gap(buildToken('Punctuator', ':'));
298
+ yield gap(buildToken(null, ':'));
190
299
  yield t.ref`#`;
191
- yield* Tags.traverse(buildSpace().tags);
192
- yield t.nodeClose();
193
- })(),
194
- { expressions },
195
- );
196
- };
197
-
198
- export const buildFragmentMatcher = (flags = buildNodeFlags({ fragment: true, cover: true })) => {
199
- const expressions = [];
200
- const gap = buildFilledGapFunction(expressions);
201
-
202
- if (!flags) throw new Error();
203
-
204
- return treeFromStream(
205
- (function* () {
206
- yield t.nodeOpen(t.fragmentFlags, 'FragmentMatcher');
207
- yield t.ref`openToken`;
208
- yield gap(buildToken('Punctuator', '<'));
209
- yield t.ref`flags`;
210
- yield gap(flags);
211
- yield t.ref`closeToken`;
212
- yield gap(buildToken('Punctuator', '/>'));
300
+ yield* Tags.traverse(buildSpace().value.tags);
213
301
  yield t.nodeClose();
214
302
  })(),
215
303
  { expressions },
216
304
  );
217
305
  };
218
306
 
219
- export const buildToken = (type, value, attributes = {}) => {
220
- return treeFromStream([t.nodeOpen(t.tokenFlags, type, attributes), t.lit(value), t.nodeClose()]);
307
+ export const buildToken = (name, value, attributes = {}) => {
308
+ return treeFromStream([
309
+ t.nodeOpen(t.tokenFlags, name, null, attributes),
310
+ t.lit(value),
311
+ t.nodeClose(),
312
+ ]);
221
313
  };
222
314
 
223
315
  export const buildPunctuator = (value, attributes = {}) => {
224
- return buildToken('Punctuator', value, attributes);
316
+ return buildToken(null, value, attributes);
225
317
  };
226
318
 
227
- export const buildOpenNodeTag = (flags, type = null, attributes) => {
319
+ export const buildOpenNodeTag = (flags, name = null, attributes) => {
228
320
  const expressions = [];
229
321
  const gap = buildFilledGapFunction(expressions);
230
322
 
@@ -234,10 +326,9 @@ export const buildOpenNodeTag = (flags, type = null, attributes) => {
234
326
  yield gap(buildPunctuator('<'));
235
327
  yield t.ref`flags`;
236
328
  yield gap(buildNodeFlags(flags));
237
- yield t.ref`type`;
238
- yield gap(type ? buildIdentifier(type) : buildNullNode());
239
- let rootArr = getRootArray(attributes);
240
- yield* when(rootArr.length, [t.ref`#`, gap(buildSpace())]);
329
+ yield t.ref`name`;
330
+ yield gap(name ? buildIdentifier(name) : buildNullNode());
331
+ yield* when(!isEmpty(attributes), [t.ref`#`, gap(buildSpace())]);
241
332
  yield* interpolateFragment(attributes, t.ref`attributes[]`, expressions);
242
333
  yield t.ref`closeToken`;
243
334
  yield gap(buildPunctuator('>'));
@@ -254,20 +345,20 @@ export const buildDoctypeTag = (attributes) => {
254
345
  (function* () {
255
346
  yield t.nodeOpen(t.nodeFlags, 'DoctypeTag');
256
347
  yield t.ref`openToken`;
257
- yield gap(buildPunctuator('Punctuator', '<!'));
348
+ yield gap(buildPunctuator('<!'));
258
349
  yield t.ref`version`;
259
350
  yield gap(buildToken('PositiveInteger', '0'));
260
351
  yield t.ref`versionSeparator`;
261
- yield gap(buildPunctuator('Punctuator', ':'));
352
+ yield gap(buildPunctuator(':'));
262
353
  yield t.ref`doctype`;
263
354
  yield gap(buildKeyword('cstml'));
264
355
  yield t.nodeClose();
265
356
 
266
- yield* when(getRootArray(attributes).length, [t.ref`#`, ...buildSpace().tags]);
357
+ yield* when(!isEmpty(attributes), [t.ref`#`, ...buildSpace().value.tags]);
267
358
  yield* interpolateFragment(attributes, t.ref`attributes[]`, expressions);
268
359
 
269
360
  yield t.ref`closeToken`;
270
- yield gap(buildToken('Punctuator', '>'));
361
+ yield gap(buildToken(null, '>'));
271
362
  })(),
272
363
  { expressions },
273
364
  );
@@ -285,19 +376,15 @@ export const buildIdentifierPath = (path) => {
285
376
  return treeFromStream(
286
377
  (function* () {
287
378
  yield t.nodeOpen(t.nodeFlags, 'IdentifierPath');
288
- yield t.ref`segments[]`;
289
- yield t.arr();
290
- yield t.ref`separatorTokens[]`;
291
- yield t.arr();
292
379
 
293
380
  yield* path_
294
- .flatMap((name) => [
381
+ .flatMap((segment) => [
295
382
  t.ref`segments[]`,
296
- gap(buildIdentifier(name)),
297
- t.ref`separatorTokens[]`,
298
- gap(buildToken('Punctuator', '.')),
383
+ gap(segment.type ? buildPunctuator(segment.type) : buildIdentifier(segment.name)),
384
+ t.ref`#separatorTokens`,
385
+ gap(buildToken(null, '.')),
299
386
  ])
300
- .slice(0, -1);
387
+ .slice(0, -2);
301
388
 
302
389
  yield t.nodeClose();
303
390
  })(),
@@ -319,9 +406,9 @@ export const buildCloseNodeTag = () => {
319
406
  [
320
407
  t.nodeOpen(t.nodeFlags, 'CloseNodeTag'),
321
408
  t.ref`openToken`,
322
- gap(buildToken('Punctuator', '</')),
409
+ gap(buildToken(null, '</')),
323
410
  t.ref`closeToken`,
324
- gap(buildToken('Punctuator', '>')),
411
+ gap(buildToken(null, '>')),
325
412
  t.nodeClose(),
326
413
  ],
327
414
  { expressions },
@@ -369,7 +456,7 @@ export const buildIdentifier = (name) => {
369
456
  yield t.nodeOpen(t.nodeFlags, 'Identifier');
370
457
 
371
458
  yield t.ref`openToken`;
372
- yield gap(buildToken('Punctuator', '`'));
459
+ yield gap(buildToken(null, '`'));
373
460
  yield t.ref`content`;
374
461
  yield t.nodeOpen(t.tokenFlags, 'IdentifierContent');
375
462
 
@@ -388,9 +475,9 @@ export const buildIdentifier = (name) => {
388
475
  let value = buildKeyword(chr);
389
476
 
390
477
  yield t.ref`@`;
391
- yield t.nodeOpen(t.nodeFlags, 'EscapeSequence', { cooked: chr });
478
+ yield t.nodeOpen(t.nodeFlags, 'EscapeSequence', null, { cooked: chr });
392
479
  yield t.ref`escape`;
393
- yield gap(buildToken('Punctuator', '\\'));
480
+ yield gap(buildToken(null, '\\'));
394
481
  yield t.ref`value`;
395
482
  yield gap(value);
396
483
  yield t.nodeClose();
@@ -405,7 +492,7 @@ export const buildIdentifier = (name) => {
405
492
  yield t.nodeClose();
406
493
 
407
494
  yield t.ref`closeToken`;
408
- yield gap(buildToken('Punctuator', '`'));
495
+ yield gap(buildToken(null, '`'));
409
496
  yield t.nodeClose();
410
497
  })(),
411
498
  { expressions },
@@ -418,7 +505,7 @@ export const buildIdentifierContent = (value) => {
418
505
  };
419
506
 
420
507
  export const buildKeyword = (name) => {
421
- return buildToken('Keyword', name);
508
+ return buildToken(null, name);
422
509
  };
423
510
 
424
511
  export const buildCall = (verb, args) => {
@@ -448,7 +535,7 @@ export const buildProperty = (key, value) => {
448
535
  yield t.ref`key`;
449
536
  yield gap(key);
450
537
  yield t.ref`mapOperator`;
451
- yield gap(buildToken('Punctuator', ':'));
538
+ yield gap(buildToken(null, ':'));
452
539
  yield t.ref`#`;
453
540
  yield gap(buildSpace());
454
541
  yield t.ref`value`;
@@ -478,7 +565,7 @@ export const buildInteger = (value, base = 10) => {
478
565
 
479
566
  return treeFromStream(
480
567
  concat(
481
- [t.nodeOpen(t.nodeFlags, 'Integer'), t.ref`digits[]`, t.arr()],
568
+ [t.nodeOpen(t.nodeFlags, 'Integer')],
482
569
  digits.flatMap((digit) => [t.ref`digits[]`, gap(buildDigit(digit))]),
483
570
  [t.nodeClose()],
484
571
  ),
@@ -504,9 +591,9 @@ export const buildInfinity = (value) => {
504
591
  [
505
592
  t.nodeOpen(t.nodeFlags, 'Infinity'),
506
593
  t.ref`sign`,
507
- gap(buildToken('Punctuator', sign)),
594
+ gap(buildToken(null, sign)),
508
595
  t.ref`value`,
509
- gap(buildToken('Keyword', 'Infinity')),
596
+ gap(buildToken(null, 'Infinity')),
510
597
  t.nodeClose(),
511
598
  ],
512
599
  { expressions },
@@ -533,11 +620,11 @@ export const buildString = (value) => {
533
620
  [
534
621
  t.nodeOpen(t.nodeFlags, 'String'),
535
622
  t.ref`openToken`,
536
- gap(buildToken('Punctuator', '"')),
623
+ gap(buildToken(null, '"')),
537
624
  t.ref`content`,
538
625
  gap(buildToken('StringContent', value)),
539
626
  t.ref`closeToken`,
540
- gap(buildToken('Punctuator', '"')),
627
+ gap(buildToken(null, '"')),
541
628
  t.nodeClose(),
542
629
  ],
543
630
  { expressions },
@@ -551,7 +638,7 @@ export const buildString = (value) => {
551
638
  (function* () {
552
639
  yield t.nodeOpen(t.nodeFlags, 'String');
553
640
  yield t.ref`openToken`;
554
- const tok = buildToken('Punctuator', "'");
641
+ const tok = buildToken(null, "'");
555
642
  yield gap(tok);
556
643
  yield t.ref`content`;
557
644
  yield t.nodeOpen(t.tokenFlags, 'StringContent');
@@ -586,8 +673,6 @@ export const buildString = (value) => {
586
673
  t.nodeOpen(t.nodeFlags, 'EscapeCode'),
587
674
  t.ref`sigilToken`,
588
675
  gap(buildKeyword(escapables[chr])),
589
- t.ref`digits[]`,
590
- t.arr(),
591
676
  t.nodeClose(),
592
677
  ],
593
678
  { expressions },
@@ -602,8 +687,6 @@ export const buildString = (value) => {
602
687
  t.nodeOpen(t.nodeFlags, 'EscapeCode'),
603
688
  t.ref`sigilToken`,
604
689
  gap(buildKeyword('u')),
605
- t.ref`digits[]`,
606
- t.arr(),
607
690
  [...hexDigits].flatMap((digit) => [t.ref`digits[]`, gap(buildDigit(digit))]),
608
691
  t.nodeClose(),
609
692
  ],
@@ -614,9 +697,9 @@ export const buildString = (value) => {
614
697
  }
615
698
 
616
699
  yield t.ref`@`;
617
- yield t.nodeOpen(t.nodeFlags, 'EscapeSequence', { cooked: chr });
700
+ yield t.nodeOpen(t.nodeFlags, 'EscapeSequence', null, { cooked: chr });
618
701
  yield t.ref`escape`;
619
- yield gap(buildToken('Punctuator', '\\'));
702
+ yield gap(buildToken(null, '\\'));
620
703
  yield t.ref`value`;
621
704
  yield gap(value);
622
705
  yield t.nodeClose();
@@ -643,7 +726,7 @@ export const buildString = (value) => {
643
726
 
644
727
  yield t.nodeClose();
645
728
  yield t.ref`closeToken`;
646
- yield gap(buildToken('Punctuator', "'"));
729
+ yield gap(buildToken(null, "'"));
647
730
  yield t.nodeClose();
648
731
  })(),
649
732
  { expressions },
@@ -658,7 +741,7 @@ export const buildBoolean = (value) => {
658
741
  [
659
742
  t.nodeOpen(t.nodeFlags, 'Boolean'),
660
743
  t.ref`sigilToken`,
661
- gap(buildToken('Keyword', value ? 'true' : 'false')),
744
+ gap(buildToken(null, value ? 'true' : 'false')),
662
745
  t.nodeClose(),
663
746
  ],
664
747
  { expressions },
@@ -673,7 +756,7 @@ export const buildNull = () => {
673
756
  [
674
757
  t.nodeOpen(t.nodeFlags, 'Null'),
675
758
  t.ref`sigilToken`,
676
- gap(buildToken('Keyword', 'null')),
759
+ gap(buildToken(null, 'null')),
677
760
  t.nodeClose(),
678
761
  ],
679
762
  { expressions },
@@ -688,7 +771,7 @@ export const buildUndefined = () => {
688
771
  [
689
772
  t.nodeOpen(t.nodeFlags, 'Undefined'),
690
773
  t.ref`sigilToken`,
691
- gap(buildToken('Keyword', 'undefined')),
774
+ gap(buildToken(null, 'undefined')),
692
775
  t.nodeClose(),
693
776
  ],
694
777
  { expressions },
@@ -703,7 +786,7 @@ export const buildNullTag = () => {
703
786
  [
704
787
  t.nodeOpen(t.nodeFlags, 'NullTag'),
705
788
  t.ref`sigilToken`,
706
- gap(buildToken('Keyword', 'null')),
789
+ gap(buildToken(null, 'null')),
707
790
  t.nodeClose(),
708
791
  ],
709
792
  { expressions },
@@ -718,10 +801,14 @@ export const buildArray = (elements) => {
718
801
  (function* () {
719
802
  yield t.nodeOpen(t.nodeFlags, 'Array');
720
803
  yield t.ref`openToken`;
721
- yield gap(buildToken('Punctuator', '['));
722
- yield* interpolateFragment(elements, t.ref`elements[]`, expressions);
804
+ yield gap(buildToken(null, '['));
805
+ yield* interpolateFragment(
806
+ isArray(elements) ? buildArrayElements(elements) : elements,
807
+ t.ref`elements[]`,
808
+ expressions,
809
+ );
723
810
  yield t.ref`closeToken`;
724
- yield gap(buildToken('Punctuator', ']'));
811
+ yield gap(buildToken(null, ']'));
725
812
  yield t.nodeClose();
726
813
  })(),
727
814
  { expressions },
@@ -732,28 +819,24 @@ export const buildArrayElements = (values) => {
732
819
  const expressions = [];
733
820
  return treeFromStream(
734
821
  (function* () {
735
- yield t.doctype({ bablrLanguage: l.Instruction });
736
- yield t.nodeOpen(t.nodeFlags);
737
- yield* buildCommaSeparatedList(values, t.ref`.[]`, expressions);
822
+ yield t.fragOpen();
823
+ yield* buildSeparatedList(',', values, t.ref`elements[]`, expressions);
738
824
  yield t.nodeClose();
739
825
  })(),
740
826
  { expressions },
741
827
  );
742
828
  };
743
829
 
744
- export function* buildCommaSeparatedList(values, ref, expressions) {
830
+ export function* buildSeparatedList(separator, values, ref, expressions) {
745
831
  const gap = buildFilledGapFunction(expressions);
746
832
 
747
- if (!ref.value.isArray) throw new Error();
748
-
749
- yield freeze({ ...ref });
750
- yield t.arr();
833
+ if (!ref.value.flags.array) throw new Error();
751
834
 
752
835
  let first = true;
753
836
  for (const value of values) {
754
837
  if (!first) {
755
- yield t.buildReferenceTag('#');
756
- yield gap(buildToken('Punctuator', ','));
838
+ yield t.buildReferenceTag('#', 'separatorTokens');
839
+ yield gap(buildToken(null, separator));
757
840
  }
758
841
  yield freeze({ ...ref });
759
842
  yield gap(value || buildNullNode());
@@ -766,8 +849,8 @@ export const buildObjectProperties = (properties) => {
766
849
 
767
850
  return treeFromStream(
768
851
  concat(
769
- [t.doctype({ bablrLanguage: l.Instruction }), t.nodeOpen(t.nodeFlags)],
770
- buildCommaSeparatedList(properties, t.ref`properties[]`, expressions),
852
+ [t.doctype({ 'bablr-lang': l.Instruction }), t.fragOpen(t.nodeFlags)],
853
+ buildSeparatedList(',', properties, t.ref`properties[]`, expressions),
771
854
  [t.nodeClose()],
772
855
  ),
773
856
  { expressions },
@@ -782,12 +865,16 @@ export const buildObject = (properties) => {
782
865
  (function* () {
783
866
  yield t.nodeOpen(t.nodeFlags, 'Object');
784
867
  yield t.ref`openToken`;
785
- yield gap(buildToken('Punctuator', '{'));
868
+ yield gap(buildToken(null, '{'));
786
869
 
787
- yield* interpolateFragment(properties, t.ref`properties[]`, expressions);
870
+ yield* interpolateFragment(
871
+ isArray(properties) ? buildObjectProperties(properties) : properties,
872
+ t.ref`properties[]`,
873
+ expressions,
874
+ );
788
875
 
789
876
  yield t.ref`closeToken`;
790
- yield gap(buildToken('Punctuator', '}'));
877
+ yield gap(buildToken(null, '}'));
791
878
  yield t.nodeClose();
792
879
  })(),
793
880
  { expressions },
@@ -802,14 +889,14 @@ export const buildPattern = (alternatives, flags) => {
802
889
  (function* () {
803
890
  yield t.nodeOpen(t.nodeFlags, 'Pattern');
804
891
  yield t.ref`openToken`;
805
- yield gap(buildToken('Punctuator', '/'));
892
+ yield gap(buildToken(null, '/'));
806
893
 
807
894
  yield* interpolateFragment(alternatives, t.ref`alternatives[]`, expressions);
808
895
 
809
896
  yield t.ref`closeToken`;
810
- yield gap(buildToken('Punctuator', '/'));
897
+ yield gap(buildToken(null, '/'));
811
898
  yield t.ref`flags`;
812
- yield gap(flags || buildReferenceFlags());
899
+ yield gap(flags || buildRegexFlags());
813
900
  yield t.nodeClose();
814
901
  })(),
815
902
  { expressions },
@@ -824,12 +911,12 @@ export const buildRegexGroup = (alternatives, flags) => {
824
911
  (function* () {
825
912
  yield t.nodeOpen(t.nodeFlags, 'Group');
826
913
  yield t.ref`openToken`;
827
- yield gap(buildToken('Punctuator', '('));
914
+ yield gap(buildToken(null, '(?:'));
828
915
 
829
916
  yield* interpolateFragment(alternatives, t.ref`alternatives[]`, expressions);
830
917
 
831
918
  yield t.ref`closeToken`;
832
- yield gap(buildToken('Punctuator', ')'));
919
+ yield gap(buildToken(null, ')'));
833
920
  yield t.ref`flags`;
834
921
  yield gap(flags || buildReferenceFlags());
835
922
  yield t.nodeClose();
@@ -856,9 +943,9 @@ export const buildRegexFlags = (flags = '') => {
856
943
  yield t.nodeOpen(t.nodeFlags, 'Flags');
857
944
 
858
945
  for (const { 0: name, 1: chr } of Object.entries(flagCharacters)) {
859
- yield t.buildReferenceTag(name + 'Token');
946
+ yield t.buildReferenceTag(null, name + 'Token');
860
947
 
861
- yield gap(flags.includes(chr) ? buildToken('Punctuator', chr) : buildNullNode());
948
+ yield gap(flags.includes(chr) ? buildToken(null, chr) : buildNullNode());
862
949
  }
863
950
  yield t.nodeClose();
864
951
  })(),
@@ -885,18 +972,13 @@ export const buildAlternatives = (alternatives = []) => {
885
972
 
886
973
  return treeFromStream(
887
974
  (function* () {
888
- yield t.doctype({ bablrLanguage: l.Instruction });
889
- yield t.nodeOpen(t.nodeFlags);
890
- yield t.ref`.[]`;
891
- yield t.arr();
892
- yield t.ref`separatorTokens[]`;
893
- yield t.arr();
975
+ yield t.fragOpen();
894
976
 
895
977
  yield* alternatives
896
978
  .flatMap((alt) => [
897
- t.ref`.[]`,
979
+ t.ref`alternatives[]`,
898
980
  gap(alt),
899
- t.ref`separatorTokens[]`,
981
+ t.ref`#separatorTokens`,
900
982
  gap(buildPunctuator('|')),
901
983
  ])
902
984
  .slice(0, -2);
@@ -915,9 +997,9 @@ export const buildRegexGap = () => {
915
997
  [
916
998
  t.nodeOpen(t.nodeFlags, 'Gap'),
917
999
  t.ref`escapeToken`,
918
- gap(buildToken('Punctuator', '\\')),
1000
+ gap(buildToken(null, '\\')),
919
1001
  t.ref`value`,
920
- gap(buildToken('Keyword', 'g')),
1002
+ gap(buildToken(null, 'g')),
921
1003
  t.nodeClose(),
922
1004
  ],
923
1005
  { expressions },
@@ -930,14 +1012,33 @@ export const buildElements = (elements) => {
930
1012
 
931
1013
  return treeFromStream(
932
1014
  concat(
933
- [t.doctype({ bablrLanguage: l.Instruction }), t.nodeOpen(t.nodeFlags), t.ref`.[]+`, t.arr()],
934
- elements.flatMap((el) => [t.ref`.[]+`, gap(el)]),
1015
+ [t.doctype({ 'bablr-lang': l.Instruction }), t.fragOpen()],
1016
+ elements.flatMap((el) => [t.ref`elements[]+`, gap(el)]),
935
1017
  [t.nodeClose()],
936
1018
  ),
937
1019
  { expressions },
938
1020
  );
939
1021
  };
940
1022
 
1023
+ export const buildCharacterClass = (elements, negate = false) => {
1024
+ const expressions = [];
1025
+ const gap = buildFilledGapFunction(expressions);
1026
+
1027
+ return treeFromStream(
1028
+ (function* () {
1029
+ yield t.nodeOpen(t.nodeFlags, 'CharacterClass', null, { negate });
1030
+ yield t.ref`openToken`;
1031
+ yield gap(buildToken(null, '['));
1032
+
1033
+ yield* interpolateFragment(elements, t.ref`elements[]`, expressions);
1034
+ yield t.ref`closeToken`;
1035
+ yield gap(buildToken(null, ']'));
1036
+ yield t.nodeClose();
1037
+ })(),
1038
+ { expressions },
1039
+ );
1040
+ };
1041
+
941
1042
  export const buildJSExpressionDeep = (expr) => {
942
1043
  if (expr === null) {
943
1044
  return buildNull();
@@ -990,13 +1091,13 @@ export const buildTaggedString = (tag, content) => {
990
1091
  [
991
1092
  t.buildOpenNodeTag(t.nodeFlags, 'SpamexString'),
992
1093
  t.buildReferenceTag('sigilToken'),
993
- gap(buildToken('Keyword', tag)),
1094
+ gap(buildToken(null, tag)),
994
1095
  t.buildReferenceTag('openToken'),
995
- gap(buildToken('Punctuator', "'")),
1096
+ gap(buildToken(null, "'")),
996
1097
  t.buildReferenceTag('content'),
997
1098
  gap(content),
998
1099
  t.buildReferenceTag('closeToken'),
999
- gap(buildToken('Punctuator', "'")),
1100
+ gap(buildToken(null, "'")),
1000
1101
  t.buildCloseNodeTag(),
1001
1102
  ],
1002
1103
  { expressions },
@@ -1011,25 +1112,6 @@ export const buildRegexString = (content) => {
1011
1112
  return buildTaggedString('re', content);
1012
1113
  };
1013
1114
 
1014
- export const buildPropertyMatcher = (refMatcher, bindingMatcher, nodeMatcher) => {
1015
- const expressions = [];
1016
- const gap = buildFilledGapFunction(expressions);
1017
-
1018
- return treeFromStream(
1019
- [
1020
- t.nodeOpen(t.nodeFlags, 'PropertyMatcher'),
1021
- t.ref`refMatcher`,
1022
- gap(refMatcher || buildNullNode()),
1023
- t.ref`bindingMatcher`,
1024
- gap(bindingMatcher || buildNullNode()),
1025
- t.ref`nodeMatcher`,
1026
- gap(nodeMatcher),
1027
- t.nodeClose(),
1028
- ],
1029
- { expressions },
1030
- );
1031
- };
1032
-
1033
1115
  export const buildGapNodeMatcher = () => {
1034
1116
  return buildToken('GapNodeMatcher', '<//>');
1035
1117
  };