@bablr/helpers 0.23.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
- // import { i } from '@bablr/boot/shorthand.macro';
2
1
  import { interpolateFragment, buildFilledGapFunction } from '@bablr/agast-helpers/template';
3
- import {
4
- buildNullNode,
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
- import * as sumtree from '@bablr/agast-helpers/children';
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,86 +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`coverFragmentToken`,
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, intrinsicValue, 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
- let language_;
123
-
124
- if (!type) throw new Error();
209
+ if (!type && !name) throw new Error();
125
210
 
126
211
  return treeFromStream(
127
212
  (function* () {
128
- yield t.nodeOpen(t.nodeFlags, 'OpenNodeMatcher');
213
+ yield t.nodeOpen(t.nodeFlags, 'TreeNodeMatcherOpen');
129
214
  yield t.ref`openToken`;
130
- yield gap(buildToken('Punctuator', '<'));
215
+ yield gap(buildToken(null, '<'));
131
216
  yield t.ref`flags`;
132
217
  yield gap(flags);
133
218
  yield t.ref`type`;
134
- yield gap(typeof type === 'string' ? buildIdentifier(type) : type);
135
-
136
- yield* when(intrinsicValue, [t.ref`#`, ...buildSpace().children]);
219
+ yield gap(typeof type === 'string' ? buildToken(null, type) : type);
220
+ yield t.ref`name`;
221
+ yield gap(typeof name === 'string' ? buildIdentifier(name) : name);
137
222
 
138
- yield t.ref`intrinsicValue`;
139
- yield gap(intrinsicValue ? buildString(intrinsicValue) : buildNullNode());
223
+ yield* when(literalValue, [t.ref`#`, ...Tags.traverse(buildSpace().value.tags)]);
140
224
 
141
- let rootArr = getRootArray(attributes);
225
+ yield t.ref`literalValue`;
226
+ yield gap(literalValue ? buildString(literalValue) : buildNullNode());
142
227
 
143
- if (rootArr.length) {
228
+ if (!isEmpty(attributes)) {
144
229
  yield t.ref`#`;
145
- yield* buildSpace().children;
230
+ yield* buildSpace().value.tags;
146
231
  yield* interpolateFragment(attributes, t.ref`attributes[]`, expressions);
147
232
  }
148
233
 
149
- yield t.ref`selfClosingTagToken`;
150
- yield gap(buildToken('Punctuator', '/'));
234
+ yield t.ref`selfClosingToken`;
235
+ yield gap(buildToken(null, '/'));
151
236
  yield t.ref`closeToken`;
152
- yield gap(buildToken('Punctuator', '>'));
237
+ yield gap(buildToken(null, '>'));
153
238
  yield t.nodeClose();
154
239
  })(),
155
240
  { expressions },
156
241
  );
157
242
  };
158
243
 
159
- export const buildBasicNodeMatcher = (open) => {
244
+ export const buildTreeNodeMatcher = (open, children) => {
160
245
  const expressions = [];
161
246
  const gap = buildFilledGapFunction(expressions);
162
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
+
163
257
  return treeFromStream(
164
- [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
+ })(),
165
267
  { expressions },
166
268
  );
167
269
  };
168
270
 
169
- export const buildReferenceMatcher = (type, name, isArray, flags) => {
271
+ export const buildReferenceMatcher = (type = '.', name, flags) => {
170
272
  const expressions = [];
171
273
  const gap = buildFilledGapFunction(expressions);
172
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
+
173
288
  return treeFromStream(
174
289
  (function* () {
175
290
  yield t.nodeOpen(t.nodeFlags, 'ReferenceMatcher');
@@ -177,56 +292,31 @@ export const buildReferenceMatcher = (type, name, isArray, flags) => {
177
292
  yield gap(type && buildKeyword(type));
178
293
  yield t.ref`name`;
179
294
  yield gap(name && buildIdentifier(name));
180
- yield* (function* () {
181
- if (isArray) {
182
- yield t.ref`openIndexToken`;
183
- yield gap(buildToken('Punctuator', '['));
184
- yield t.ref`closeIndexToken`;
185
- yield gap(buildToken('Punctuator', ']'));
186
- }
187
- })();
188
295
  yield t.ref`flags`;
189
296
  yield gap(flags);
190
297
  yield t.ref`sigilToken`;
191
- yield gap(buildToken('Punctuator', ':'));
298
+ yield gap(buildToken(null, ':'));
192
299
  yield t.ref`#`;
193
- yield* sumtree.traverse(buildSpace().children);
300
+ yield* Tags.traverse(buildSpace().value.tags);
194
301
  yield t.nodeClose();
195
302
  })(),
196
303
  { expressions },
197
304
  );
198
305
  };
199
306
 
200
- export const buildFragmentMatcher = (flags = buildNodeFlags({ fragment: true })) => {
201
- const expressions = [];
202
- const gap = buildFilledGapFunction(expressions);
203
-
204
- if (!flags) throw new Error();
205
-
206
- return treeFromStream(
207
- (function* () {
208
- yield t.nodeOpen(t.nodeFlags, 'FragmentMatcher');
209
- yield t.ref`openToken`;
210
- yield gap(buildToken('Punctuator', '<'));
211
- yield t.ref`flags`;
212
- yield gap(flags);
213
- yield t.ref`closeToken`;
214
- yield gap(buildToken('Punctuator', '/>'));
215
- yield t.nodeClose();
216
- })(),
217
- { expressions },
218
- );
219
- };
220
-
221
- export const buildToken = (type, value, attributes = {}) => {
222
- 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
+ ]);
223
313
  };
224
314
 
225
315
  export const buildPunctuator = (value, attributes = {}) => {
226
- return buildToken('Punctuator', value, attributes);
316
+ return buildToken(null, value, attributes);
227
317
  };
228
318
 
229
- export const buildOpenNodeTag = (flags, type = null, attributes) => {
319
+ export const buildOpenNodeTag = (flags, name = null, attributes) => {
230
320
  const expressions = [];
231
321
  const gap = buildFilledGapFunction(expressions);
232
322
 
@@ -236,10 +326,9 @@ export const buildOpenNodeTag = (flags, type = null, attributes) => {
236
326
  yield gap(buildPunctuator('<'));
237
327
  yield t.ref`flags`;
238
328
  yield gap(buildNodeFlags(flags));
239
- yield t.ref`type`;
240
- yield gap(type ? buildIdentifier(type) : buildNullNode());
241
- let rootArr = getRootArray(attributes);
242
- 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())]);
243
332
  yield* interpolateFragment(attributes, t.ref`attributes[]`, expressions);
244
333
  yield t.ref`closeToken`;
245
334
  yield gap(buildPunctuator('>'));
@@ -256,20 +345,20 @@ export const buildDoctypeTag = (attributes) => {
256
345
  (function* () {
257
346
  yield t.nodeOpen(t.nodeFlags, 'DoctypeTag');
258
347
  yield t.ref`openToken`;
259
- yield gap(buildPunctuator('Punctuator', '<!'));
348
+ yield gap(buildPunctuator('<!'));
260
349
  yield t.ref`version`;
261
350
  yield gap(buildToken('PositiveInteger', '0'));
262
351
  yield t.ref`versionSeparator`;
263
- yield gap(buildPunctuator('Punctuator', ':'));
352
+ yield gap(buildPunctuator(':'));
264
353
  yield t.ref`doctype`;
265
354
  yield gap(buildKeyword('cstml'));
266
355
  yield t.nodeClose();
267
356
 
268
- yield* when(getRootArray(attributes).length, [t.ref`#`, ...buildSpace().children]);
357
+ yield* when(!isEmpty(attributes), [t.ref`#`, ...buildSpace().value.tags]);
269
358
  yield* interpolateFragment(attributes, t.ref`attributes[]`, expressions);
270
359
 
271
360
  yield t.ref`closeToken`;
272
- yield gap(buildToken('Punctuator', '>'));
361
+ yield gap(buildToken(null, '>'));
273
362
  })(),
274
363
  { expressions },
275
364
  );
@@ -287,19 +376,15 @@ export const buildIdentifierPath = (path) => {
287
376
  return treeFromStream(
288
377
  (function* () {
289
378
  yield t.nodeOpen(t.nodeFlags, 'IdentifierPath');
290
- yield t.ref`segments[]`;
291
- yield t.arr();
292
- yield t.ref`separatorTokens[]`;
293
- yield t.arr();
294
379
 
295
380
  yield* path_
296
- .flatMap((name) => [
381
+ .flatMap((segment) => [
297
382
  t.ref`segments[]`,
298
- gap(buildIdentifier(name)),
299
- t.ref`separatorTokens[]`,
300
- gap(buildToken('Punctuator', '.')),
383
+ gap(segment.type ? buildPunctuator(segment.type) : buildIdentifier(segment.name)),
384
+ t.ref`#separatorTokens`,
385
+ gap(buildToken(null, '.')),
301
386
  ])
302
- .slice(0, -1);
387
+ .slice(0, -2);
303
388
 
304
389
  yield t.nodeClose();
305
390
  })(),
@@ -321,9 +406,9 @@ export const buildCloseNodeTag = () => {
321
406
  [
322
407
  t.nodeOpen(t.nodeFlags, 'CloseNodeTag'),
323
408
  t.ref`openToken`,
324
- gap(buildToken('Punctuator', '</')),
409
+ gap(buildToken(null, '</')),
325
410
  t.ref`closeToken`,
326
- gap(buildToken('Punctuator', '>')),
411
+ gap(buildToken(null, '>')),
327
412
  t.nodeClose(),
328
413
  ],
329
414
  { expressions },
@@ -340,7 +425,8 @@ export const buildLiteralTag = (value) => {
340
425
  };
341
426
 
342
427
  export const buildTerminalProps = (matcher) => {
343
- const { attributes, value } = matcher.properties;
428
+ const value = get('value', matcher);
429
+ const attributes = get('attributes', matcher);
344
430
 
345
431
  return buildObject({ value, attributes });
346
432
  };
@@ -350,20 +436,68 @@ export const buildSpace = () => {
350
436
  };
351
437
 
352
438
  export const buildIdentifier = (name) => {
353
- if (!/^[a-zA-Z_]+$/.test(name)) throw new Error();
354
-
439
+ let unquoted = /^[a-zA-Z\u{80}-\u{10ffff}][a-zA-Z0-9_\u{80}-\u{10ffff}-]*$/uy.test(name);
355
440
  const expressions = [];
356
441
  const gap = buildFilledGapFunction(expressions);
357
442
 
358
- return treeFromStream(
359
- [
360
- t.nodeOpen(t.nodeFlags, 'Identifier'),
361
- t.ref`content`,
362
- gap(buildIdentifierContent(name)),
363
- t.nodeClose(),
364
- ],
365
- { expressions },
366
- );
443
+ if (unquoted) {
444
+ return treeFromStream(
445
+ [
446
+ t.nodeOpen(t.nodeFlags, 'Identifier'),
447
+ t.ref`content`,
448
+ gap(buildIdentifierContent(name)),
449
+ t.nodeClose(),
450
+ ],
451
+ { expressions },
452
+ );
453
+ } else {
454
+ return treeFromStream(
455
+ (function* () {
456
+ yield t.nodeOpen(t.nodeFlags, 'Identifier');
457
+
458
+ yield t.ref`openToken`;
459
+ yield gap(buildToken(null, '`'));
460
+ yield t.ref`content`;
461
+ yield t.nodeOpen(t.tokenFlags, 'IdentifierContent');
462
+
463
+ let lit = '';
464
+
465
+ let pieces = name.split(/[\\`]/g);
466
+
467
+ for (const piece of pieces) {
468
+ if (/[\\`]/y.test(piece)) {
469
+ let chr = piece;
470
+ if (lit) {
471
+ yield agastBuildLiteralTag(lit);
472
+ lit = '';
473
+ }
474
+
475
+ let value = buildKeyword(chr);
476
+
477
+ yield t.ref`@`;
478
+ yield t.nodeOpen(t.nodeFlags, 'EscapeSequence', null, { cooked: chr });
479
+ yield t.ref`escape`;
480
+ yield gap(buildToken(null, '\\'));
481
+ yield t.ref`value`;
482
+ yield gap(value);
483
+ yield t.nodeClose();
484
+ } else {
485
+ lit += piece;
486
+ }
487
+ }
488
+
489
+ if (lit) yield agastBuildLiteralTag(lit);
490
+ lit = '';
491
+
492
+ yield t.nodeClose();
493
+
494
+ yield t.ref`closeToken`;
495
+ yield gap(buildToken(null, '`'));
496
+ yield t.nodeClose();
497
+ })(),
498
+ { expressions },
499
+ );
500
+ }
367
501
  };
368
502
 
369
503
  export const buildIdentifierContent = (value) => {
@@ -371,7 +505,7 @@ export const buildIdentifierContent = (value) => {
371
505
  };
372
506
 
373
507
  export const buildKeyword = (name) => {
374
- return buildToken('Keyword', name);
508
+ return buildToken(null, name);
375
509
  };
376
510
 
377
511
  export const buildCall = (verb, args) => {
@@ -401,7 +535,7 @@ export const buildProperty = (key, value) => {
401
535
  yield t.ref`key`;
402
536
  yield gap(key);
403
537
  yield t.ref`mapOperator`;
404
- yield gap(buildToken('Punctuator', ':'));
538
+ yield gap(buildToken(null, ':'));
405
539
  yield t.ref`#`;
406
540
  yield gap(buildSpace());
407
541
  yield t.ref`value`;
@@ -431,7 +565,7 @@ export const buildInteger = (value, base = 10) => {
431
565
 
432
566
  return treeFromStream(
433
567
  concat(
434
- [t.nodeOpen(t.nodeFlags, 'Integer'), t.ref`digits[]`, t.arr()],
568
+ [t.nodeOpen(t.nodeFlags, 'Integer')],
435
569
  digits.flatMap((digit) => [t.ref`digits[]`, gap(buildDigit(digit))]),
436
570
  [t.nodeClose()],
437
571
  ),
@@ -457,9 +591,9 @@ export const buildInfinity = (value) => {
457
591
  [
458
592
  t.nodeOpen(t.nodeFlags, 'Infinity'),
459
593
  t.ref`sign`,
460
- gap(buildToken('Punctuator', sign)),
594
+ gap(buildToken(null, sign)),
461
595
  t.ref`value`,
462
- gap(buildToken('Keyword', 'Infinity')),
596
+ gap(buildToken(null, 'Infinity')),
463
597
  t.nodeClose(),
464
598
  ],
465
599
  { expressions },
@@ -486,11 +620,11 @@ export const buildString = (value) => {
486
620
  [
487
621
  t.nodeOpen(t.nodeFlags, 'String'),
488
622
  t.ref`openToken`,
489
- gap(buildToken('Punctuator', '"')),
623
+ gap(buildToken(null, '"')),
490
624
  t.ref`content`,
491
625
  gap(buildToken('StringContent', value)),
492
626
  t.ref`closeToken`,
493
- gap(buildToken('Punctuator', '"')),
627
+ gap(buildToken(null, '"')),
494
628
  t.nodeClose(),
495
629
  ],
496
630
  { expressions },
@@ -504,7 +638,7 @@ export const buildString = (value) => {
504
638
  (function* () {
505
639
  yield t.nodeOpen(t.nodeFlags, 'String');
506
640
  yield t.ref`openToken`;
507
- const tok = buildToken('Punctuator', "'");
641
+ const tok = buildToken(null, "'");
508
642
  yield gap(tok);
509
643
  yield t.ref`content`;
510
644
  yield t.nodeOpen(t.tokenFlags, 'StringContent');
@@ -539,8 +673,6 @@ export const buildString = (value) => {
539
673
  t.nodeOpen(t.nodeFlags, 'EscapeCode'),
540
674
  t.ref`sigilToken`,
541
675
  gap(buildKeyword(escapables[chr])),
542
- t.ref`digits[]`,
543
- t.arr(),
544
676
  t.nodeClose(),
545
677
  ],
546
678
  { expressions },
@@ -555,8 +687,6 @@ export const buildString = (value) => {
555
687
  t.nodeOpen(t.nodeFlags, 'EscapeCode'),
556
688
  t.ref`sigilToken`,
557
689
  gap(buildKeyword('u')),
558
- t.ref`digits[]`,
559
- t.arr(),
560
690
  [...hexDigits].flatMap((digit) => [t.ref`digits[]`, gap(buildDigit(digit))]),
561
691
  t.nodeClose(),
562
692
  ],
@@ -567,9 +697,9 @@ export const buildString = (value) => {
567
697
  }
568
698
 
569
699
  yield t.ref`@`;
570
- yield t.nodeOpen(t.nodeFlags, 'EscapeSequence', { cooked: chr });
700
+ yield t.nodeOpen(t.nodeFlags, 'EscapeSequence', null, { cooked: chr });
571
701
  yield t.ref`escape`;
572
- yield gap(buildToken('Punctuator', '\\'));
702
+ yield gap(buildToken(null, '\\'));
573
703
  yield t.ref`value`;
574
704
  yield gap(value);
575
705
  yield t.nodeClose();
@@ -596,7 +726,7 @@ export const buildString = (value) => {
596
726
 
597
727
  yield t.nodeClose();
598
728
  yield t.ref`closeToken`;
599
- yield gap(buildToken('Punctuator', "'"));
729
+ yield gap(buildToken(null, "'"));
600
730
  yield t.nodeClose();
601
731
  })(),
602
732
  { expressions },
@@ -611,7 +741,7 @@ export const buildBoolean = (value) => {
611
741
  [
612
742
  t.nodeOpen(t.nodeFlags, 'Boolean'),
613
743
  t.ref`sigilToken`,
614
- gap(buildToken('Keyword', value ? 'true' : 'false')),
744
+ gap(buildToken(null, value ? 'true' : 'false')),
615
745
  t.nodeClose(),
616
746
  ],
617
747
  { expressions },
@@ -626,7 +756,22 @@ export const buildNull = () => {
626
756
  [
627
757
  t.nodeOpen(t.nodeFlags, 'Null'),
628
758
  t.ref`sigilToken`,
629
- gap(buildToken('Keyword', 'null')),
759
+ gap(buildToken(null, 'null')),
760
+ t.nodeClose(),
761
+ ],
762
+ { expressions },
763
+ );
764
+ };
765
+
766
+ export const buildUndefined = () => {
767
+ const expressions = [];
768
+ const gap = buildFilledGapFunction(expressions);
769
+
770
+ return treeFromStream(
771
+ [
772
+ t.nodeOpen(t.nodeFlags, 'Undefined'),
773
+ t.ref`sigilToken`,
774
+ gap(buildToken(null, 'undefined')),
630
775
  t.nodeClose(),
631
776
  ],
632
777
  { expressions },
@@ -641,7 +786,7 @@ export const buildNullTag = () => {
641
786
  [
642
787
  t.nodeOpen(t.nodeFlags, 'NullTag'),
643
788
  t.ref`sigilToken`,
644
- gap(buildToken('Keyword', 'null')),
789
+ gap(buildToken(null, 'null')),
645
790
  t.nodeClose(),
646
791
  ],
647
792
  { expressions },
@@ -656,10 +801,14 @@ export const buildArray = (elements) => {
656
801
  (function* () {
657
802
  yield t.nodeOpen(t.nodeFlags, 'Array');
658
803
  yield t.ref`openToken`;
659
- yield gap(buildToken('Punctuator', '['));
660
- 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
+ );
661
810
  yield t.ref`closeToken`;
662
- yield gap(buildToken('Punctuator', ']'));
811
+ yield gap(buildToken(null, ']'));
663
812
  yield t.nodeClose();
664
813
  })(),
665
814
  { expressions },
@@ -670,28 +819,24 @@ export const buildArrayElements = (values) => {
670
819
  const expressions = [];
671
820
  return treeFromStream(
672
821
  (function* () {
673
- yield t.doctype({ bablrLanguage: l.Instruction });
674
- yield t.nodeOpen(t.nodeFlags);
675
- yield* buildSpaceSeparatedList(values, t.ref`.[]`, expressions);
822
+ yield t.fragOpen();
823
+ yield* buildSeparatedList(',', values, t.ref`elements[]`, expressions);
676
824
  yield t.nodeClose();
677
825
  })(),
678
826
  { expressions },
679
827
  );
680
828
  };
681
829
 
682
- export function* buildSpaceSeparatedList(values, ref, expressions) {
830
+ export function* buildSeparatedList(separator, values, ref, expressions) {
683
831
  const gap = buildFilledGapFunction(expressions);
684
832
 
685
- if (!ref.value.isArray) throw new Error();
686
-
687
- yield freeze({ ...ref });
688
- yield t.arr();
833
+ if (!ref.value.flags.array) throw new Error();
689
834
 
690
835
  let first = true;
691
836
  for (const value of values) {
692
837
  if (!first) {
693
- yield t.buildReferenceTag('#');
694
- yield gap(buildSpace());
838
+ yield t.buildReferenceTag('#', 'separatorTokens');
839
+ yield gap(buildToken(null, separator));
695
840
  }
696
841
  yield freeze({ ...ref });
697
842
  yield gap(value || buildNullNode());
@@ -704,8 +849,8 @@ export const buildObjectProperties = (properties) => {
704
849
 
705
850
  return treeFromStream(
706
851
  concat(
707
- [t.doctype({ bablrLanguage: l.Instruction }), t.nodeOpen(t.nodeFlags)],
708
- buildSpaceSeparatedList(properties, t.ref`properties[]`, expressions),
852
+ [t.doctype({ 'bablr-lang': l.Instruction }), t.fragOpen(t.nodeFlags)],
853
+ buildSeparatedList(',', properties, t.ref`properties[]`, expressions),
709
854
  [t.nodeClose()],
710
855
  ),
711
856
  { expressions },
@@ -720,12 +865,16 @@ export const buildObject = (properties) => {
720
865
  (function* () {
721
866
  yield t.nodeOpen(t.nodeFlags, 'Object');
722
867
  yield t.ref`openToken`;
723
- yield gap(buildToken('Punctuator', '{'));
868
+ yield gap(buildToken(null, '{'));
724
869
 
725
- yield* interpolateFragment(properties, t.ref`properties[]`, expressions);
870
+ yield* interpolateFragment(
871
+ isArray(properties) ? buildObjectProperties(properties) : properties,
872
+ t.ref`properties[]`,
873
+ expressions,
874
+ );
726
875
 
727
876
  yield t.ref`closeToken`;
728
- yield gap(buildToken('Punctuator', '}'));
877
+ yield gap(buildToken(null, '}'));
729
878
  yield t.nodeClose();
730
879
  })(),
731
880
  { expressions },
@@ -740,14 +889,14 @@ export const buildPattern = (alternatives, flags) => {
740
889
  (function* () {
741
890
  yield t.nodeOpen(t.nodeFlags, 'Pattern');
742
891
  yield t.ref`openToken`;
743
- yield gap(buildToken('Punctuator', '/'));
892
+ yield gap(buildToken(null, '/'));
744
893
 
745
894
  yield* interpolateFragment(alternatives, t.ref`alternatives[]`, expressions);
746
895
 
747
896
  yield t.ref`closeToken`;
748
- yield gap(buildToken('Punctuator', '/'));
897
+ yield gap(buildToken(null, '/'));
749
898
  yield t.ref`flags`;
750
- yield gap(flags || buildReferenceFlags());
899
+ yield gap(flags || buildRegexFlags());
751
900
  yield t.nodeClose();
752
901
  })(),
753
902
  { expressions },
@@ -762,12 +911,12 @@ export const buildRegexGroup = (alternatives, flags) => {
762
911
  (function* () {
763
912
  yield t.nodeOpen(t.nodeFlags, 'Group');
764
913
  yield t.ref`openToken`;
765
- yield gap(buildToken('Punctuator', '('));
914
+ yield gap(buildToken(null, '(?:'));
766
915
 
767
916
  yield* interpolateFragment(alternatives, t.ref`alternatives[]`, expressions);
768
917
 
769
918
  yield t.ref`closeToken`;
770
- yield gap(buildToken('Punctuator', ')'));
919
+ yield gap(buildToken(null, ')'));
771
920
  yield t.ref`flags`;
772
921
  yield gap(flags || buildReferenceFlags());
773
922
  yield t.nodeClose();
@@ -794,9 +943,9 @@ export const buildRegexFlags = (flags = '') => {
794
943
  yield t.nodeOpen(t.nodeFlags, 'Flags');
795
944
 
796
945
  for (const { 0: name, 1: chr } of Object.entries(flagCharacters)) {
797
- yield t.buildReferenceTag(name + 'Token');
946
+ yield t.buildReferenceTag(null, name + 'Token');
798
947
 
799
- yield gap(flags.includes(chr) ? buildToken('Punctuator', chr) : buildNullNode());
948
+ yield gap(flags.includes(chr) ? buildToken(null, chr) : buildNullNode());
800
949
  }
801
950
  yield t.nodeClose();
802
951
  })(),
@@ -823,18 +972,13 @@ export const buildAlternatives = (alternatives = []) => {
823
972
 
824
973
  return treeFromStream(
825
974
  (function* () {
826
- yield t.doctype({ bablrLanguage: l.Instruction });
827
- yield t.nodeOpen(t.nodeFlags);
828
- yield t.ref`.[]`;
829
- yield t.arr();
830
- yield t.ref`separatorTokens[]`;
831
- yield t.arr();
975
+ yield t.fragOpen();
832
976
 
833
977
  yield* alternatives
834
978
  .flatMap((alt) => [
835
- t.ref`.[]`,
979
+ t.ref`alternatives[]`,
836
980
  gap(alt),
837
- t.ref`separatorTokens[]`,
981
+ t.ref`#separatorTokens`,
838
982
  gap(buildPunctuator('|')),
839
983
  ])
840
984
  .slice(0, -2);
@@ -853,9 +997,9 @@ export const buildRegexGap = () => {
853
997
  [
854
998
  t.nodeOpen(t.nodeFlags, 'Gap'),
855
999
  t.ref`escapeToken`,
856
- gap(buildToken('Punctuator', '\\')),
1000
+ gap(buildToken(null, '\\')),
857
1001
  t.ref`value`,
858
- gap(buildToken('Keyword', 'g')),
1002
+ gap(buildToken(null, 'g')),
859
1003
  t.nodeClose(),
860
1004
  ],
861
1005
  { expressions },
@@ -868,14 +1012,77 @@ export const buildElements = (elements) => {
868
1012
 
869
1013
  return treeFromStream(
870
1014
  concat(
871
- [t.doctype({ bablrLanguage: l.Instruction }), t.nodeOpen(t.nodeFlags), t.ref`.[]+`, t.arr()],
872
- 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)]),
873
1017
  [t.nodeClose()],
874
1018
  ),
875
1019
  { expressions },
876
1020
  );
877
1021
  };
878
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
+
1042
+ export const buildJSExpressionDeep = (expr) => {
1043
+ if (expr === null) {
1044
+ return buildNull();
1045
+ } else if (expr === undefined) {
1046
+ return buildUndefined();
1047
+ }
1048
+
1049
+ switch (typeof expr) {
1050
+ case 'boolean':
1051
+ return buildBoolean(expr);
1052
+
1053
+ case 'string':
1054
+ return buildString(expr);
1055
+
1056
+ case 'number':
1057
+ return buildInteger(expr);
1058
+
1059
+ case 'object': {
1060
+ switch (Object.getPrototypeOf(expr)) {
1061
+ case Array.prototype:
1062
+ return buildArray(buildArrayElements(expr.map((e) => buildJSExpressionDeep(e))));
1063
+
1064
+ case Object.prototype:
1065
+ return buildObject(
1066
+ buildObjectProperties(
1067
+ Object.entries(expr).map((e) =>
1068
+ buildProperty(
1069
+ /^[a-zA-Z_]+$/.test(e[0]) ? buildIdentifier(e[0]) : buildString(e[0]),
1070
+ buildJSExpressionDeep(e[1]),
1071
+ ),
1072
+ ),
1073
+ ),
1074
+ );
1075
+
1076
+ default:
1077
+ throw new Error();
1078
+ }
1079
+ }
1080
+
1081
+ default:
1082
+ throw new Error();
1083
+ }
1084
+ };
1085
+
879
1086
  export const buildTaggedString = (tag, content) => {
880
1087
  const expressions = [];
881
1088
  const gap = buildFilledGapFunction(expressions);
@@ -884,13 +1091,13 @@ export const buildTaggedString = (tag, content) => {
884
1091
  [
885
1092
  t.buildOpenNodeTag(t.nodeFlags, 'SpamexString'),
886
1093
  t.buildReferenceTag('sigilToken'),
887
- gap(buildToken('Keyword', tag)),
1094
+ gap(buildToken(null, tag)),
888
1095
  t.buildReferenceTag('openToken'),
889
- gap(buildToken('Punctuator', "'")),
1096
+ gap(buildToken(null, "'")),
890
1097
  t.buildReferenceTag('content'),
891
1098
  gap(content),
892
1099
  t.buildReferenceTag('closeToken'),
893
- gap(buildToken('Punctuator', "'")),
1100
+ gap(buildToken(null, "'")),
894
1101
  t.buildCloseNodeTag(),
895
1102
  ],
896
1103
  { expressions },
@@ -905,25 +1112,6 @@ export const buildRegexString = (content) => {
905
1112
  return buildTaggedString('re', content);
906
1113
  };
907
1114
 
908
- export const buildPropertyMatcher = (refMatcher, bindingMatcher, nodeMatcher) => {
909
- const expressions = [];
910
- const gap = buildFilledGapFunction(expressions);
911
-
912
- return treeFromStream(
913
- [
914
- t.nodeOpen(t.nodeFlags, 'PropertyMatcher'),
915
- t.ref`refMatcher`,
916
- gap(refMatcher || buildNullNode()),
917
- t.ref`bindingMatcher`,
918
- gap(bindingMatcher || buildNullNode()),
919
- t.ref`nodeMatcher`,
920
- gap(nodeMatcher),
921
- t.nodeClose(),
922
- ],
923
- { expressions },
924
- );
925
- };
926
-
927
1115
  export const buildGapNodeMatcher = () => {
928
1116
  return buildToken('GapNodeMatcher', '<//>');
929
1117
  };