@bablr/helpers 0.22.1 → 0.24.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,47 +1,40 @@
1
- // import { i } from '@bablr/boot/shorthand.macro';
2
1
  import { interpolateFragment, buildFilledGapFunction } from '@bablr/agast-helpers/template';
3
2
  import {
4
3
  buildNullNode,
5
- isNull,
4
+ get,
5
+ getRootArray,
6
6
  treeFromStreamSync as treeFromStream,
7
7
  } from '@bablr/agast-helpers/tree';
8
8
  import { buildLiteralTag as agastBuildLiteralTag } from '@bablr/agast-helpers/builders';
9
9
  import * as t from '@bablr/agast-helpers/shorthand';
10
- import * as sumtree from '@bablr/agast-helpers/sumtree';
10
+ import * as Tags from '@bablr/agast-helpers/tags';
11
11
  import * as l from '@bablr/agast-vm-helpers/languages';
12
12
  import { concat } from '@bablr/agast-vm-helpers/iterable';
13
13
 
14
- const { getPrototypeOf, freeze, hasOwn } = Object;
14
+ const { freeze } = Object;
15
15
  const { isArray } = Array;
16
16
 
17
17
  const when = (condition, value) => (condition ? value : { *[Symbol.iterator]() {} });
18
18
 
19
19
  const isString = (val) => typeof val === 'string';
20
20
 
