@bablr/agast-vm-helpers 0.5.1 → 0.6.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/deembed.js CHANGED
@@ -1,25 +1,37 @@
1
- import { EmbeddedExpression } from '@bablr/agast-helpers/symbols';
1
+ import {
2
+ EmbeddedNode,
3
+ EmbeddedObject,
4
+ EmbeddedTag,
5
+ EmbeddedMatcher,
6
+ EmbeddedRegex,
7
+ } from '@bablr/agast-helpers/symbols';
2
8
 
3
- const { isArray } = Array;
4
- const isString = (val) => typeof val === 'string';
5
- const isNumber = (val) => typeof val === 'number';
9
+ export const getEmbeddedObject = (expr) => {
10
+ if (!expr) return expr;
11
+ if (expr.type !== EmbeddedObject) throw new Error();
12
+ return expr.value;
13
+ };
6
14
 
7
- export const deembedExpression = (expr) => {
8
- if (isString(expr) || expr == null || typeof expr === 'boolean' || isNumber(expr)) {
9
- return expr;
10
- } else if (isArray(expr)) {
11
- return expr.map((value) => deembedExpression(value));
12
- } else if (typeof expr === 'object') {
13
- return Object.fromEntries(
14
- Object.entries(expr.value).map(({ 0: key, 1: value }) => [key, deembedExpression(value)]),
15
- );
16
- } else {
17
- throw new Error();
18
- }
15
+ export const getEmbeddedNode = (expr) => {
16
+ if (!expr) return expr;
17
+ if (expr.type !== EmbeddedNode) throw new Error();
18
+ return expr.value;
19
19
  };
20
20
 
21
- export const getEmbeddedExpression = (expr) => {
21
+ export const getEmbeddedMatcher = (expr) => {
22
22
  if (!expr) return expr;
23
- if (expr.type !== EmbeddedExpression) throw new Error();
23
+ if (expr.type !== EmbeddedMatcher) throw new Error();
24
24
  return expr.value;
25
25
  };
26
+
27
+ export const getEmbeddedRegex = (expr) => {
28
+ if (!expr) return expr;
29
+ if (expr.type !== EmbeddedRegex) throw new Error();
30
+ return expr.value;
31
+ };
32
+
33
+ export const getEmbeddedTag = (expr) => {
34
+ if (expr.type !== EmbeddedTag) throw new Error();
35
+ const tag = expr.value;
36
+ return tag;
37
+ };
package/lib/embed.js CHANGED
@@ -1,4 +1,4 @@
1
- import { buildEmbeddedExpression } from './internal-builders.js';
1
+ import { buildEmbeddedObject } from './internal-builders.js';
2
2
 
3
3
  const { isArray } = Array;
4
4
  const isString = (val) => typeof val === 'string';
@@ -10,7 +10,7 @@ export const embedExpression = (expr) => {
10
10
  } else if (isArray(expr)) {
11
11
  return expr.map((value) => embedExpression(value));
12
12
  } else if (typeof expr === 'object') {
13
- return buildEmbeddedExpression(
13
+ return buildEmbeddedObject(
14
14
  Object.fromEntries(
15
15
  Object.entries(expr).map(({ 0: key, 1: value }) => [key, embedExpression(value)]),
16
16
  ),
@@ -19,3 +19,5 @@ export const embedExpression = (expr) => {
19
19
  throw new Error();
20
20
  }
21
21
  };
22
+
23
+ export { buildEmbeddedObject, buildEmbeddedObject as o };
package/lib/index.js CHANGED
@@ -1,22 +1,37 @@
1
- import { sourceTextFor, getCooked, isNull, nodeFlags } from '@bablr/agast-helpers/tree';
1
+ import {
2
+ sourceTextFor,
3
+ getCooked,
4
+ isNull,
5
+ nodeFlags,
6
+ printType,
7
+ buildGapTag,
8
+ buildEmbeddedMatcher,
9
+ isNullNode,
10
+ buildNullTag,
11
+ buildStubNode,
12
+ isFragmentNode,
13
+ buildReferenceTag,
14
+ } from '@bablr/agast-helpers/tree';
2
15
  import * as btree from '@bablr/agast-helpers/btree';
3
- import * as sym from '@bablr/agast-helpers/symbols';
4
- import { buildNodeCloseTag, buildLiteralTag, buildDoctypeTag } from '@bablr/agast-helpers/builders';
16
+ import {
17
+ buildCloseNodeTag,
18
+ buildLiteralTag,
19
+ buildDoctypeTag,
20
+ referenceFlags,
21
+ buildEmbeddedRegex,
22
+ } from '@bablr/agast-helpers/builders';
5
23
  import {
6
24
  DoctypeTag,
7
25
  OpenNodeTag,
8
- OpenFragmentTag,
9
- CloseFragmentTag,
10
26
  CloseNodeTag,
11
- ReferenceTag,
12
27
  ShiftTag,
13
28
  GapTag,
14
29
  NullTag,
15
- ArrayTag,
30
+ ArrayInitializerTag,
16
31
  LiteralTag,
17
32
  } from '@bablr/agast-helpers/symbols';
18
33
 
19
- export * from './builders.js';
34
+ const { freeze } = Object;
20
35
 
21
36
  export const effectsFor = (verb) => {
22
37
  switch (verb) {
@@ -43,15 +58,21 @@ export const shouldBranch = (effects) => {
43
58
  return effects ? effects.success === 'none' || effects.failure === 'none' : false;
44
59
  };
45
60
 
46
- const reifyFlags = (flags) => {
47
- let { triviaToken, escapeToken, tokenToken, expressionToken, hasGapToken } = flags.properties;
61
+ export const reifyNodeFlags = (flags) => {
62
+ let { tokenToken, hasGapToken } = flags.properties;
48
63
 
49
64
  return {
50
- token: !!reifyExpression(tokenToken),
51
- escape: !!reifyExpression(escapeToken),
52
- trivia: !!reifyExpression(triviaToken),
53
- expression: !!reifyExpression(expressionToken),
54
- hasGap: !!reifyExpression(hasGapToken),
65
+ token: !!(tokenToken && reifyExpression(tokenToken.node)),
66
+ hasGap: !!(hasGapToken && reifyExpression(hasGapToken.node)),
67
+ };
68
+ };
69
+
70
+ export const reifyReferenceFlags = (flags) => {
71
+ let { expressionToken, hasGapToken } = flags.properties;
72
+
73
+ return {
74
+ expression: !!(expressionToken && reifyExpression(expressionToken.node)),
75
+ hasGap: !!(hasGapToken && reifyExpression(hasGapToken.node)),
55
76
  };
56
77
  };
57
78
 
@@ -64,18 +85,18 @@ export const reifyLanguage = (language) => {
64
85
  export const reifyProperties = (properties = []) => {
65
86
  const built = {};
66
87
  for (const property of btree.traverse(properties)) {
67
- switch (property.type) {
68
- case 'Property': {
69
- let { reference, value } = property.properties;
88
+ switch (property.node.type) {
89
+ case Symbol.for('Property'): {
90
+ let { reference, value: node } = property.node.properties;
70
91
 
71
- reference = reifyExpression(reference);
72
- value = reifyExpression(value);
92
+ reference = reifyExpression(reference.node);
93
+ node = reifyExpression(node.node);
73
94
 
74
95
  if (reference.value.isArray) {
75
96
  built[reference.value.name] ||= [];
76
- built[reference.value.name].push(value);
97
+ built[reference.value.name].push({ reference, node });
77
98
  } else {
78
- built[reference.value.name] = value;
99
+ built[reference.value.name] = { reference, node };
79
100
  }
80
101
  break;
81
102
  }
@@ -91,19 +112,20 @@ export const buildFragmentChildren = (node) => {
91
112
 
92
113
  let built = [];
93
114
 
94
- open = reifyExpression(open);
95
- close = reifyExpression(close);
115
+ open = reifyExpression(open.node);
116
+ close = reifyExpression(close.node);
96
117
 
97
118
  built = btree.push(built, open);
98
119
 
99
120
  for (const child of btree.traverse(children)) {
100
- if (child.type !== 'Property') throw new Error('umimplemented');
121
+ if (child.node.type !== Symbol.for('Property')) throw new Error('umimplemented');
101
122
 
102
- let { reference } = child.properties;
123
+ let { reference } = child.node.properties;
103
124
 
104
- reference = reifyExpression(reference);
125
+ reference = reifyExpression(reference.node);
105
126
 
106
127
  built = btree.push(built, reference);
128
+ built = btree.push(built, buildGapTag());
107
129
  }
108
130
 
109
131
  built = btree.push(built, close);
@@ -114,29 +136,30 @@ export const buildFragmentChildren = (node) => {
114
136
  export const buildChildren = (node) => {
115
137
  let { open, children = [], close } = node.properties;
116
138
 
117
- const selfClosing = !!open.properties.selfClosingTagToken;
118
- const { intrinsicValue } = open.properties;
139
+ const selfClosing = !isNull(open.node.properties.selfClosingTagToken?.node);
140
+ const { intrinsicValue } = open.node.properties;
119
141
  let built = [];
120
142
 
121
- open = reifyExpression(open);
122
- close = reifyExpression(close);
143
+ open = reifyExpression(open.node);
144
+ close = reifyExpression(close?.node);
123
145
 
124
146
  if (selfClosing) {
125
147
  built = btree.push(built, open);
126
- if (intrinsicValue) {
127
- built = btree.push(built, buildLiteralTag(intrinsicValue));
148
+ if (!isNull(intrinsicValue?.node)) {
149
+ built = btree.push(built, buildLiteralTag(intrinsicValue.node));
128
150
  }
129
- built = btree.push(built, buildNodeCloseTag());
151
+ built = btree.push(built, buildCloseNodeTag());
130
152
  } else {
131
153
  built = btree.push(built, open);
132
154
  for (const child of btree.traverse(children)) {
133
- if (child.type !== 'Property') throw new Error('umimplemented');
155
+ if (child.node.type !== Symbol.for('Property')) throw new Error('umimplemented');
134
156
 
135
- let { reference } = child.properties;
157
+ let { reference } = child.node.properties;
136
158
 
137
- reference = reifyExpression(reference);
159
+ reference = reifyExpression(reference.node);
138
160
 
139
161
  built = btree.push(built, reference);
162
+ built = btree.push(built, buildGapTag());
140
163
  }
141
164
 
142
165
  built = btree.push(built, close);
@@ -148,10 +171,11 @@ export const buildChildren = (node) => {
148
171
  export const reifyExpression = (node) => {
149
172
  if (node instanceof Promise) throw new Error();
150
173
 
151
- if (!node || node.type === sym.null) return null;
174
+ if (node == null) return node;
175
+ if (isNullNode(node)) return null;
152
176
 
153
- if (!node.type) {
154
- node = node.properties['.'];
177
+ if (isFragmentNode(node)) {
178
+ node = node.properties['.'].node;
155
179
  }
156
180
 
157
181
  if (node.language === 'https://bablr.org/languages/core/en/cstml') {
@@ -159,19 +183,19 @@ export const reifyExpression = (node) => {
159
183
  case 'Document': {
160
184
  let { doctype, tree } = node.properties;
161
185
 
162
- doctype = reifyExpression(doctype);
163
- tree = reifyExpression(tree);
186
+ doctype = reifyExpression(doctype.node);
187
+ tree = reifyExpression(tree.node);
164
188
 
165
189
  let { attributes } = doctype.value;
166
190
  let { properties } = tree;
167
191
 
168
192
  return {
169
193
  flags: nodeFlags,
170
- language: attributes['bablr-language'],
194
+ language: attributes.bablrLanguage,
171
195
  type: null,
172
196
  children: btree.addAt(
173
197
  0,
174
- buildFragmentChildren(node.properties.tree),
198
+ buildFragmentChildren(node.properties.tree.node),
175
199
  buildDoctypeTag(attributes),
176
200
  ),
177
201
  properties,
@@ -182,7 +206,7 @@ export const reifyExpression = (node) => {
182
206
  case 'Node': {
183
207
  let { open, children } = node.properties;
184
208
 
185
- open = reifyExpression(open);
209
+ open = reifyExpression(open.node);
186
210
 
187
211
  let { flags, language, type, attributes } = open.value;
188
212
 
@@ -201,7 +225,7 @@ export const reifyExpression = (node) => {
201
225
  case 'Fragment': {
202
226
  let { open, children } = node.properties;
203
227
 
204
- open = reifyExpression(open);
228
+ open = reifyExpression(open.node);
205
229
 
206
230
  let { flags } = open.value;
207
231
 
@@ -218,26 +242,24 @@ export const reifyExpression = (node) => {
218
242
  }
219
243
 
220
244
  case 'DoctypeTag': {
221
- let { doctype, version, attributes } = node.properties;
245
+ let { doctypeToken, version, attributes } = node.properties;
222
246
  return {
223
247
  type: DoctypeTag,
224
248
  value: {
225
- doctype: getCooked(doctype),
226
- version: parseInt(sourceTextFor(version), 10),
227
- attributes: reifyAttributes(attributes),
249
+ doctypeToken: getCooked(doctypeToken?.node),
250
+ version: parseInt(sourceTextFor(version.node), 10),
251
+ attributes: reifyExpression(attributes.node),
228
252
  },
229
253
  };
230
254
  }
231
255
 
232
256
  case 'ReferenceTag': {
233
- let { name, arrayOperatorToken, hasGapToken } = node.properties;
257
+ let { name, arrayOperatorToken, flags } = node.properties;
234
258
 
235
- name = reifyExpression(name);
259
+ name = reifyExpression(name.node);
260
+ flags = freeze({ expression: !!flags?.expressionToken, hasGap: !!flags?.hasGapToken });
236
261
 
237
- return {
238
- type: ReferenceTag,
239
- value: { name, isArray: !isNull(arrayOperatorToken), hasGap: !isNull(hasGapToken) },
240
- };
262
+ return buildReferenceTag(name, !isNull(arrayOperatorToken?.node), flags);
241
263
  }
242
264
 
243
265
  case 'LiteralTag': {
@@ -245,20 +267,22 @@ export const reifyExpression = (node) => {
245
267
 
246
268
  return { type: LiteralTag, value: getCooked(value.properties.content) };
247
269
  }
270
+
248
271
  case 'Identifier': {
249
272
  return getCooked(node);
250
273
  }
274
+
251
275
  case 'IdentifierPath': {
252
- return node.properties.segments.map((segment) => reifyExpression(segment));
276
+ return node.properties.segments.map((segment) => reifyExpression(segment.node));
253
277
  }
254
278
 
255
279
  case 'OpenNodeTag': {
256
280
  let { flags, language, type, attributes } = node.properties;
257
281
 
258
- flags = reifyFlags(flags);
259
- language = reifyLanguage(language);
260
- type = reifyExpression(type);
261
- attributes = reifyAttributes(attributes);
282
+ flags = reifyNodeFlags(flags.node);
283
+ language = reifyLanguage(language?.node);
284
+ type = reifyExpression(type?.node);
285
+ attributes = reifyExpression(attributes?.node);
262
286
 
263
287
  return {
264
288
  type: OpenNodeTag,
@@ -267,50 +291,27 @@ export const reifyExpression = (node) => {
267
291
  }
268
292
 
269
293
  case 'CloseNodeTag': {
270
- let { language, type } = node.properties;
271
-
272
- language = reifyLanguage(language);
273
- type = reifyExpression(type);
274
-
275
- return { type: CloseNodeTag, value: { language, type } };
276
- }
277
-
278
- case 'OpenFragmentTag': {
279
- let { flags } = node.properties;
280
-
281
- flags = reifyFlags(flags);
282
-
283
- return {
284
- type: OpenFragmentTag,
285
- value: { flags },
286
- };
287
- }
288
-
289
- case 'CloseFragmentTag': {
290
- return { type: CloseFragmentTag, value: undefined };
294
+ return { type: CloseNodeTag, value: undefined };
291
295
  }
292
296
 
293
297
  case 'Integer': {
294
298
  let { digits } = node.properties;
295
- return parseInt(digits.map((digit) => getCooked(digit)).join(''), 10);
299
+ return parseInt(digits.map((digit) => getCooked(digit.node)).join(''), 10);
296
300
  }
297
301
 
298
302
  case 'Infinity': {
299
- return node.properties.sign === '-' ? -Infinity : Infinity;
303
+ return getCooked(node.properties.sign.node) === '-' ? -Infinity : Infinity;
300
304
  }
301
305
 
302
306
  case 'Punctuator': {
303
307
  return getCooked(node);
304
308
  }
305
309
 
306
- case 'String':
307
- return node.properties.content ? getCooked(node.properties.content) : '';
308
-
309
310
  case 'GapTag':
310
311
  return { type: GapTag, value: undefined };
311
312
 
312
- case 'ArrayTag':
313
- return { type: ArrayTag, value: undefined };
313
+ case 'ArrayInitializerTag':
314
+ return { type: ArrayInitializerTag, value: undefined };
314
315
 
315
316
  case 'NullTag':
316
317
  return { type: NullTag, value: undefined };
@@ -326,6 +327,7 @@ export const reifyExpression = (node) => {
326
327
  if (
327
328
  ![
328
329
  'https://bablr.org/languages/core/en/bablr-vm-instruction',
330
+ 'https://bablr.org/languages/core/en/cstml-json',
329
331
  'https://bablr.org/languages/core/en/cstml',
330
332
  'https://bablr.org/languages/core/en/spamex',
331
333
  ].includes(node.language)
@@ -333,44 +335,112 @@ export const reifyExpression = (node) => {
333
335
  return node;
334
336
  }
335
337
 
336
- switch (node.type) {
337
- case 'NodeMatcher':
338
- let { flags, language, type, attributes, intrinsicValue } = node.properties.open.properties;
338
+ switch (printType(node.type)) {
339
+ case 'String':
340
+ return node.properties.content.node ? getCooked(node.properties.content.node) : '';
341
+
342
+ case 'SpamexString': {
343
+ return buildEmbeddedMatcher(node.properties.content.node);
344
+ }
345
+
346
+ case 'RegexString': {
347
+ return buildEmbeddedRegex(node.properties.content.node);
348
+ }
349
+
350
+ case 'OpenNodeMatcher': {
351
+ let { flags, language, type, attributes, intrinsicValue } = node.properties;
339
352
 
340
- flags = reifyFlags(flags);
341
- language = reifyLanguage(language);
342
- type = reifyExpression(type);
343
- attributes = reifyAttributes(attributes);
344
- intrinsicValue = reifyExpression(intrinsicValue);
353
+ flags = (flags && reifyNodeFlags(flags.node)) || {};
354
+ language = language && reifyLanguage(language.node);
355
+ type =
356
+ type.node.type === Symbol.for('String')
357
+ ? getCooked(type.node.properties.content.node)
358
+ : getCooked(type.node);
359
+ attributes = attributes ? reifyExpression(attributes.node) : {};
360
+ intrinsicValue = intrinsicValue && reifyExpression(intrinsicValue.node);
345
361
 
346
362
  return { flags, language, type, intrinsicValue, attributes };
363
+ }
364
+
365
+ case 'FragmentMatcher': {
366
+ let { flags } = node.properties;
367
+
368
+ flags = (flags && reifyNodeFlags(flags.node)) || {};
369
+
370
+ return {
371
+ flags,
372
+ language: null,
373
+ type: Symbol.for('@bablr/fragment'),
374
+ intrinsicValue: null,
375
+ attributes: null,
376
+ };
377
+ }
378
+
379
+ case 'BasicNodeMatcher': {
380
+ let { open } = node.properties;
381
+
382
+ return reifyExpression(open.node);
383
+ }
384
+
385
+ case 'PropertyMatcher': {
386
+ let { refMatcher, nodeMatcher } = node.properties;
387
+
388
+ refMatcher = refMatcher ? reifyExpression(refMatcher.node) : null;
389
+ nodeMatcher = reifyExpression(nodeMatcher.node);
390
+
391
+ return { refMatcher, nodeMatcher };
392
+ }
393
+
394
+ case 'ReferenceMatcher': {
395
+ let { name, openIndexToken, flags } = node.properties;
396
+
397
+ name =
398
+ name &&
399
+ (name.node.type === Symbol.for('Identifier')
400
+ ? reifyExpression(name.node)
401
+ : getCooked(name.node));
402
+ let isArray = !isNull(openIndexToken?.node);
403
+ flags = (flags && reifyReferenceFlags(flags?.node)) || referenceFlags;
404
+
405
+ return { name, isArray, flags };
406
+ }
407
+
408
+ case 'GapNodeMatcher':
409
+ return buildStubNode(buildGapTag());
410
+
411
+ case 'NullNodeMatcher':
412
+ return buildStubNode(buildNullTag());
413
+
414
+ case 'ArrayNodeMatcher':
415
+ return [];
347
416
 
348
417
  case 'Call': {
349
418
  const { verb, arguments: args } = node.properties;
350
- return { verb: reifyExpression(verb), arguments: reifyExpression(args) };
419
+
420
+ const args_ = [...btree.traverse(args)].map((el) => reifyExpression(el.node));
421
+
422
+ return { verb: reifyExpression(verb.node), arguments: args_ };
351
423
  }
352
424
 
353
425
  case 'Object': {
354
426
  const { properties } = node.properties;
355
427
 
356
428
  return Object.fromEntries(
357
- [...btree.traverse(properties)].map(({ properties: { key, value } }) => [
358
- getCooked(key),
359
- reifyExpression(value),
360
- ]),
429
+ [...btree.traverse(properties)].map((property) => {
430
+ const {
431
+ node: {
432
+ properties: { key, value },
433
+ },
434
+ } = property;
435
+ return [getCooked(key.node), reifyExpression(value.node)];
436
+ }),
361
437
  );
362
438
  }
363
439
 
364
- case 'Tuple': {
365
- const { values = [] } = node.properties;
366
-
367
- return [...btree.traverse(values)].map((el) => reifyExpression(el));
368
- }
369
-
370
440
  case 'Array': {
371
441
  const { elements = [] } = node.properties;
372
442
 
373
- return [...btree.traverse(elements)].map((el) => reifyExpression(el));
443
+ return [...btree.traverse(elements)].map((el) => reifyExpression(el.node));
374
444
  }
375
445
 
376
446
  case 'LiteralTag':
@@ -379,7 +449,7 @@ export const reifyExpression = (node) => {
379
449
 
380
450
  case 'Boolean': {
381
451
  // prettier-ignore
382
- switch (getCooked(node.properties.sigilToken)) {
452
+ switch (getCooked(node.properties.sigilToken.node)) {
383
453
  case 'true': return true;
384
454
  case 'false': return false;
385
455
  default: throw new Error();
@@ -395,52 +465,56 @@ export const reifyExpression = (node) => {
395
465
  };
396
466
 
397
467
  export const reifyExpressionShallow = (node) => {
398
- if (!node || node.type === sym.null) return null;
468
+ if (!node || isNullNode(node)) return null;
399
469
 
400
470
  if (
401
471
  ![
402
472
  'https://bablr.org/languages/core/en/bablr-vm-instruction',
403
473
  'https://bablr.org/languages/core/en/cstml',
474
+ 'https://bablr.org/languages/core/en/cstml-json',
475
+ 'https://bablr.org/languages/core/en/spamex',
404
476
  ].includes(node.language)
405
477
  ) {
406
478
  return node;
407
479
  }
408
480
 
409
- switch (node.type) {
481
+ switch (printType(node.type)) {
482
+ case 'String':
483
+ case 'SpamexString':
484
+ case 'RegexString': {
485
+ return reifyExpression(node);
486
+ }
487
+
410
488
  case 'Object': {
411
489
  const { properties } = node.properties;
412
490
 
413
491
  return Object.fromEntries(
414
- [...btree.traverse(properties)].map(({ properties: { key, value } }) => [
415
- getCooked(key),
416
- value,
417
- ]),
492
+ [...btree.traverse(properties)].map(
493
+ ({
494
+ node: {
495
+ properties: { key, value },
496
+ },
497
+ }) => [getCooked(key.node), value.node],
498
+ ),
418
499
  );
419
500
  }
420
501
 
421
502
  case 'Array':
422
- return [...btree.traverse(node.properties.elements)];
503
+ return [...btree.traverse(node.properties.elements)].map((prop) => prop.node);
423
504
 
424
- case 'Tuple':
425
- return [...btree.traverse(node.properties.values)];
505
+ case 'Boolean': {
506
+ // prettier-ignore
507
+ switch (getCooked(node.properties.sigilToken.node)) {
508
+ case 'true': return true;
509
+ case 'false': return false;
510
+ default: throw new Error();
511
+ }
512
+ }
513
+
514
+ case 'Null':
515
+ return null;
426
516
 
427
517
  default:
428
518
  return reifyExpression(node);
429
519
  }
430
520
  };
431
-
432
- export const reifyAttributes = (attributes) => {
433
- if (attributes == null) return {};
434
-
435
- return Object.fromEntries(
436
- [...btree.traverse(attributes)].map((attr) => {
437
- if (attr.type === 'MappingAttribute') {
438
- return [reifyExpression(attr.properties.key), reifyExpression(attr.properties.value)];
439
- } else if (attr.type === 'BooleanAttribute') {
440
- return [reifyExpression(attr.properties.key), isNull(attr.properties.negateToken)];
441
- } else {
442
- throw new Error();
443
- }
444
- }),
445
- );
446
- };
@@ -0,0 +1,29 @@
1
+ export function* concat(...iterables) {
2
+ for (const iterable of iterables) yield* iterable;
3
+ }
4
+
5
+ export const reduce = (reducer, values, initial) => {
6
+ let acc = initial;
7
+
8
+ for (const value of values) {
9
+ acc = reducer(acc, value);
10
+ }
11
+
12
+ return acc;
13
+ };
14
+
15
+ export function* takeWhile(fn, iterable) {
16
+ for (const value of iterable) {
17
+ if (fn(value)) {
18
+ yield value;
19
+ } else {
20
+ break;
21
+ }
22
+ }
23
+ }
24
+
25
+ export function* map(fn, iterable) {
26
+ for (const value of iterable) {
27
+ yield fn(value);
28
+ }
29
+ }
package/lib/languages.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export const Spamex = 'https://bablr.org/languages/core/en/spamex';
2
2
  export const CSTML = 'https://bablr.org/languages/core/en/cstml';
3
+ export const JSON = 'https://bablr.org/languages/core/en/cstml-json';
3
4
  export const Regex = 'https://bablr.org/languages/core/en/bablr-regex-pattern';
4
5
  export const Instruction = 'https://bablr.org/languages/core/en/bablr-vm-instruction';
5
- export const Comment = 'https://bablr.org/languages/core/en/c-comments';
6
6
  export const Space = 'https://bablr.org/languages/core/en/space-tab-newline';