@bablr/agast-vm-helpers 0.8.0 → 0.10.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,16 +1,21 @@
1
- import {
2
- EmbeddedNode,
3
- EmbeddedMatcher,
4
- EmbeddedRegex,
5
- EmbeddedTag,
6
- EmbeddedObject,
7
- EmbeddedInstruction,
8
- } from './symbols.js';
1
+ import { isNode } from '@bablr/agast-helpers/path';
2
+ import { Matcher, Regex, Tag, EmbeddedObject, Instruction, Node } from './symbols.js';
9
3
 
10
4
  const isObject = (val) => val !== null && typeof val === 'object';
11
5
 
12
6
  const { freeze } = Object;
13
7
 
8
+ export const buildOptions = (options) => {
9
+ let { shift, bind, allowEmpty, internal } = options;
10
+
11
+ let s = !shift ? 'S' : ' ';
12
+ let b = bind ? 'b' : ' ';
13
+ let e = allowEmpty ? 'e' : ' ';
14
+ let i = internal ? 'i' : ' ';
15
+
16
+ return `${s}${b}${e}${i}`;
17
+ };
18
+
14
19
  export const buildCall = (verb, ...args) => {
15
20
  return freeze({ verb, arguments: freeze(args) });
16
21
  };
@@ -20,29 +25,29 @@ export const buildEmbeddedObject = (obj) => {
20
25
  return freeze({ type: EmbeddedObject, value: freeze(obj) });
21
26
  };
22
27
 
23
- export const buildEmbeddedNode = (node) => {
24
- if (!isObject(node)) throw new Error();
25
- return freeze({ type: EmbeddedNode, value: freeze(node) });
26
- };
27
-
28
28
  export const buildEmbeddedMatcher = (matcher) => {
29
29
  if (!isObject(matcher)) throw new Error();
30
- return freeze({ type: EmbeddedMatcher, value: matcher });
30
+ return freeze({ type: Matcher, value: matcher });
31
31
  };
32
32
 
33
33
  export const buildEmbeddedInstruction = (instr) => {
34
34
  if (!isObject(instr)) throw new Error();
35
- return freeze({ type: EmbeddedInstruction, value: instr });
35
+ return freeze({ type: Instruction, value: instr });
36
36
  };
37
37
 
38
38
  export const buildEmbeddedRegex = (re) => {
39
39
  if (!isObject(re)) throw new Error();
40
- return freeze({ type: EmbeddedRegex, value: re });
40
+ return freeze({ type: Regex, value: re });
41
41
  };
42
42
 
43
43
  export const buildEmbeddedTag = (tag) => {
44
44
  if (!isObject(tag)) throw new Error();
45
- return freeze({ type: EmbeddedTag, value: tag });
45
+ return freeze({ type: Tag, value: tag });
46
+ };
47
+
48
+ export const buildEmbeddedNode = (node) => {
49
+ if (!isNode(node)) throw new Error();
50
+ return freeze({ type: Node, value: node });
46
51
  };
47
52
 