21
- export const buildReferenceTag = (
22
- name,
23
- isArray = false,
24
- flags = t.referenceFlags,
25
- index = null,
26
- ) => {
21
+ export const buildReferenceTag = (name, isArray = false, flags = t.referenceFlags) => {
27
22
  let expressions = [];
28
23
  const gap = buildFilledGapFunction(expressions);
29
24
 
30
25
  return treeFromStream(
31
26
  [
32
- t.nodeOpen(t.nodeFlags, l.CSTML, 'ReferenceTag'),
27
+ t.nodeOpen(t.nodeFlags, 'ReferenceTag'),
33
28
  t.ref`name`,
34
29
  gap(name ? buildIdentifier(name) : buildNullNode()),
35
30
  t.ref`openIndexToken`,
36
- gap(isArray ? buildToken(l.CSTML, 'Punctuator', '[') : buildNullNode()),
37
- t.ref`index`,
38
- gap(index || buildNullNode()),
31
+ gap(isArray ? buildToken('Punctuator', '[') : buildNullNode()),
39
32
  t.ref`closeIndexToken`,
40
- gap(isArray ? buildToken(l.CSTML, 'Punctuator', ']') : buildNullNode()),
33
+ gap(isArray ? buildToken('Punctuator', ']') : buildNullNode()),
41
34
  t.ref`flags`,
42
35
  gap(flags ? buildReferenceFlags(flags) : buildNullNode()),
43
36
  t.ref`sigilToken`,
44
- gap(buildToken(l.CSTML, 'Punctuator', ':')),
37
+ gap(buildToken('Punctuator', ':')),
45
38
  t.nodeClose(),
46
39
  ],
47
40
  { expressions },
@@ -54,9 +47,9 @@ export const buildGapTag = () => {
54
47
 
55
48
  return treeFromStream(
56
49
  [
57
- t.nodeOpen(t.nodeFlags, l.CSTML, 'ShiftTag'),
50
+ t.nodeOpen(t.nodeFlags, 'ShiftTag'),
58
51
  t.ref`sigilToken`,
59
- gap(buildToken(l.CSTML, 'Punctuator', '<//>')),
52
+ gap(buildToken('Punctuator', '<//>')),
60
53
  t.nodeClose(),
61
54
  ],
62
55
  { expressions },
@@ -69,9 +62,9 @@ export const buildShiftTag = () => {
69
62
 
70
63
  return treeFromStream(
71
64
  [
72
- t.nodeOpen(t.nodeFlags, l.CSTML, 'ShiftTag'),
65
+ t.nodeOpen(t.nodeFlags, 'ShiftTag'),
73
66
  t.ref`sigilToken`,
74
- gap(buildToken(l.CSTML, 'Punctuator', '^^^')),
67
+ gap(buildToken('Punctuator', '^^^')),
75
68
  t.nodeClose(),
76
69
  ],
77
70
  { expressions },
@@ -85,11 +78,11 @@ export const buildReferenceFlags = (flags = t.referenceFlags) => {
85
78
 
86
79
  return treeFromStream(
87
80
  [
88
- t.nodeOpen(t.nodeFlags, l.CSTML, 'ReferenceFlags'),
81
+ t.nodeOpen(t.nodeFlags, 'ReferenceFlags'),
89
82
  t.ref`expressionToken`,
90
- gap(expression ? buildToken(l.CSTML, 'Punctuator', '+') : buildNullNode()),
83
+ gap(expression ? buildToken('Punctuator', '+') : buildNullNode()),
91
84
  t.ref`hasGapToken`,
92
- gap(hasGap ? buildToken(l.CSTML, 'Punctuator', '$') : buildNullNode()),
85
+ gap(hasGap ? buildToken('Punctuator', '$') : buildNullNode()),
93
86
  t.nodeClose(),
94
87
  ],
95
88
  { expressions },
@@ -97,17 +90,21 @@ export const buildReferenceFlags = (flags = t.referenceFlags) => {
97
90
  };
98
91
 
99
92
  export const buildNodeFlags = (flags = t.nodeFlags) => {
100
- const { token = null, hasGap = null } = flags;
93
+ const { token = null, hasGap = null, fragment = null, cover = null } = flags;
101
94
  let expressions = [];
102
95
  const gap = buildFilledGapFunction(expressions);
103
96
 
104
97
  return treeFromStream(
105
98
  [
106
- t.nodeOpen(t.nodeFlags, l.CSTML, 'NodeFlags'),
107
- t.ref`triviaToken`,
108
- gap(token ? buildToken(l.CSTML, 'Punctuator', '*') : buildNullNode()),
99
+ t.nodeOpen(t.nodeFlags, 'NodeFlags'),
100
+ t.ref`tokenToken`,
101
+ gap(token ? buildToken('Punctuator', '*') : buildNullNode()),
109
102
  t.ref`hasGapToken`,
110
- gap(hasGap ? buildToken(l.CSTML, 'Punctuator', '$') : buildNullNode()),
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()),
111
108
  t.nodeClose(),
112
109
  ],
113
110
  { expressions },
@@ -115,59 +112,42 @@ export const buildNodeFlags = (flags = t.nodeFlags) => {
115
112
  };
116
113
 
117
114
  export const buildSpamMatcher = (type = null, value = null, attributes = null) => {
118
- return buildOpenNodeMatcher(t.nodeFlags, null, type, value, attributes);
115
+ return buildOpenNodeMatcher(buildNodeFlags(t.nodeFlags), null, type, value, attributes);
119
116
  };
120
117
 
121
- export const buildOpenNodeMatcher = (flags, language, type, intrinsicValue, attributes = null) => {
118
+ export const buildOpenNodeMatcher = (flags, type, literalValue, attributes = null) => {
122
119
  const expressions = [];
123
120
  const gap = buildFilledGapFunction(expressions);
124
121
 
125
- let language_;
126
-
127
122
  if (!type) throw new Error();
128
123
 
129
- if (isString(language)) {
130
- language_ = language;
131
- } else {
132
- let lArr = isString(language) ? language : language ? [...language] : [];
133
-
134
- language_ = lArr.length === 0 ? null : lArr;
135
- }
136
-
137
124
  return treeFromStream(
138
125
  (function* () {
139
- yield t.nodeOpen(t.nodeFlags, l.Spamex, 'OpenNodeMatcher');
126
+ yield t.nodeOpen(t.nodeFlags, 'OpenNodeMatcher');
140
127
  yield t.ref`openToken`;
141
- yield gap(buildToken(l.CSTML, 'Punctuator', '<'));
128
+ yield gap(buildToken('Punctuator', '<'));
142
129
  yield t.ref`flags`;
143
- yield gap(buildNodeFlags(flags));
144
- yield t.ref`language`;
145
- yield gap(language_ ? buildLanguage(language_) : buildNullNode());
146
- yield t.ref`languageSeparator`;
147
- yield gap(language_ && type ? buildToken(l.CSTML, 'Punctuator', ':') : buildNullNode());
130
+ yield gap(flags);
148
131
  yield t.ref`type`;
149
- yield gap(
150
- typeof type === 'string'
151
- ? ['.', '#', '@'].includes(type)
152
- ? buildKeyword(type)
153
- : buildIdentifier(type)
154
- : type,
155
- );
132
+ yield gap(typeof type === 'string' ? buildIdentifier(type) : type);
133
+
134
+ yield* when(literalValue, [t.ref`#`, ...buildSpace().tags]);
156
135
 
157
- yield* when(intrinsicValue, [t.ref`#`, ...buildSpace().children]);
136
+ yield t.ref`literalValue`;
137
+ yield gap(literalValue ? buildString(literalValue) : buildNullNode());
158
138
 
159
- yield t.ref`intrinsicValue`;
160
- yield gap(intrinsicValue ? buildString(intrinsicValue) : buildNullNode());
139
+ let rootArr = getRootArray(attributes);
161
140
 
162
- yield* when(attributes?.properties['.'].length, [t.ref`#`, ...buildSpace().children]);
163
- if (attributes?.properties['.'].length) {
141
+ if (rootArr.length) {
142
+ yield t.ref`#`;
143
+ yield* buildSpace().tags;
164
144
  yield* interpolateFragment(attributes, t.ref`attributes[]`, expressions);
165
145
  }
166
146
 
167
- yield t.ref`selfClosingTagToken`;
168
- yield gap(buildToken(l.CSTML, 'Punctuator', '/'));
147
+ yield t.ref`selfClosingToken`;
148
+ yield gap(buildToken('Punctuator', '/'));
169
149
  yield t.ref`closeToken`;
170
- yield gap(buildToken(l.CSTML, 'Punctuator', '>'));
150
+ yield gap(buildToken('Punctuator', '>'));
171
151
  yield t.nodeClose();
172
152
  })(),
173
153
  { expressions },
@@ -179,95 +159,88 @@ export const buildBasicNodeMatcher = (open) => {
179
159
  const gap = buildFilledGapFunction(expressions);
180
160
 
181
161
  return treeFromStream(
182
- [t.nodeOpen(t.nodeFlags, l.Spamex, 'BasicNodeMatcher'), t.ref`open`, gap(open), t.nodeClose()],
162
+ [t.nodeOpen(t.nodeFlags, 'BasicNodeMatcher'), t.ref`open`, gap(open), t.nodeClose()],
183
163
  { expressions },
184
164
  );
185
165
  };
186
166
 
187
- export const buildReferenceMatcher = (name, isArray, flags) => {
167
+ export const buildReferenceMatcher = (type, name, isArray, flags) => {
188
168
  const expressions = [];
189
169
  const gap = buildFilledGapFunction(expressions);
190
170
 
191
171
  return treeFromStream(
192
172
  (function* () {
193
- yield t.nodeOpen(t.nodeFlags, l.Spamex, 'ReferenceMatcher');
173
+ yield t.nodeOpen(t.nodeFlags, 'ReferenceMatcher');
174
+ yield t.ref`type`;
175
+ yield gap(type && buildKeyword(type));
194
176
  yield t.ref`name`;
195
- yield gap(['#', '@', '.'].includes(name) ? buildKeyword(name) : buildIdentifier(name));
177
+ yield gap(name && buildIdentifier(name));
196
178
  yield* (function* () {
197
179
  if (isArray) {
198
180
  yield t.ref`openIndexToken`;
199
- yield gap(buildToken(l.CSTML, 'Punctuator', '['));
181
+ yield gap(buildToken('Punctuator', '['));
200
182
  yield t.ref`closeIndexToken`;
201
- yield gap(buildToken(l.CSTML, 'Punctuator', ']'));
183
+ yield gap(buildToken('Punctuator', ']'));
202
184
  }
203
185
  })();
204
186
  yield t.ref`flags`;
205
187
  yield gap(flags);
206
188
  yield t.ref`sigilToken`;
207
- yield gap(buildToken(l.CSTML, 'Punctuator', ':'));
189
+ yield gap(buildToken('Punctuator', ':'));
208
190
  yield t.ref`#`;
209
- yield* sumtree.traverse(buildSpace().children);
191
+ yield* Tags.traverse(buildSpace().tags);
210
192
  yield t.nodeClose();
211
193
  })(),
212
194
  { expressions },
213
195
  );
214
196
  };
215
197
 
216
- export const buildFragmentMatcher = (flags) => {
198
+ export const buildFragmentMatcher = (flags = buildNodeFlags({ fragment: true, cover: true })) => {
217
199
  const expressions = [];
218
200
  const gap = buildFilledGapFunction(expressions);
219
201
 
202
+ if (!flags) throw new Error();
203
+
220
204
  return treeFromStream(
221
205
  (function* () {
222
- yield t.nodeOpen(t.nodeFlags, l.Spamex, 'FragmentMatcher');
206
+ yield t.nodeOpen(t.fragmentFlags, 'FragmentMatcher');
223
207
  yield t.ref`openToken`;
224
- yield gap(buildToken(l.CSTML, 'Punctuator', '<'));
208
+ yield gap(buildToken('Punctuator', '<'));
225
209
  yield t.ref`flags`;
226
210
  yield gap(flags);
227
- yield t.ref`#`;
228
- yield* sumtree.traverse(buildSpace().children);
229
211
  yield t.ref`closeToken`;
230
- yield gap(buildToken(l.CSTML, 'Punctuator', '/>'));
212
+ yield gap(buildToken('Punctuator', '/>'));
231
213
  yield t.nodeClose();
232
214
  })(),
233
215
  { expressions },
234
216
  );
235
217
  };
236
218
 
237
- export const buildToken = (language, type, value, attributes = {}) => {
238
- return treeFromStream([
239
- t.nodeOpen(t.tokenFlags, language, type, attributes),
240
- t.lit(value),
241
- t.nodeClose(),
242
- ]);
219
+ export const buildToken = (type, value, attributes = {}) => {
220
+ return treeFromStream([t.nodeOpen(t.tokenFlags, type, attributes), t.lit(value), t.nodeClose()]);
243
221
  };
244
222
 
245
- export const buildPunctuator = (language, value, attributes = {}) => {
246
- return buildToken(language, 'Punctuator', value, attributes);
223
+ export const buildPunctuator = (value, attributes = {}) => {
224
+ return buildToken('Punctuator', value, attributes);
247
225
  };
248
226
 
249
- export const buildOpenNodeTag = (flags, language, type = null, attributes) => {
227
+ export const buildOpenNodeTag = (flags, type = null, attributes) => {
250
228
  const expressions = [];
251
229
  const gap = buildFilledGapFunction(expressions);
252
230
 
253
- let language_ = !language || language.length === 0 ? null : language;
254
-
255
231
  return treeFromStream(
256
232
  (function* () {
257
233
  yield t.ref`openToken`;
258
- yield gap(buildPunctuator(l.CSTML, '<'));
234
+ yield gap(buildPunctuator('<'));
259
235
  yield t.ref`flags`;
260
236
  yield gap(buildNodeFlags(flags));
261
- yield t.ref`language`;
262
- yield gap(language_ && type ? buildLanguage(language_) : buildNullNode());
263
- yield t.ref`languageSeparator`;
264
- yield gap(language_ && type ? buildPunctuator(l.CSTML, ':') : buildNullNode());
265
237
  yield t.ref`type`;
266
238
  yield gap(type ? buildIdentifier(type) : buildNullNode());
267
- yield* when(attributes.properties['.'].length, [t.ref`#`, gap(buildSpace())]);
239
+ let rootArr = getRootArray(attributes);
240
+ yield* when(rootArr.length, [t.ref`#`, gap(buildSpace())]);
268
241
  yield* interpolateFragment(attributes, t.ref`attributes[]`, expressions);
269
242
  yield t.ref`closeToken`;
270
- yield gap(buildPunctuator(l.CSTML, '>'));
243
+ yield gap(buildPunctuator('>'));
271
244
  })(),
272
245
  { expressions },
273
246
  );
@@ -279,22 +252,22 @@ export const buildDoctypeTag = (attributes) => {
279
252
 
280
253
  return treeFromStream(
281
254
  (function* () {
282
- yield t.nodeOpen(t.nodeFlags, l.CSTML, 'DoctypeTag');
255
+ yield t.nodeOpen(t.nodeFlags, 'DoctypeTag');
283
256
  yield t.ref`openToken`;
284
- yield gap(buildPunctuator(l.CSTML, 'Punctuator', '<!'));
257
+ yield gap(buildPunctuator('Punctuator', '<!'));
285
258
  yield t.ref`version`;
286
- yield gap(buildToken(l.CSTML, 'PositiveInteger', '0'));
259
+ yield gap(buildToken('PositiveInteger', '0'));
287
260
  yield t.ref`versionSeparator`;
288
- yield gap(buildPunctuator(l.CSTML, 'Punctuator', ':'));
261
+ yield gap(buildPunctuator('Punctuator', ':'));
289
262
  yield t.ref`doctype`;
290
- yield gap(buildKeyword(l.CSTML, 'cstml'));
263
+ yield gap(buildKeyword('cstml'));
291
264
  yield t.nodeClose();
292
265
 
293
- yield* when(attributes.properties['.'].length, [t.ref`#`, ...buildSpace().children]);
266
+ yield* when(getRootArray(attributes).length, [t.ref`#`, ...buildSpace().tags]);
294
267
  yield* interpolateFragment(attributes, t.ref`attributes[]`, expressions);
295
268
 
296
269
  yield t.ref`closeToken`;
297
- yield gap(buildToken(l.CSTML, 'Punctuator', '>'));
270
+ yield gap(buildToken('Punctuator', '>'));
298
271
  })(),
299
272
  { expressions },
300
273
  );
@@ -311,7 +284,7 @@ export const buildIdentifierPath = (path) => {
311
284
 
312
285
  return treeFromStream(
313
286
  (function* () {
314
- yield t.nodeOpen(t.nodeFlags, l.CSTML, 'IdentifierPath');
287
+ yield t.nodeOpen(t.nodeFlags, 'IdentifierPath');
315
288
  yield t.ref`segments[]`;
316
289
  yield t.arr();
317
290
  yield t.ref`separatorTokens[]`;
@@ -322,7 +295,7 @@ export const buildIdentifierPath = (path) => {
322
295
  t.ref`segments[]`,
323
296
  gap(buildIdentifier(name)),
324
297
  t.ref`separatorTokens[]`,
325
- gap(buildToken(l.CSTML, 'Punctuator', '.')),
298
+ gap(buildToken('Punctuator', '.')),
326
299
  ])
327
300
  .slice(0, -1);
328
301
 
@@ -338,23 +311,17 @@ export const buildLanguage = (language) => {
338
311
  : buildIdentifierPath(language);
339
312
  };
340
313
 
341
- export const buildCloseNodeTag = (type, language) => {
314
+ export const buildCloseNodeTag = () => {
342
315
  const expressions = [];
343
316
  const gap = buildFilledGapFunction(expressions);
344
317
 
345
318
  return treeFromStream(
346
319
  [
347
- t.nodeOpen(t.nodeFlags, l.CSTML, 'CloseNodeTag'),
320
+ t.nodeOpen(t.nodeFlags, 'CloseNodeTag'),
348
321
  t.ref`openToken`,
349
- gap(buildToken(l.CSTML, 'Punctuator', '</')),
350
- t.ref`language`,
351
- t.gap(language ? buildLanguage(language) : buildNullNode()),
352
- t.ref`languageSeparator`,
353
- gap(language && type ? buildToken(l.CSTML, 'Punctuator', ':') : buildNullNode()),
354
- t.ref`type`,
355
- gap(type ? buildIdentifier(type) : buildNullNode()),
322
+ gap(buildToken('Punctuator', '</')),
356
323
  t.ref`closeToken`,
357
- gap(buildToken(l.CSTML, 'Punctuator', '>')),
324
+ gap(buildToken('Punctuator', '>')),
358
325
  t.nodeClose(),
359
326
  ],
360
327
  { expressions },
@@ -363,7 +330,7 @@ export const buildCloseNodeTag = (type, language) => {
363
330
 
364
331
  export const buildLiteralTag = (value) => {
365
332
  return treeFromStream([
366
- t.nodeOpen(t.nodeFlags, l.Instruction, 'LiteralTag'),
333
+ t.nodeOpen(t.nodeFlags, 'LiteralTag'),
367
334
  t.ref`value`,
368
335
  t.lit(value),
369
336
  t.nodeClose(),
@@ -371,38 +338,87 @@ export const buildLiteralTag = (value) => {
371
338
  };
372
339
 
373
340
  export const buildTerminalProps = (matcher) => {
374
- const { attributes, value } = matcher.properties;
341
+ const value = get('value', matcher);
342
+ const attributes = get('attributes', matcher);
375
343
 
376
344
  return buildObject({ value, attributes });
377
345
  };
378
346
 
379
347
  export const buildSpace = () => {
380
- return buildToken(l.Space, 'Space', ' ');
348
+ return buildToken('Space', ' ');
381
349
  };
382
350
 
383
351
  export const buildIdentifier = (name) => {
384
- if (!/^[a-zA-Z_]+$/.test(name)) throw new Error();
385
-
352
+ let unquoted = /^[a-zA-Z\u{80}-\u{10ffff}][a-zA-Z0-9_\u{80}-\u{10ffff}-]*$/uy.test(name);
386
353
  const expressions = [];
387
354
  const gap = buildFilledGapFunction(expressions);
388
355
 
389
- return treeFromStream(
390
- [
391
- t.nodeOpen(t.nodeFlags, l.CSTML, 'Identifier'),
392
- t.ref`content`,
393
- gap(buildIdentifierContent(name)),
394
- t.nodeClose(),
395
- ],
396
- { expressions },
397
- );
356
+ if (unquoted) {
357
+ return treeFromStream(
358
+ [
359
+ t.nodeOpen(t.nodeFlags, 'Identifier'),
360
+ t.ref`content`,
361
+ gap(buildIdentifierContent(name)),
362
+ t.nodeClose(),
363
+ ],
364
+ { expressions },
365
+ );
366
+ } else {
367
+ return treeFromStream(
368
+ (function* () {
369
+ yield t.nodeOpen(t.nodeFlags, 'Identifier');
370
+
371
+ yield t.ref`openToken`;
372
+ yield gap(buildToken('Punctuator', '`'));
373
+ yield t.ref`content`;
374
+ yield t.nodeOpen(t.tokenFlags, 'IdentifierContent');
375
+
376
+ let lit = '';
377
+
378
+ let pieces = name.split(/[\\`]/g);
379
+
380
+ for (const piece of pieces) {
381
+ if (/[\\`]/y.test(piece)) {
382
+ let chr = piece;
383
+ if (lit) {
384
+ yield agastBuildLiteralTag(lit);
385
+ lit = '';
386
+ }
387
+
388
+ let value = buildKeyword(chr);
389
+
390
+ yield t.ref`@`;
391
+ yield t.nodeOpen(t.nodeFlags, 'EscapeSequence', { cooked: chr });
392
+ yield t.ref`escape`;
393
+ yield gap(buildToken('Punctuator', '\\'));
394
+ yield t.ref`value`;
395
+ yield gap(value);
396
+ yield t.nodeClose();
397
+ } else {
398
+ lit += piece;
399
+ }
400
+ }
401
+
402
+ if (lit) yield agastBuildLiteralTag(lit);
403
+ lit = '';
404
+
405
+ yield t.nodeClose();
406
+
407
+ yield t.ref`closeToken`;
408
+ yield gap(buildToken('Punctuator', '`'));
409
+ yield t.nodeClose();
410
+ })(),
411
+ { expressions },
412
+ );
413
+ }
398
414
  };
399
415
 
400
416
  export const buildIdentifierContent = (value) => {
401
- return buildToken(l.CSTML, 'IdentifierContent', value);
417
+ return buildToken('IdentifierContent', value);
402
418
  };
403
419
 
404
420
  export const buildKeyword = (name) => {
405
- return buildToken(l.Instruction, 'Keyword', name);
421
+ return buildToken('Keyword', name);
406
422
  };
407
423
 
408
424
  export const buildCall = (verb, args) => {
@@ -411,7 +427,7 @@ export const buildCall = (verb, args) => {
411
427
 
412
428
  return treeFromStream(
413
429
  [
414
- t.nodeOpen(t.nodeFlags, l.Instruction, 'Call'),
430
+ t.nodeOpen(t.nodeFlags, 'Call'),
415
431
  t.ref`verb`,
416
432
  gap(verb),
417
433
  t.ref`arguments`,
@@ -428,11 +444,11 @@ export const buildProperty = (key, value) => {
428
444
 
429
445
  return treeFromStream(
430
446
  (function* () {
431
- yield t.nodeOpen(t.nodeFlags, l.Instruction, 'Property');
447
+ yield t.nodeOpen(t.nodeFlags, 'Property');
432
448
  yield t.ref`key`;
433
449
  yield gap(key);
434
450
  yield t.ref`mapOperator`;
435
- yield gap(buildToken(l.Instruction, 'Punctuator', ':'));
451
+ yield gap(buildToken('Punctuator', ':'));
436
452
  yield t.ref`#`;
437
453
  yield gap(buildSpace());
438
454
  yield t.ref`value`;
@@ -451,7 +467,7 @@ const escapables = {
451
467
  };
452
468
 
453
469
  export const buildDigit = (value) => {
454
- return buildToken(l.CSTML, 'Digit', value);
470
+ return buildToken('Digit', value);
455
471
  };
456
472
 
457
473
  export const buildInteger = (value, base = 10) => {
@@ -462,7 +478,7 @@ export const buildInteger = (value, base = 10) => {
462
478
 
463
479
  return treeFromStream(
464
480
  concat(
465
- [t.nodeOpen(t.nodeFlags, l.CSTML, 'Integer'), t.ref`digits[]`, t.arr()],
481
+ [t.nodeOpen(t.nodeFlags, 'Integer'), t.ref`digits[]`, t.arr()],
466
482
  digits.flatMap((digit) => [t.ref`digits[]`, gap(buildDigit(digit))]),
467
483
  [t.nodeClose()],
468
484
  ),
@@ -486,11 +502,11 @@ export const buildInfinity = (value) => {
486
502
 
487
503
  return treeFromStream(
488
504
  [
489
- t.nodeOpen(t.nodeFlags, l.CSTML, 'Infinity'),
505
+ t.nodeOpen(t.nodeFlags, 'Infinity'),
490
506
  t.ref`sign`,
491
- gap(buildToken(l.CSTML, 'Punctuator', sign)),
507
+ gap(buildToken('Punctuator', sign)),
492
508
  t.ref`value`,
493
- gap(buildToken(l.CSTML, 'Keyword', 'Infinity')),
509
+ gap(buildToken('Keyword', 'Infinity')),
494
510
  t.nodeClose(),
495
511
  ],
496
512
  { expressions },
@@ -506,6 +522,7 @@ export const buildNumber = (value) => {
506
522
  };
507
523
 
508
524
  export const buildString = (value) => {
525
+ if (value == null) throw new Error();
509
526
  const pieces = isArray(value) ? value : [value];
510
527
  let lit = '';
511
528
 
@@ -514,13 +531,13 @@ export const buildString = (value) => {
514
531
  const gap = buildFilledGapFunction(expressions);
515
532
  return treeFromStream(
516
533
  [
517
- t.nodeOpen(t.nodeFlags, l.JSON, 'String'),
534
+ t.nodeOpen(t.nodeFlags, 'String'),
518
535
  t.ref`openToken`,
519
- gap(buildToken(l.JSON, 'Punctuator', '"')),
536
+ gap(buildToken('Punctuator', '"')),
520
537
  t.ref`content`,
521
- gap(buildToken(l.JSON, 'StringContent', value)),
538
+ gap(buildToken('StringContent', value)),
522
539
  t.ref`closeToken`,
523
- gap(buildToken(l.JSON, 'Punctuator', '"')),
540
+ gap(buildToken('Punctuator', '"')),
524
541
  t.nodeClose(),
525
542
  ],
526
543
  { expressions },
@@ -532,12 +549,12 @@ export const buildString = (value) => {
532
549
 
533
550
  return treeFromStream(
534
551
  (function* () {
535
- yield t.nodeOpen(t.nodeFlags, l.JSON, 'String');
552
+ yield t.nodeOpen(t.nodeFlags, 'String');
536
553
  yield t.ref`openToken`;
537
- const tok = buildToken(l.JSON, 'Punctuator', "'");
554
+ const tok = buildToken('Punctuator', "'");
538
555
  yield gap(tok);
539
556
  yield t.ref`content`;
540
- yield t.nodeOpen(t.tokenFlags, l.JSON, 'StringContent');
557
+ yield t.nodeOpen(t.tokenFlags, 'StringContent');
541
558
 
542
559
  for (const piece of pieces) {
543
560
  if (isString(piece)) {
@@ -566,7 +583,7 @@ export const buildString = (value) => {
566
583
 
567
584
  value = treeFromStream(
568
585
  [
569
- t.nodeOpen(t.nodeFlags, l.JSON, 'EscapeCode'),
586
+ t.nodeOpen(t.nodeFlags, 'EscapeCode'),
570
587
  t.ref`sigilToken`,
571
588
  gap(buildKeyword(escapables[chr])),
572
589
  t.ref`digits[]`,
@@ -582,7 +599,7 @@ export const buildString = (value) => {
582
599
 
583
600
  value = treeFromStream(
584
601
  [
585
- t.nodeOpen(t.nodeFlags, l.JSON, 'EscapeCode'),
602
+ t.nodeOpen(t.nodeFlags, 'EscapeCode'),
586
603
  t.ref`sigilToken`,
587
604
  gap(buildKeyword('u')),
588
605
  t.ref`digits[]`,
@@ -597,9 +614,9 @@ export const buildString = (value) => {
597
614
  }
598
615
 
599
616
  yield t.ref`@`;
600
- yield t.nodeOpen(t.nodeFlags, l.JSON, 'EscapeSequence', { cooked: chr });
617
+ yield t.nodeOpen(t.nodeFlags, 'EscapeSequence', { cooked: chr });
601
618
  yield t.ref`escape`;
602
- yield gap(buildToken(l.JSON, 'Punctuator', '\\'));
619
+ yield gap(buildToken('Punctuator', '\\'));
603
620
  yield t.ref`value`;
604
621
  yield gap(value);
605
622
  yield t.nodeClose();
@@ -626,7 +643,7 @@ export const buildString = (value) => {
626
643
 
627
644
  yield t.nodeClose();
628
645
  yield t.ref`closeToken`;
629
- yield gap(buildToken(l.JSON, 'Punctuator', "'"));
646
+ yield gap(buildToken('Punctuator', "'"));
630
647
  yield t.nodeClose();
631
648
  })(),
632
649
  { expressions },
@@ -639,9 +656,9 @@ export const buildBoolean = (value) => {
639
656
 
640
657
  return treeFromStream(
641
658
  [
642
- t.nodeOpen(t.nodeFlags, l.Instruction, 'Boolean'),
659
+ t.nodeOpen(t.nodeFlags, 'Boolean'),
643
660
  t.ref`sigilToken`,
644
- gap(buildToken(l.Instruction, 'Keyword', value ? 'true' : 'false')),
661
+ gap(buildToken('Keyword', value ? 'true' : 'false')),
645
662
  t.nodeClose(),
646
663
  ],
647
664
  { expressions },
@@ -654,9 +671,24 @@ export const buildNull = () => {
654
671
 
655
672
  return treeFromStream(
656
673
  [
657
- t.nodeOpen(t.nodeFlags, l.Instruction, 'Null'),
674
+ t.nodeOpen(t.nodeFlags, 'Null'),
675
+ t.ref`sigilToken`,
676
+ gap(buildToken('Keyword', 'null')),
677
+ t.nodeClose(),
678
+ ],
679
+ { expressions },
680
+ );
681
+ };
682
+
683
+ export const buildUndefined = () => {
684
+ const expressions = [];
685
+ const gap = buildFilledGapFunction(expressions);
686
+
687
+ return treeFromStream(
688
+ [
689
+ t.nodeOpen(t.nodeFlags, 'Undefined'),
658
690
  t.ref`sigilToken`,
659
- gap(buildToken(l.Instruction, 'Keyword', 'null')),
691
+ gap(buildToken('Keyword', 'undefined')),
660
692
  t.nodeClose(),
661
693
  ],
662
694
  { expressions },
@@ -669,9 +701,9 @@ export const buildNullTag = () => {
669
701
 
670
702
  return treeFromStream(
671
703
  [
672
- t.nodeOpen(t.nodeFlags, l.CSTML, 'NullTag'),
704
+ t.nodeOpen(t.nodeFlags, 'NullTag'),
673
705
  t.ref`sigilToken`,
674
- gap(buildToken(l.CSTML, 'Keyword', 'null')),
706
+ gap(buildToken('Keyword', 'null')),
675
707
  t.nodeClose(),
676
708
  ],
677
709
  { expressions },
@@ -684,12 +716,12 @@ export const buildArray = (elements) => {
684
716
 
685
717
  return treeFromStream(
686
718
  (function* () {
687
- yield t.nodeOpen(t.nodeFlags, l.Instruction, 'Array');
719
+ yield t.nodeOpen(t.nodeFlags, 'Array');
688
720
  yield t.ref`openToken`;
689
- yield gap(buildToken(l.Instruction, 'Punctuator', '['));
721
+ yield gap(buildToken('Punctuator', '['));
690
722
  yield* interpolateFragment(elements, t.ref`elements[]`, expressions);
691
723
  yield t.ref`closeToken`;
692
- yield gap(buildToken(l.Instruction, 'Punctuator', ']'));
724
+ yield gap(buildToken('Punctuator', ']'));
693
725
  yield t.nodeClose();
694
726
  })(),
695
727
  { expressions },
@@ -702,14 +734,14 @@ export const buildArrayElements = (values) => {
702
734
  (function* () {
703
735
  yield t.doctype({ bablrLanguage: l.Instruction });
704
736
  yield t.nodeOpen(t.nodeFlags);
705
- yield* buildSpaceSeparatedList(values, t.ref`.[]`, expressions);
737
+ yield* buildCommaSeparatedList(values, t.ref`.[]`, expressions);
706
738
  yield t.nodeClose();
707
739
  })(),
708
740
  { expressions },
709
741
  );
710
742
  };
711
743
 
712
- export function* buildSpaceSeparatedList(values, ref, expressions) {
744
+ export function* buildCommaSeparatedList(values, ref, expressions) {
713
745
  const gap = buildFilledGapFunction(expressions);
714
746
 
715
747
  if (!ref.value.isArray) throw new Error();
@@ -720,8 +752,8 @@ export function* buildSpaceSeparatedList(values, ref, expressions) {
720
752
  let first = true;
721
753
  for (const value of values) {
722
754
  if (!first) {
723
- yield t.buildReferenceTag('#', false);
724
- yield gap(buildSpace());
755
+ yield t.buildReferenceTag('#');
756
+ yield gap(buildToken('Punctuator', ','));
725
757
  }
726
758
  yield freeze({ ...ref });
727
759
  yield gap(value || buildNullNode());
@@ -735,7 +767,7 @@ export const buildObjectProperties = (properties) => {
735
767
  return treeFromStream(
736
768
  concat(
737
769
  [t.doctype({ bablrLanguage: l.Instruction }), t.nodeOpen(t.nodeFlags)],
738
- buildSpaceSeparatedList(properties, t.ref`properties[]`, expressions),
770
+ buildCommaSeparatedList(properties, t.ref`properties[]`, expressions),
739
771
  [t.nodeClose()],
740
772
  ),
741
773
  { expressions },
@@ -748,14 +780,14 @@ export const buildObject = (properties) => {
748
780
 
749
781
  return treeFromStream(
750
782
  (function* () {
751
- yield t.nodeOpen(t.nodeFlags, l.Instruction, 'Object');
783
+ yield t.nodeOpen(t.nodeFlags, 'Object');
752
784
  yield t.ref`openToken`;
753
- yield gap(buildToken(l.Instruction, 'Punctuator', '{'));
785
+ yield gap(buildToken('Punctuator', '{'));
754
786
 
755
787
  yield* interpolateFragment(properties, t.ref`properties[]`, expressions);
756
788
 
757
789
  yield t.ref`closeToken`;
758
- yield gap(buildToken(l.Instruction, 'Punctuator', '}'));
790
+ yield gap(buildToken('Punctuator', '}'));
759
791
  yield t.nodeClose();
760
792
  })(),
761
793
  { expressions },
@@ -768,14 +800,36 @@ export const buildPattern = (alternatives, flags) => {
768
800
 
769
801
  return treeFromStream(
770
802
  (function* () {
771
- yield t.nodeOpen(t.nodeFlags, l.Regex, 'Pattern');
803
+ yield t.nodeOpen(t.nodeFlags, 'Pattern');
804
+ yield t.ref`openToken`;
805
+ yield gap(buildToken('Punctuator', '/'));
806
+
807
+ yield* interpolateFragment(alternatives, t.ref`alternatives[]`, expressions);
808
+
809
+ yield t.ref`closeToken`;
810
+ yield gap(buildToken('Punctuator', '/'));
811
+ yield t.ref`flags`;
812
+ yield gap(flags || buildReferenceFlags());
813
+ yield t.nodeClose();
814
+ })(),
815
+ { expressions },
816
+ );
817
+ };
818
+
819
+ export const buildRegexGroup = (alternatives, flags) => {
820
+ const expressions = [];
821
+ const gap = buildFilledGapFunction(expressions);
822
+
823
+ return treeFromStream(
824
+ (function* () {
825
+ yield t.nodeOpen(t.nodeFlags, 'Group');
772
826
  yield t.ref`openToken`;
773
- yield gap(buildToken(l.Regex, 'Punctuator', '/'));
827
+ yield gap(buildToken('Punctuator', '('));
774
828
 
775
829
  yield* interpolateFragment(alternatives, t.ref`alternatives[]`, expressions);
776
830
 
777
831
  yield t.ref`closeToken`;
778
- yield gap(buildToken(l.Regex, 'Punctuator', '/'));
832
+ yield gap(buildToken('Punctuator', ')'));
779
833
  yield t.ref`flags`;
780
834
  yield gap(flags || buildReferenceFlags());
781
835
  yield t.nodeClose();
@@ -799,12 +853,12 @@ export const buildRegexFlags = (flags = '') => {
799
853
 
800
854
  return treeFromStream(
801
855
  (function* () {
802
- yield t.nodeOpen(t.nodeFlags, l.Regex, 'Flags');
856
+ yield t.nodeOpen(t.nodeFlags, 'Flags');
803
857
 
804
858
  for (const { 0: name, 1: chr } of Object.entries(flagCharacters)) {
805
859
  yield t.buildReferenceTag(name + 'Token');
806
860
 
807
- yield gap(flags.includes(chr) ? buildToken(l.CSTML, 'Punctuator', chr) : buildNullNode());
861
+ yield gap(flags.includes(chr) ? buildToken('Punctuator', chr) : buildNullNode());
808
862
  }
809
863
  yield t.nodeClose();
810
864
  })(),
@@ -817,7 +871,7 @@ export const buildAlternative = (elements) => {
817
871
 
818
872
  return treeFromStream(
819
873
  concat(
820
- [t.nodeOpen(t.nodeFlags, l.Regex, 'Alternative')],
874
+ [t.nodeOpen(t.nodeFlags, 'Alternative')],
821
875
  interpolateFragment(elements, t.ref`elements[]+`, expressions),
822
876
  [t.nodeClose()],
823
877
  ),
@@ -831,7 +885,7 @@ export const buildAlternatives = (alternatives = []) => {
831
885
 
832
886
  return treeFromStream(
833
887
  (function* () {
834
- yield t.doctype({ bablrLanguage: l.Regex });
888
+ yield t.doctype({ bablrLanguage: l.Instruction });
835
889
  yield t.nodeOpen(t.nodeFlags);
836
890
  yield t.ref`.[]`;
837
891
  yield t.arr();
@@ -843,7 +897,7 @@ export const buildAlternatives = (alternatives = []) => {
843
897
  t.ref`.[]`,
844
898
  gap(alt),
845
899
  t.ref`separatorTokens[]`,
846
- gap(buildPunctuator(l.Regex, '|')),
900
+ gap(buildPunctuator('|')),
847
901
  ])
848
902
  .slice(0, -2);
849
903
 
@@ -859,11 +913,11 @@ export const buildRegexGap = () => {
859
913
 
860
914
  return treeFromStream(
861
915
  [
862
- t.nodeOpen(t.nodeFlags, l.Regex, 'Gap'),
916
+ t.nodeOpen(t.nodeFlags, 'Gap'),
863
917
  t.ref`escapeToken`,
864
- gap(buildToken(l.Regex, 'Punctuator', '\\')),
918
+ gap(buildToken('Punctuator', '\\')),
865
919
  t.ref`value`,
866
- gap(buildToken(l.Regex, 'Keyword', 'g')),
920
+ gap(buildToken('Keyword', 'g')),
867
921
  t.nodeClose(),
868
922
  ],
869
923
  { expressions },
@@ -876,7 +930,7 @@ export const buildElements = (elements) => {
876
930
 
877
931
  return treeFromStream(
878
932
  concat(
879
- [t.doctype({ bablrLanguage: l.Regex }), t.nodeOpen(t.nodeFlags), t.ref`.[]+`, t.arr()],
933
+ [t.doctype({ bablrLanguage: l.Instruction }), t.nodeOpen(t.nodeFlags), t.ref`.[]+`, t.arr()],
880
934
  elements.flatMap((el) => [t.ref`.[]+`, gap(el)]),
881
935
  [t.nodeClose()],
882
936
  ),
@@ -884,41 +938,45 @@ export const buildElements = (elements) => {
884
938
  );
885
939
  };
886
940
 
887
- export const buildExpression = (expr) => {
888
- throw new Error('unimplemented');
889
-
890
- if (isNull(expr)) return buildNullTag();
941
+ export const buildJSExpressionDeep = (expr) => {
942
+ if (expr === null) {
943
+ return buildNull();
944
+ } else if (expr === undefined) {
945
+ return buildUndefined();
946
+ }
891
947
 
892
948
  switch (typeof expr) {
893
- case 'symbol':
894
949
  case 'boolean':
895
950
  return buildBoolean(expr);
951
+
896
952
  case 'string':
897
953
  return buildString(expr);
954
+
898
955
  case 'number':
899
956
  return buildInteger(expr);
957
+
900
958
  case 'object': {
901
- switch (getPrototypeOf(expr)) {
959
+ switch (Object.getPrototypeOf(expr)) {
902
960
  case Array.prototype:
903
- return buildArray(buildArrayElements(expr));
961
+ return buildArray(buildArrayElements(expr.map((e) => buildJSExpressionDeep(e))));
962
+
904
963
  case Object.prototype:
905
- if (
906
- hasOwn(expr, 'type') &&
907
- hasOwn(expr, 'language') &&
908
- hasOwn(expr, 'children') &&
909
- hasOwn(expr, 'properties')
910
- ) {
911
- return expr;
912
- }
913
964
  return buildObject(
914
965
  buildObjectProperties(
915
- Object.entries(expr).map((e) => buildProperty(buildIdentifier(e[0]), e[1])),
966
+ Object.entries(expr).map((e) =>
967
+ buildProperty(
968
+ /^[a-zA-Z_]+$/.test(e[0]) ? buildIdentifier(e[0]) : buildString(e[0]),
969
+ buildJSExpressionDeep(e[1]),
970
+ ),
971
+ ),
916
972
  ),
917
973
  );
974
+
918
975
  default:
919
976
  throw new Error();
920
977
  }
921
978
  }
979
+
922
980
  default:
923
981
  throw new Error();
924
982
  }
@@ -930,15 +988,15 @@ export const buildTaggedString = (tag, content) => {
930
988
 
931
989
  return treeFromStream(
932
990
  [
933
- t.buildOpenNodeTag(t.nodeFlags, l.Spamex, 'SpamexString'),
991
+ t.buildOpenNodeTag(t.nodeFlags, 'SpamexString'),
934
992
  t.buildReferenceTag('sigilToken'),
935
- gap(buildToken(l.Instruction, 'Keyword', tag)),
993
+ gap(buildToken('Keyword', tag)),
936
994
  t.buildReferenceTag('openToken'),
937
- gap(buildToken(l.Instruction, 'Punctuator', "'")),
995
+ gap(buildToken('Punctuator', "'")),
938
996
  t.buildReferenceTag('content'),
939
997
  gap(content),
940
998
  t.buildReferenceTag('closeToken'),
941
- gap(buildToken(l.Instruction, 'Punctuator', "'")),
999
+ gap(buildToken('Punctuator', "'")),
942
1000
  t.buildCloseNodeTag(),
943
1001
  ],
944
1002
  { expressions },
@@ -953,15 +1011,17 @@ export const buildRegexString = (content) => {
953
1011
  return buildTaggedString('re', content);
954
1012
  };
955
1013
 
956
- export const buildPropertyMatcher = (refMatcher, nodeMatcher) => {
1014
+ export const buildPropertyMatcher = (refMatcher, bindingMatcher, nodeMatcher) => {
957
1015
  const expressions = [];
958
1016
  const gap = buildFilledGapFunction(expressions);
959
1017
 
960
1018
  return treeFromStream(
961
1019
  [
962
- t.nodeOpen(t.nodeFlags, l.Spamex, 'PropertyMatcher'),
1020
+ t.nodeOpen(t.nodeFlags, 'PropertyMatcher'),
963
1021
  t.ref`refMatcher`,
964
1022
  gap(refMatcher || buildNullNode()),
1023
+ t.ref`bindingMatcher`,
1024
+ gap(bindingMatcher || buildNullNode()),
965
1025
  t.ref`nodeMatcher`,
966
1026
  gap(nodeMatcher),
967
1027
  t.nodeClose(),
@@ -971,5 +1031,5 @@ export const buildPropertyMatcher = (refMatcher, nodeMatcher) => {
971
1031
  };
972
1032
 
973
1033
  export const buildGapNodeMatcher = () => {
974
- return buildToken(l.Spamex, 'GapNodeMatcher', '<//>');
1034
+ return buildToken('GapNodeMatcher', '<//>');
975
1035
  };