48
53
  export const buildEffect = (value) => {
package/lib/deembed.js CHANGED
@@ -1,11 +1,4 @@
1
- import {
2
- EmbeddedNode,
3
- EmbeddedObject,
4
- EmbeddedTag,
5
- EmbeddedMatcher,
6
- EmbeddedRegex,
7
- EmbeddedInstruction,
8
- } from './symbols.js';
1
+ import { EmbeddedObject, Tag, Matcher, Regex, Instruction } from './symbols.js';
9
2
 
10
3
  export const getEmbeddedObject = (expr) => {
11
4
  if (!expr) return expr;
@@ -13,32 +6,26 @@ export const getEmbeddedObject = (expr) => {
13
6
  return expr.value;
14
7
  };
15
8
 
16
- export const getEmbeddedNode = (expr) => {
17
- if (!expr) return expr;
18
- if (expr.type !== EmbeddedNode) throw new Error();
19
- return expr.value;
20
- };
21
-
22
9
  export const getEmbeddedMatcher = (expr) => {
23
10
  if (!expr) return expr;
24
- if (expr.type !== EmbeddedMatcher) throw new Error();
11
+ if (expr.type !== Matcher) throw new Error();
25
12
  return expr.value;
26
13
  };
27
14
 
28
15
  export const getEmbeddedInstruction = (expr) => {
29
16
  if (!expr) return expr;
30
- if (expr.type !== EmbeddedInstruction) throw new Error();
17
+ if (expr.type !== Instruction) throw new Error();
31
18
  return expr.value;
32
19
  };
33
20
 
34
21
  export const getEmbeddedRegex = (expr) => {
35
22
  if (!expr) return expr;
36
- if (expr.type !== EmbeddedRegex) throw new Error();
23
+ if (expr.type !== Regex) throw new Error();
37
24
  return expr.value;
38
25
  };
39
26
 
40
27
  export const getEmbeddedTag = (expr) => {
41
- if (expr.type !== EmbeddedTag) throw new Error();
28
+ if (expr.type !== Tag) throw new Error();
42
29
  const tag = expr.value;
43
30
  return tag;
44
31
  };
package/lib/index.js CHANGED
@@ -2,39 +2,41 @@ import {
2
2
  sourceTextFor,
3
3
  getCooked,
4
4
  isNull,
5
- nodeFlags,
6
5
  isNullNode,
7
- buildStubNode,
8
- isFragmentNode,
6
+ isCover,
9
7
  buildReferenceTag,
10
8
  getRoot,
11
- buildChild,
9
+ get,
10
+ list,
12
11
  } from '@bablr/agast-helpers/tree';
13
- import * as btree from '@bablr/agast-helpers/btree';
14
- import * as sumtree from '@bablr/agast-helpers/children';
12
+ import * as Tags from '@bablr/agast-helpers/tags';
15
13
  import {
16
14
  buildGapTag,
17
15
  buildNullTag,
18
16
  buildOpenNodeTag,
19
- buildBindingTag,
20
- buildInitializerTag,
21
17
  buildShiftTag,
22
18
  buildCloseNodeTag,
23
19
  buildLiteralTag,
24
20
  buildDoctypeTag,
21
+ buildAttributeDefinition,
22
+ nodeFlags,
25
23
  referenceFlags,
26
- buildProperty,
24
+ buildBindingTag,
25
+ buildDocument,
26
+ buildPropertyTag,
27
+ tokenFlags,
28
+ buildFullOpenNodeTag,
27
29
  } from '@bablr/agast-helpers/builders';
28
30
  import { buildEmbeddedMatcher, buildEmbeddedRegex } from './builders.js';
29
31
  import {
30
32
  AttributeDefinition,
33
+ CloseNodeTag,
31
34
  GapTag,
32
- InitializerTag,
33
35
  LiteralTag,
34
36
  NullTag,
35
- Property,
37
+ OpenNodeTag,
36
38
  } from './symbols.js';
37
- import { isStubNode } from '@bablr/agast-helpers/path';
39
+ import { buildNode, getFlags, getTags } from '@bablr/agast-helpers/path';
38
40
 
39
41
  const { freeze } = Object;
40
42
 
@@ -42,6 +44,7 @@ export const effectsFor = (verb) => {
42
44
  switch (verb) {
43
45
  case 'eat':
44
46
  case 'shift':
47
+ case 'eatHeld':
45
48
  return { success: 'eat', failure: 'fail' };
46
49
 
47
50
  case 'eatMatch':
@@ -59,108 +62,96 @@ export const effectsFor = (verb) => {
59
62
  }
60
63
  };
61
64
 
65
+ export const reifyBablrOptions = (str) => {
66
+ if (str.length !== 4) throw new Error();
67
+ let shift = !str.includes('S');
68
+ let bind = str.includes('b');
69
+ let allowEmpty = str.includes('e');
70
+ let internal = str.includes('i');
71
+
72
+ return freeze({ shift, bind, allowEmpty, internal });
73
+ };
74
+
62
75
  export const shouldBranch = (effects) => {
63
76
  return effects ? effects.success === 'none' || effects.failure === 'none' : false;
64
77
  };
65
78
 
79
+ export const reifyMatcherReferenceName = (matcher) => {
80
+ if (matcher == null) return null;
81
+ return reifyExpression(get(['refMatcher', 'name'], matcher.value));
82
+ };
83
+
66
84
  export const reifyNodeFlags = (flags) => {
67
- let { tokenToken, hasGapToken, fragmentToken, coverFragmentToken } = flags.properties;
85
+ let { token, hasGap } = flags.value.attributes;
68
86
 
69
87
  return {
70
- token: !!(tokenToken && reifyExpression(tokenToken.node)),
71
- hasGap: !!(hasGapToken && reifyExpression(hasGapToken.node)),
72
- fragment: !!(fragmentToken && reifyExpression(fragmentToken.node)),
73
- cover: !!(coverFragmentToken && reifyExpression(coverFragmentToken.node)),
88
+ token,
89
+ hasGap,
74
90
  };
75
91
  };
76
92
 
77
93
  export const reifyReferenceFlags = (flags) => {
78
- let { expressionToken, hasGapToken } = flags.properties;
79
-
80
- return {
81
- expression: !!(expressionToken && reifyExpression(expressionToken.node)),
82
- hasGap: !!(hasGapToken && reifyExpression(hasGapToken.node)),
83
- };
94
+ let arrayToken = get('arrayToken', flags);
95
+ let expressionToken = get('expressionToken', flags);
96
+ let intrinsicToken = get('intrinsicToken', flags);
97
+ let hasGapToken = get('hasGapToken', flags);
98
+
99
+ return freeze({
100
+ array: !!reifyExpression(arrayToken),
101
+ expression: !!reifyExpression(expressionToken),
102
+ intrinsic: !!reifyExpression(intrinsicToken),
103
+ hasGap: !!reifyExpression(hasGapToken),
104
+ });
84
105
  };
85
106
 
86
- export const reifyProperties = (properties = { node: [] }) => {
87
- const built = {};
88
- for (const property of btree.traverse(properties.node)) {
89
- switch (property.node.type) {
90
- case Symbol.for('Property'): {
91
- let { reference, value: node } = property.node.properties;
92
-
93
- reference = reference ? reifyExpression(reference.node) : buildReferenceTag('.');
94
- node = reifyExpression(node.node);
95
-
96
- let { name, isArray } = reference.value;
97
- if (name) {
98
- if (isArray) {
99
- built[name] ||= [];
100
- built[name].push({ reference, node });
101
- } else {
102
- built[name] = { reference, node };
103
- }
104
- }
105
- break;
106
- }
107
- }
108
- }
109
- return built;
110
- };
107
+ export const buildTags = (node) => {
108
+ let open = get('openTag', node);
111
109
 
112
- export const buildChildren = (node) => {
113
- let { open, children = [], close } = node.properties;
110
+ let openTag = reifyExpression(open);
111
+ let children = buildChildren(list('children', node));
112
+ let closeTag = buildCloseNodeTag();
114
113
 
115
- const selfClosing = !isNull(open.node.properties.selfClosingTagToken?.node);
116
- const { intrinsicValue } = open.node.properties;
117
- let built = [];
114
+ if (openTag.value.selfClosing) {
115
+ return Tags.fromValues([openTag]);
116
+ }
118
117
 
119
- open = reifyExpression(open.node);
120
- close = reifyExpression(close?.node);
118
+ return Tags.fromValues([openTag, children, closeTag]);
119
+ };
120
+
121
+ export const buildChildren = (children) => {
122
+ let built = Tags.fromValues([]);
121
123
 
122
- if (selfClosing) {
123
- built = sumtree.push(built, open);
124
- if (!isNull(intrinsicValue?.node)) {
125
- built = sumtree.push(built, buildLiteralTag(reifyExpression(intrinsicValue.node)));
124
+ for (const child of children) {
125
+ if ([AttributeDefinition, LiteralTag].includes(child.value.name)) {
126
+ built = Tags.push(built, reifyExpression(child));
127
+ continue;
126
128
  }
127
- built = sumtree.push(built, buildCloseNodeTag());
128
- } else {
129
- built = sumtree.push(built, open);
130
- for (const child of sumtree.traverse(children.node)) {
131
- if ([AttributeDefinition, LiteralTag].includes(child.node.type)) {
132
- built = sumtree.push(built, reifyExpression(child.node));
133
- continue;
134
- }
135
129
 
136
- if (child.node.type !== Symbol.for('Property')) throw new Error('umimplemented');
130
+ if (child.value.name !== Symbol.for('Property')) throw new Error('umimplemented');
137
131
 
138
- let { reference, binding, value } = child.node.properties;
132
+ let reference = get('referenceTag', child);
133
+ let boundNode = reifyExpression(get('value', child));
134
+ let { node, bindingTags } = boundNode;
139
135
 
140
- let referenceTag = reference ? reifyExpression(reference.node) : buildReferenceTag('.');
141
- let bindingTag = binding
142
- ? reifyExpression(binding.node)
143
- : buildBindingTag(isStubNode(value.node) ? null : []);
136
+ let bindings = bindingTags.map((tag) => tag.value);
144
137
 
145
- value = reifyExpression(value.node);
138
+ let referenceTag =
139
+ reference && !isNullNode(reference) ? reifyExpression(reference) : buildReferenceTag();
146
140
 
147
- if (value.type === NullTag || value.type === GapTag) {
148
- value = buildStubNode(value);
141
+ if (node.value.type === Symbol.for('__')) {
142
+ for (let child of Tags.traverse(getTags(node))) {
143
+ if (![OpenNodeTag, CloseNodeTag].includes(child.value.type))
144
+ built = Tags.push(built, child);
149
145
  }
146
+ continue;
147
+ }
150
148
 
151
- built = sumtree.push(built, referenceTag);
152
- if (value.type === InitializerTag) {
153
- built = sumtree.push(built, value);
154
- } else {
155
- built = sumtree.push(built, bindingTag);
156
- built = sumtree.push(
157
- built,
158
- buildChild(Property, buildProperty(referenceTag.value, buildBindingTag().value, value)),
159
- );
160
- }
149
+ if (node.value.name === NullTag || node.value.name === GapTag) {
150
+ throw new Error('not implemented');
151
+ node = buildNode(node);
161
152
  }
162
153
 
163
- built = sumtree.push(built, close);
154
+ built = Tags.push(built, buildPropertyTag([referenceTag, bindingTags, node]));
164
155
  }
165
156
 
166
157
  return built;
@@ -172,91 +163,135 @@ export const reifyExpression = (node) => {
172
163
  if (node == null) return node;
173
164
  if (isNullNode(node)) return null;
174
165
 
175
- if (isFragmentNode(node)) {
166
+ if (isCover(node)) {
176
167
  node = getRoot(node);
177
168
  }
178
169
 
179
- switch (node.type?.description || node.type) {
170
+ if (getFlags(node)?.token && !node.value.name) {
171
+ return getCooked(node);
172
+ }
173
+
174
+ switch (node.value.name?.description) {
180
175
  case 'Document': {
181
- let { doctype, tree } = node.properties;
176
+ let tree = get(['tree', 'node'], node);
182
177
 
183
- doctype = reifyExpression(doctype.node);
184
- tree = reifyExpression(tree.node);
178
+ tree = reifyExpression(tree);
185
179
 
186
- let { attributes } = doctype.value;
187
- let { properties } = tree;
180
+ let doctypeTag = null;
188
181
 
189
- return Object.freeze({
190
- flags: nodeFlags,
191
- type: null,
192
- children: sumtree.addAt(
193
- 0,
194
- buildChildren(node.properties.tree.node),
195
- buildDoctypeTag(attributes),
196
- ),
197
- properties,
198
- attributes,
199
- });
182
+ return buildDocument(doctypeTag, tree);
200
183
  }
201
184
 
202
- case 'Node': {
203
- let { open, children } = node.properties;
185
+ case 'TreeNode': {
186
+ let open = get('openTag', node);
204
187
 
205
- open = reifyExpression(open.node);
188
+ let openTag = reifyExpression(open);
206
189
 
207
- let { flags, type, attributes } = open.value;
190
+ let { flags, name, attributes, literalValue } = openTag.value;
208
191
 
209
- const properties = reifyProperties(children);
192
+ if (literalValue && !flags.token) {
193
+ let tokenFragment = buildNode(
194
+ Tags.fromValues([buildOpenNodeTag(tokenFlags, null, literalValue, {}, true)]),
195
+ );
210
196
 
211
- return Object.freeze({
212
- flags,
213
- type,
214
- children: buildChildren(node),
215
- properties,
216
- attributes,
217
- });
197
+ return buildNode(
198
+ Tags.fromValues([
199
+ buildOpenNodeTag(flags, name, null, attributes),
200
+ Tags.fromValues([buildPropertyTag([buildReferenceTag('_'), [], tokenFragment])]),
201
+ buildCloseNodeTag(),
202
+ ]),
203
+ );
204
+ } else {
205
+ return buildNode(buildTags(node));
206
+ }
218
207
  }
219
208
 
220
209
  case 'DoctypeTag': {
221
- let { version, attributes } = node.properties;
210
+ let version = get('version', node);
211
+ let attributes = get('attributes', node);
212
+
222
213
  return buildDoctypeTag(
223
- attributes && reifyExpression(attributes.node),
224
- parseInt(sourceTextFor(version.node), 10),
214
+ attributes && reifyExpression(attributes),
215
+ parseInt(sourceTextFor(version), 10),
225
216
  );
226
217
  }
227
218
 
228
219
  case 'ReferenceTag': {
229
- let { type, name, arrayOperatorToken, flags } = node.properties;
220
+ let type = get('type', node);
221
+ let name = get('name', node);
222
+ let flags = get('flags', node);
230
223
 
231
- name = name && reifyExpression(name.node);
232
- type = type && reifyExpression(type.node);
233
- flags = freeze({ expression: !!flags?.expressionToken, hasGap: !!flags?.hasGapToken });
224
+ name = name && (reifyExpression(name) || null);
225
+ type = type && reifyExpression(type);
226
+ flags = (flags && reifyReferenceFlags(flags)) || undefined;
234
227
 
235
- return buildReferenceTag(type, name, !isNull(arrayOperatorToken?.node), flags);
228
+ return buildReferenceTag(type, name, flags);
236
229
  }
237
230
 
238
231
  case 'LiteralTag': {
239
- let { value } = node.properties;
240
-
241
- return buildLiteralTag(getCooked(value.node.properties.content.node));
232
+ return buildLiteralTag(getCooked(get(['value', 'content'], node)));
242
233
  }
243
234
 
244
235
  case 'Identifier': {
245
- return getCooked(node.properties.content.node);
236
+ return getCooked(get('content', node));
246
237
  }
247
238
 
248
239
  case 'IdentifierPath': {
249
- return node.properties.segments.node.map((segment) => reifyExpression(segment.node));
240
+ return freeze(
241
+ [...list('segments', node)].map((segment) =>
242
+ freeze({ type: null, name: reifyExpression(segment) }),
243
+ ),
244
+ );
245
+ }
246
+
247
+ case 'AttributeDefinition': {
248
+ let key = get('key', node);
249
+ let value = get('value', node);
250
+
251
+ key = key && reifyExpression(key).map((segment) => segment.name);
252
+ value = value && reifyExpression(value);
253
+
254
+ return buildAttributeDefinition(key, value);
255
+ }
256
+
257
+ case 'NodeFlags': {
258
+ return reifyNodeFlags(node);
259
+ }
260
+
261
+ case 'ReferenceFlags': {
262
+ return reifyReferenceFlags(node);
250
263
  }
251
264
 
252
265
  case 'OpenNodeTag': {
253
- let { flags, type, attributes } = node.properties;
266
+ let flags = get('flags', node);
267
+ let type = get('type', node);
268
+ let name = get('name', node);
269
+ let attributes = get('attributes', node);
270
+ let literalValue = get('literalValue', node);
271
+ let openToken = get('openToken', node);
272
+ let selfClosing = get('selfClosingToken', node);
273
+
274
+ literalValue = literalValue && reifyExpression(literalValue);
254
275
 
255
- flags = reifyNodeFlags(flags.node);
256
- type = reifyExpression(type?.node);
257
- attributes = reifyExpression(attributes?.node);
276
+ if (!openToken) {
277
+ if (!literalValue) throw new Error();
258
278
 
259
- return buildOpenNodeTag(flags, type, attributes);
279
+ return buildOpenNodeTag(tokenFlags, null, literalValue, {});
280
+ }
281
+
282
+ let anonymousToken = !openToken;
283
+
284
+ flags = anonymousToken ? tokenFlags : (flags && reifyNodeFlags(flags)) || nodeFlags;
285
+ type = reifyExpression(type);
286
+ name = reifyExpression(name);
287
+ attributes = reifyExpression(attributes) || {};
288
+ selfClosing = !!reifyExpression(selfClosing);
289
+
290
+ // if (literalValue && !flags.token) {
291
+ // return buildOpenNodeTag(tokenFlags, null, literalValue, {}, true);
292
+ // }
293
+
294
+ return buildFullOpenNodeTag(flags, type, name, literalValue, attributes, selfClosing);
260
295
  }
261
296
 
262
297
  case 'CloseNodeTag': {
@@ -264,153 +299,186 @@ export const reifyExpression = (node) => {
264
299
  }
265
300
 
266
301
  case 'Integer': {
267
- let { digits } = node.properties;
268
- return parseInt(digits.map((digit) => getCooked(digit.node)).join(''), 10);
302
+ return parseInt([...list('digits', node)].map((digit) => getCooked(digit)).join(''), 10);
269
303
  }
270
304
 
271
305
  case 'Infinity': {
272
- return getCooked(node.properties.sign.node) === '-' ? -Infinity : Infinity;
273
- }
274
-
275
- case 'Punctuator': {
276
- return getCooked(node);
306
+ return getCooked(get('sign', node)) === '-' ? -Infinity : Infinity;
277
307
  }
278
308
 
279
309
  case 'GapTag':
280
310
  return buildGapTag();
281
311
 
282
- case 'InitializerTag':
283
- return buildInitializerTag();
284
-
285
- case 'BindingTag':
286
- let { languagePath } = node.properties;
287
-
288
- return buildBindingTag(reifyExpression(languagePath));
289
-
290
312
  case 'NullTag':
291
313
  return buildNullTag();
292
314
 
315
+ case 'NullNode':
316
+ case 'GapNode':
317
+ let tag = reifyExpression(get('sigilTag', node));
318
+ return buildNode(tag);
319
+
293
320
  case 'ShiftTag':
294
321
  return buildShiftTag();
295
322
 
296
323
  case 'String':
297
- return node.properties.content.node ? getCooked(node.properties.content.node) : '';
324
+ return get('content', node) ? getCooked(get('content', node)) : '';
298
325
 
299
326
  case 'SpamexString': {
300
- return buildEmbeddedMatcher(node.properties.content.node);
327
+ return buildEmbeddedMatcher(get('content', node));
301
328
  }
302
329
 
303
330
  case 'RegexString': {
304
- return buildEmbeddedRegex(node.properties.content.node);
331
+ return buildEmbeddedRegex(get('content', node));
305
332
  }
306
333
 
307
- case 'OpenNodeMatcher': {
308
- let { flags, type, attributes, intrinsicValue } = node.properties;
334
+ case 'TreeNodeMatcherOpen': {
335
+ let flags = get('flags', node);
336
+ let name = get('name', node);
337
+ let type = get('type', node);
338
+ let attributes = get('attributes', node);
339
+ let literalValue = get('literalValue', node);
340
+
341
+ flags = (flags && reifyNodeFlags(flags)) || {};
342
+ name = name
343
+ ? name.value.name === Symbol.for('String')
344
+ ? getCooked(get('content', name))
345
+ : reifyExpression(name)
346
+ : null;
347
+ type = reifyExpression(type);
348
+ attributes = attributes ? reifyExpression(attributes) : {};
349
+ literalValue = literalValue && reifyExpression(literalValue);
350
+
351
+ return freeze({
352
+ flags,
353
+ type,
354
+ name,
355
+ literalValue,
356
+ attributes,
357
+ });
358
+ }
309
359
 
310
- flags = (flags && reifyNodeFlags(flags.node)) || {};
311
- type =
312
- type.node.type === Symbol.for('String')
313
- ? getCooked(type.node.properties.content.node)
314
- : reifyExpression(type.node);
315
- attributes = attributes ? reifyExpression(attributes.node) : {};
316
- intrinsicValue = intrinsicValue && reifyExpression(intrinsicValue.node);
360
+ case 'TreeNodeMatcher': {
361
+ let open = reifyExpression(get('open', node));
317
362
 
318
- return { flags, type, intrinsicValue, attributes };
363
+ if (!open) {
364
+ return [...list('children', node)].map(reifyExpression)[0];
365
+ }
366
+
367
+ return open;
319
368
  }
320
369
 
321
- case 'FragmentMatcher': {
322
- let { flags } = node.properties;
370
+ case 'PropertyMatcher': {
371
+ let refMatcher = get('refMatcher', node);
372
+ let boundMatcher = get('valueMatcher', node);
373
+
374
+ refMatcher = reifyExpression(refMatcher);
323
375
 
324
- flags = (flags && reifyNodeFlags(flags.node)) || {};
376
+ if (!boundMatcher) throw new Error();
325
377
 
326
- return {
327
- flags,
328
- type: Symbol.for('@bablr/fragment'),
329
- intrinsicValue: null,
330
- attributes: null,
331
- };
378
+ if (boundMatcher.value.name !== Symbol.for('BoundNodeMatcher')) throw new Error();
379
+
380
+ let { nodeMatcher, bindingMatchers } = reifyExpression(boundMatcher);
381
+
382
+ return freeze({ refMatcher, bindingMatchers, nodeMatcher });
332
383
  }
333
384
 
334
- case 'BasicNodeMatcher': {
335
- let { open } = node.properties;
385
+ case 'ReferenceMatcher': {
386
+ let type = get('type', node);
387
+ let name = get('name', node);
388
+ let flags = get('flags', node);
389
+
390
+ type = type && reifyExpression(type);
391
+ name = name && reifyExpression(name);
392
+ flags = (flags && reifyReferenceFlags(flags)) || referenceFlags;
336
393
 
337
- return reifyExpression(open.node);
394
+ return freeze({ type, name, flags });
338
395
  }
339
396
 
340
- case 'PropertyMatcher': {
341
- let { refMatcher, bindingMatcher, nodeMatcher } = node.properties;
397
+ case 'BoundNodeMatcher': {
398
+ let bindingMatchers, nodeMatcher;
399
+ // if (get(['nodeMatcher', 'open'], node)) {
400
+ bindingMatchers = [...list('bindingMatchers', node)].map(reifyExpression);
401
+ nodeMatcher = reifyExpression(get('nodeMatcher', node));
402
+ // } else {
403
+ // ({ bindingMatchers, nodeMatcher } = reifyExpression(get('nodeMatcher', node)));
404
+ // }
405
+
406
+ return freeze({ bindingMatchers, nodeMatcher });
407
+ }
342
408
 
343
- refMatcher = refMatcher ? reifyExpression(refMatcher.node) : null;
344
- bindingMatcher = bindingMatcher ? reifyExpression(bindingMatcher.node) : null;
345
- nodeMatcher = reifyExpression(nodeMatcher.node);
409
+ case 'BoundNode': {
410
+ let bindingTags = [],
411
+ node_;
412
+ if (get(['node', 'openTag'], node)) {
413
+ bindingTags = [...list('bindingTags', node)].map(reifyExpression);
414
+ node_ = reifyExpression(get('node', node));
415
+ } else {
416
+ ({ bindingTags, node: node_ } = reifyExpression(get('node', node)));
417
+ }
346
418
 
347
- return { refMatcher, bindingMatcher, nodeMatcher };
419
+ return freeze({ bindingTags, node: node_ });
348
420
  }
349
421
 
350
- case 'ReferenceMatcher': {
351
- let { type, name, openIndexToken, flags } = node.properties;
422
+ case 'BindingTag': {
423
+ let segments = [...list('segments', node)].map((segment) => {
424
+ return reifyExpression(segment);
425
+ });
426
+ return buildBindingTag(segments);
427
+ }
352
428
 
353
- type = type && reifyExpression(type.node);
354
- name = name && reifyExpression(name.node);
355
- let isArray = !isNull(openIndexToken?.node);
356
- flags = (flags && reifyReferenceFlags(flags?.node)) || referenceFlags;
429
+ case 'BindingSegment': {
430
+ let path = get('path', node);
357
431
 
358
- return { type, name, isArray, flags };
432
+ switch (path.value.name && path.value.name.description) {
433
+ case 'Identifier':
434
+ return freeze({ type: null, name: reifyExpression(path) });
435
+ case null:
436
+ return freeze({ type: reifyExpression(path), name: null });
437
+ default:
438
+ throw new Error();
439
+ }
359
440
  }
360
441
 
361
442
  case 'BindingMatcher': {
362
- let { languagePath } = node.properties;
363
-
364
- languagePath = languagePath && reifyExpression(languagePath.node);
443
+ let segments = [...list('segments', node)].map((segment) => reifyExpression(segment));
365
444
 
366
- return { languagePath };
445
+ return freeze({ segments });
367
446
  }
368
447
 
369
448
  case 'GapNodeMatcher':
370
- return buildStubNode(buildGapTag());
449
+ return buildNode(buildGapTag());
371
450
 
372
451
  case 'NullNodeMatcher':
373
- return buildStubNode(buildNullTag());
374
-
375
- case 'ArrayNodeMatcher':
376
- return [];
452
+ return buildNode(buildNullTag());
377
453
 
378
454
  case 'Call': {
379
- const { verb, arguments: args } = node.properties;
455
+ const verb = get('verb', node);
380
456
 
381
- const args_ = [...btree.traverse(args.node)].map((el) => reifyExpression(el.node));
457
+ const args = [...list('arguments', node)].map((el) => reifyExpression(el));
382
458
 
383
- return { verb: reifyExpression(verb.node), arguments: args_ };
459
+ return freeze({ verb: reifyExpression(verb), arguments: args });
384
460
  }
385
461
 
386
462
  case 'Object': {
387
- const { properties } = node.properties;
388
-
389
463
  return Object.fromEntries(
390
- [...btree.traverse(properties.node)].map((property) => {
391
- const {
392
- node: {
393
- properties: { key, value },
394
- },
395
- } = property;
396
- return [getCooked(key.node), reifyExpression(value.node)];
464
+ [...list('properties', node)].map((property) => {
465
+ const key = get('key', property);
466
+ const value = get('value', property);
467
+ return freeze([reifyExpression(key), reifyExpression(value)]);
397
468
  }),
398
469
  );
399
470
  }
400
471
 
401
472
  case 'Array': {
402
- const { elements = [] } = node.properties;
403
-
404
- return [...btree.traverse(elements.node)].map((el) => reifyExpression(el.node));
473
+ return freeze([...list('elements', node)].map((el) => reifyExpression(el)));
405
474
  }
406
475
 
407
- case 'Punctuator':
408
476
  case 'Keyword':
409
477
  return getCooked(node);
410
478
 
411
479
  case 'Boolean': {
412
480
  // prettier-ignore
413
- switch (getCooked(node.properties.sigilToken.node)) {
481
+ switch (getCooked(get('sigilToken', node))) {
414
482
  case 'true': return true;
415
483
  case 'false': return false;
416
484
  default: throw new Error();
package/lib/print.js CHANGED
@@ -2,40 +2,52 @@ import {
2
2
  printSource,
3
3
  printTag,
4
4
  printExpression as printExpression_,
5
+ printCSTML,
5
6
  } from '@bablr/agast-helpers/tree';
6
7
  import {
7
- EmbeddedNode,
8
- EmbeddedTag,
8
+ Tag,
9
9
  EmbeddedObject,
10
- EmbeddedMatcher,
11
- EmbeddedRegex,
12
- EmbeddedInstruction,
10
+ Matcher,
11
+ Regex,
12
+ Instruction,
13
+ TreeNode,
14
+ NullNode,
15
+ GapNode,
16
+ Node,
13
17
  } from './symbols.js';
14
18
 
15
19
  export const printEmbedded = (value) => {
16
20
  switch (value.type) {
17
- case EmbeddedTag:
21
+ case Tag:
18
22
  return `t\`${printTag(value.value)}\``;
19
23
 
20
- case EmbeddedMatcher:
24
+ case Node:
25
+ return `n\`${printCSTML(value.value)}\``;
26
+
27
+ case Matcher:
21
28
  return `m\`${printSource(value.value)}\``;
22
29
 
23
- case EmbeddedRegex:
30
+ case Regex:
24
31
  return `re\`${printSource(value.value)}\``;
25
32
 
26
- case EmbeddedInstruction:
33
+ case Instruction:
27
34
  return `i\`${printCall(value.value)}\``;
28
35
 
29
36
  case EmbeddedObject: {
30
37
  return printObject(value.value);
31
38
  }
32
39
 
33
- case EmbeddedNode: {
34
- return printSource(value.value);
35
- }
40
+ case NullNode:
41
+ case GapNode:
42
+ case TreeNode:
43
+ throw new Error(
44
+ 'Cannot print plain node. Did you mean to wrap this node in buildEmbeddedNode?',
45
+ );
36
46
 
37
47
  default:
38
- throw new Error();
48
+ throw new Error(
49
+ 'Cannot print plain object. Did you mean to wrap this object in buildEmbedded*?',
50
+ );
39
51
  }
40
52
  };
41
53
 
package/lib/stream.js CHANGED
@@ -1,23 +1,21 @@
1
1
  import { Coroutine } from '@bablr/coroutine';
2
2
  import {
3
- InitializerTag,
4
3
  CloseNodeTag,
5
4
  EmbeddedObject,
6
5
  GapTag,
7
6
  NullTag,
8
7
  OpenNodeTag,
9
8
  ReferenceTag,
10
- TokenGroup,
11
9
  } from './symbols.js';
12
10
  import {
13
11
  getStreamIterator,
14
- prettyGroupTags,
15
- printSelfClosingNodeTag,
16
12
  printTag,
13
+ wait,
17
14
  StreamIterable,
15
+ prettyGroupTags,
16
+ hoistTrivia,
18
17
  } from '@bablr/agast-helpers/stream';
19
18
  import { buildWriteEffect } from './builders.js';
20
- import { getCooked } from '@bablr/agast-helpers/tree';
21
19
 
22
20
  const getEmbeddedObject = (obj) => {
23
21
  if (obj.type !== EmbeddedObject) throw new Error();
@@ -31,7 +29,7 @@ function* __generateStandardOutput(tags) {
31
29
  co.advance();
32
30
 
33
31
  if (co.current instanceof Promise) {
34
- co.current = yield co.current;
32
+ co.current = yield wait(co.current);
35
33
  }
36
34
  if (co.done) break;
37
35
 
@@ -60,7 +58,7 @@ function* __generateAllOutput(tags) {
60
58
  co.advance();
61
59
 
62
60
  if (co.current instanceof Promise) {
63
- co.current = yield co.current;
61
+ co.current = yield wait(co.current);
64
62
  }
65
63
  if (co.done) break;
66
64
 
@@ -101,7 +99,7 @@ function* __writeCSTMLStrategy(tags) {
101
99
  co.advance();
102
100
 
103
101
  if (co.current instanceof Promise) {
104
- co.current = yield co.current;
102
+ co.current = yield wait(co.current);
105
103
  }
106
104
  if (co.done) break;
107
105
 
@@ -117,12 +115,7 @@ function* __writeCSTMLStrategy(tags) {
117
115
  continue;
118
116
  }
119
117
 
120
- if (tag.type === TokenGroup) {
121
- const intrinsicValue = getCooked(tag.value);
122
- yield buildWriteEffect(printSelfClosingNodeTag(tag.value[0], intrinsicValue));
123
- } else {
124
- yield buildWriteEffect(printTag(tag));
125
- }
118
+ yield buildWriteEffect(printTag(tag));
126
119
 
127
120
  prevTag = tag;
128
121
  }
@@ -141,7 +134,8 @@ function* __writePrettyCSTMLStrategy(tags, options) {
141
134
  return;
142
135
  }
143
136
 
144
- const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
137
+ const co = new Coroutine(getStreamIterator(prettyGroupTags(hoistTrivia(tags))));
138
+ // const co = new Coroutine(getStreamIterator(prettyGroupTags(tags)));
145
139
  let indentLevel = 0;
146
140
  let first = true;
147
141
  let inline = false;
@@ -150,11 +144,10 @@ function* __writePrettyCSTMLStrategy(tags, options) {
150
144
  for (;;) {
151
145
  co.advance();
152
146
 
153
- if (co.done) break;
154
-
155
147
  if (co.current instanceof Promise) {
156
- co.current = yield co.current;
148
+ co.current = yield wait(co.current);
157
149
  }
150
+ if (co.done) break;
158
151
 
159
152
  const tag = co.value;
160
153
 
@@ -181,8 +174,7 @@ function* __writePrettyCSTMLStrategy(tags, options) {
181
174
  ref &&
182
175
  (tag.type === NullTag ||
183
176
  tag.type === GapTag ||
184
- tag.type === InitializerTag ||
185
- tag.type === TokenGroup);
177
+ (tag.type === OpenNodeTag && tag.value.selfClosing));
186
178
 
187
179
  if (!first && !inline) {
188
180
  yield buildWriteEffect('\n');
@@ -203,20 +195,14 @@ function* __writePrettyCSTMLStrategy(tags, options) {
203
195
  yield buildWriteEffect(' ');
204
196
  }
205
197
 
206
- if (tag.type === TokenGroup) {
207
- ref = null;
208
- const intrinsicValue = tag.value[0].value.flags.token ? getCooked(tag.value) : null;
209
- yield buildWriteEffect(printSelfClosingNodeTag(tag.value[0], intrinsicValue));
210
- } else {
211
- yield buildWriteEffect(printTag(tag));
212
- }
198
+ yield buildWriteEffect(printTag(tag));
213
199
 
214
200
  if (tag.type === ReferenceTag) {
215
201
  inline = true;
216
202
  ref = tag;
217
203
  }
218
204
 
219
- if (tag.type === OpenNodeTag) {
205
+ if (tag.type === OpenNodeTag && !tag.value.selfClosing) {
220
206
  indentLevel++;
221
207
  }
222
208
 
package/lib/symbols.js CHANGED
@@ -1,11 +1,8 @@
1
1
  export * from '@bablr/agast-helpers/symbols';
2
2
 
3
- export const node = Symbol.for('@bablr/node');
4
- export const fragment = Symbol.for('@bablr/fragment');
5
-
6
- export const EmbeddedInstruction = Symbol.for('EmbeddedInstruction');
7
- export const EmbeddedMatcher = Symbol.for('EmbeddedMatcher');
8
- export const EmbeddedNode = Symbol.for('EmbeddedNode');
9
- export const EmbeddedRegex = Symbol.for('EmbeddedRegex');
10
- export const EmbeddedTag = Symbol.for('EmbeddedTag');
11
- export const EmbeddedObject = Symbol.for('EmbeddedObject');
3
+ export const Instruction = Symbol.for('Instruction');
4
+ export const Matcher = Symbol.for('Matcher');
5
+ export const Regex = Symbol.for('Regex');
6
+ export const Tag = Symbol.for('Tag');
7
+ export const Object = Symbol.for('Object');
8
+ export const EmbeddedObject = Object;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bablr/agast-vm-helpers",
3
3
  "description": "Helper functions for working with the BABLR VM",
4
- "version": "0.8.0",
4
+ "version": "0.10.0",
5
5
  "author": "Conrad Buck<conartist6@gmail.com>",
6
6
  "type": "module",
7
7
  "files": [
@@ -22,7 +22,7 @@
22
22
  },
23
23
  "sideEffects": false,
24
24
  "dependencies": {
25
- "@bablr/agast-helpers": "0.8.0",
25
+ "@bablr/agast-helpers": "0.10.0",
26
26
  "@bablr/coroutine": "0.1.0"
27
27
  },
28
28
  "devDependencies": {