@bablr/agast-vm 0.5.0 → 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/context.js CHANGED
@@ -100,10 +100,8 @@ export const Context = class AgastContext {
100
100
  return this.nextTags.get(token);
101
101
  }
102
102
 
103
- sourceTextFor(nodeOrRange) {
104
- return isArray(nodeOrRange)
105
- ? sourceTextForStream(this.allTagsFor(nodeOrRange))
106
- : sourceTextForTree(nodeOrRange);
103
+ sourceTextFor(node) {
104
+ return sourceTextForTree(node);
107
105
  }
108
106
 
109
107
  buildRange(tags) {
package/lib/evaluate.js CHANGED
@@ -13,7 +13,13 @@ import {
13
13
  import { getEmbeddedExpression } from '@bablr/agast-vm-helpers/deembed';
14
14
  import { StreamIterable, getStreamIterator } from '@bablr/agast-helpers/stream';
15
15
  import { printExpression } from '@bablr/agast-helpers/print';
16
- import { getRange, getOpenTag, buildArrayTag } from '@bablr/agast-helpers/tree';
16
+ import {
17
+ getRange,
18
+ getOpenTag,
19
+ buildArrayTag,
20
+ buildFragmentCloseTag,
21
+ buildFragmentOpenTag,
22
+ } from '@bablr/agast-helpers/tree';
17
23
  import {
18
24
  DoctypeTag,
19
25
  OpenNodeTag,
@@ -24,6 +30,8 @@ import {
24
30
  NullTag,
25
31
  ArrayTag,
26
32
  LiteralTag,
33
+ CloseFragmentTag,
34
+ OpenFragmentTag,
27
35
  } from '@bablr/agast-helpers/symbols';
28
36
  import * as btree from '@bablr/agast-helpers/btree';
29
37
  import { State } from './state.js';
@@ -116,7 +124,7 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
116
124
  }
117
125
 
118
126
  case ReferenceTag: {
119
- const { name, isArray } = tag.value;
127
+ const { name, isArray, hasGap } = tag.value;
120
128
 
121
129
  if (s.result.type === ReferenceTag) {
122
130
  throw new Error('A reference must have a non-reference value');
@@ -126,7 +134,7 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
126
134
  throw new Error('A token node cannot contain a reference');
127
135
  }
128
136
 
129
- returnValue = s.advance(buildReferenceTag(name, isArray));
137
+ returnValue = s.advance(buildReferenceTag(name, isArray, hasGap));
130
138
  break;
131
139
  }
132
140
 
@@ -183,6 +191,13 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
183
191
  break;
184
192
  }
185
193
 
194
+ case OpenFragmentTag: {
195
+ const { flags } = tag.value;
196
+
197
+ returnValue = s.advance(buildFragmentOpenTag(flags), getEmbeddedExpression(options));
198
+ break;
199
+ }
200
+
186
201
  case CloseNodeTag: {
187
202
  const { type, language } = tag.value;
188
203
 
@@ -190,6 +205,11 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
190
205
  break;
191
206
  }
192
207
 
208
+ case CloseFragmentTag: {
209
+ returnValue = s.advance(buildFragmentCloseTag());
210
+ break;
211
+ }
212
+
193
213
  default:
194
214
  throw new Error();
195
215
  }
@@ -214,10 +234,6 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
214
234
 
215
235
  if (key === 'span') throw new Error('too late');
216
236
 
217
- if (key === 'balancedSpan') {
218
- throw new Error('not implemented');
219
- }
220
-
221
237
  // if (stateIsDifferent) {
222
238
  // // we can't allow effects to cross state branches
223
239
  // throw new Error();
@@ -277,7 +293,7 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
277
293
 
278
294
  case 'write': {
279
295
  if (options.emitEffects) {
280
- yield buildWriteEffect(args[0], getEmbeddedExpression(args[1]));
296
+ yield buildWriteEffect(args[0], args[1].value);
281
297
  }
282
298
  break;
283
299
  }
package/lib/path.js CHANGED
@@ -55,7 +55,7 @@ export const Path = class AgastPath extends WeakStackFrame {
55
55
  }
56
56
 
57
57
  get name() {
58
- return this.reference?.value.name || '[anonymous]';
58
+ return this.reference?.value.name;
59
59
  }
60
60
 
61
61
  get isArray() {
package/lib/state.js CHANGED
@@ -11,14 +11,13 @@ import {
11
11
  finalizeNode,
12
12
  getRoot,
13
13
  printType,
14
+ buildStubNode,
14
15
  } from '@bablr/agast-helpers/tree';
15
16
  import * as btree from '@bablr/agast-helpers/btree';
16
17
  import {
17
18
  buildBeginningOfStreamToken,
18
19
  buildEmbeddedNode,
19
- nodeFlags,
20
20
  } from '@bablr/agast-vm-helpers/internal-builders';
21
- import * as sym from '@bablr/agast-helpers/symbols';
22
21
  import {
23
22
  DoctypeTag,
24
23
  OpenNodeTag,
@@ -29,45 +28,24 @@ import {
29
28
  NullTag,
30
29
  ArrayTag,
31
30
  LiteralTag,
31
+ OpenFragmentTag,
32
+ CloseFragmentTag,
32
33
  } from '@bablr/agast-helpers/symbols';
33
34
  import { facades, actuals } from './facades.js';
34
35
  import { Path } from './path.js';
36
+ import { isArray } from 'iter-tools-es';
35
37
 
36
38
  const { hasOwn } = Object;
37
39
 
38
- const arrayLast = (arr) => arr[arr.length - 1];
39
-
40
- const createNodeWithState = (startTag, options = {}) => {
40
+ const createNodeWithState = (openTag, options = {}) => {
41
41
  const { unboundAttributes } = options;
42
- const node = createNode(startTag);
42
+ const node = createNode(openTag);
43
43
  nodeStates.set(node, {
44
44
  unboundAttributes: new Set(unboundAttributes || []),
45
45
  });
46
46
  return node;
47
47
  };
48
48
 
49
- const symbolTypeFor = (type) => {
50
- // prettier-ignore
51
- switch (type) {
52
- case NullTag: return sym.null;
53
- case GapTag: return sym.gap;
54
- default: throw new Error();
55
- }
56
- };
57
-
58
- const { freeze } = Object;
59
-
60
- const buildStubNode = (tag) => {
61
- return freeze({
62
- flags: nodeFlags,
63
- language: null,
64
- type: symbolTypeFor(tag.type),
65
- children: freeze([tag]),
66
- properties: freeze({}),
67
- attributes: freeze({}),
68
- });
69
- };
70
-
71
49
  export const StateFacade = class AgastStateFacade {
72
50
  constructor(state) {
73
51
  facades.set(state, this);
@@ -110,11 +88,11 @@ export const StateFacade = class AgastStateFacade {
110
88
  }
111
89
 
112
90
  nodeForPath(path) {
113
- return actuals.get(this).nodeForPath(path);
91
+ return actuals.get(this).nodeForPath(actuals.get(path));
114
92
  }
115
93
 
116
94
  pathForTag(tag) {
117
- return actuals.get(this).pathForTag(tag);
95
+ return facades.get(actuals.get(this).pathForTag(tag));
118
96
  }
119
97
 
120
98
  nodeForTag(tag) {
@@ -206,7 +184,7 @@ export const State = class AgastState extends WeakStackFrame {
206
184
  this.result?.type === ReferenceTag &&
207
185
  ![OpenNodeTag, GapTag, NullTag, ArrayTag].includes(tag.type)
208
186
  ) {
209
- throw new Error(`${tag.type} is not a valid reference target`);
187
+ throw new Error(`${printType(tag.type)} is not a valid reference target`);
210
188
  }
211
189
 
212
190
  prevTags.set(tag, this.result);
@@ -224,28 +202,24 @@ export const State = class AgastState extends WeakStackFrame {
224
202
 
225
203
  case OpenNodeTag: {
226
204
  const openTag = tag;
227
- const { type, flags } = tag.value;
205
+ const { flags } = tag.value;
228
206
  this.node = createNodeWithState(tag, options);
229
207
 
230
208
  const reference = this.result;
231
209
 
232
210
  this.node.children = btree.push(this.node.children, tag);
233
211
 
234
- if (!type) {
235
- this.node.attributes = this.result.value.attributes;
236
- } else {
237
- if (!flags.trivia && !flags.escape) {
238
- if (
239
- reference.type !== ReferenceTag &&
240
- reference.type !== ShiftTag &&
241
- reference.type !== OpenNodeTag &&
242
- !reference.value.type
243
- ) {
244
- throw new Error('Invalid location for OpenNodeTag');
245
- }
246
- } else {
247
- this.path = this.path.push(ctx, null, btree.getSum(this.node.children));
212
+ if (!flags.trivia && !flags.escape) {
213
+ if (
214
+ reference.type !== ReferenceTag &&
215
+ reference.type !== ShiftTag &&
216
+ reference.type !== OpenNodeTag &&
217
+ !reference.value.type
218
+ ) {
219
+ throw new Error('Invalid location for OpenNodeTag');
248
220
  }
221
+ } else {
222
+ this.path = this.path.push(ctx, null, btree.getSum(this.node.children));
249
223
  }
250
224
 
251
225
  this.pathNodes.set(this.node, this.path);
@@ -256,6 +230,25 @@ export const State = class AgastState extends WeakStackFrame {
256
230
  break;
257
231
  }
258
232
 
233
+ case OpenFragmentTag: {
234
+ const openTag = tag;
235
+ this.node = createNodeWithState(tag, options);
236
+
237
+ const reference = this.result;
238
+
239
+ this.node.attributes = this.result.value.attributes;
240
+ this.node.children = btree.push(this.node.children, reference);
241
+
242
+ this.node.children = btree.push(this.node.children, tag);
243
+
244
+ this.pathNodes.set(this.node, this.path);
245
+ this.pathNodes.set(this.path, this.node);
246
+
247
+ this.tagNodes.set(openTag, this.node);
248
+ this.tagPaths.set(openTag, this.path);
249
+ break;
250
+ }
251
+
259
252
  case CloseNodeTag: {
260
253
  const openTag = getOpenTag(this.node);
261
254
  const { flags, type: openType } = openTag.value;
@@ -264,28 +257,41 @@ export const State = class AgastState extends WeakStackFrame {
264
257
 
265
258
  this.node.children = btree.push(this.node.children, tag);
266
259
 
267
- if (openType) {
268
- if (this.node.unboundAttributes?.size)
269
- throw new Error('Grammar failed to bind all attributes');
260
+ if (this.node.unboundAttributes?.size)
261
+ throw new Error('Grammar failed to bind all attributes');
262
+
263
+ if (!type) throw new Error(`CloseNodeTag must have type`);
264
+
265
+ if (type !== openType)
266
+ throw new Error(
267
+ `Grammar close {type: ${printType(type)}} did not match open {type: ${printType(
268
+ openType,
269
+ )}}`,
270
+ );
271
+
272
+ if (!flags.escape && !flags.trivia) {
273
+ add(this.parentNode, this.path.reference, this.node);
274
+ } else if (this.parentNode) {
275
+ this.parentNode.children = btree.push(
276
+ this.parentNode.children,
277
+ buildEmbeddedNode(this.node),
278
+ );
279
+ }
280
+
281
+ this.tagNodes.set(closeTag, this.node);
282
+ this.tagPaths.set(closeTag, this.path);
270
283
 
271
- if (!type) throw new Error(`CloseNodeTag must have type`);
284
+ finalizeNode(this.node);
272
285
 
273
- if (type !== openType)
274
- throw new Error(
275
- `Grammar close {type: ${printType(type)}} did not match open {type: ${printType(
276
- openType,
277
- )}}`,
278
- );
286
+ this.node = this.parentNode;
287
+ this.path = this.path.parent;
288
+ break;
289
+ }
279
290
 
280
- if (!flags.escape && !flags.trivia) {
281
- add(this.parentNode, this.path.reference, this.node);
282
- } else {
283
- this.parentNode.children = btree.push(
284
- this.parentNode.children,
285
- buildEmbeddedNode(this.node),
286
- );
287
- }
288
- }
291
+ case CloseFragmentTag: {
292
+ const closeTag = tag;
293
+
294
+ this.node.children = btree.push(this.node.children, tag);
289
295
 
290
296
  this.tagNodes.set(closeTag, this.node);
291
297
  this.tagPaths.set(closeTag, this.path);
@@ -300,7 +306,11 @@ export const State = class AgastState extends WeakStackFrame {
300
306
  case ReferenceTag: {
301
307
  this.node.children = btree.push(this.node.children, tag);
302
308
 
303
- const { isArray, name } = tag.value;
309
+ const { isArray, name, hasGap } = tag.value;
310
+
311
+ if (hasGap && !this.node.flags.hasGap) {
312
+ throw new Error('gap reference in gapless node');
313
+ }
304
314
 
305
315
  if (isArray && !hasOwn(this.node.properties, name)) {
306
316
  this.node.properties[name] = [];
@@ -324,14 +334,32 @@ export const State = class AgastState extends WeakStackFrame {
324
334
  target = this.held.node;
325
335
 
326
336
  this.held = null;
327
- } else if (this.expressions.size) {
328
- const expression = this.expressions.value;
329
- target = expression != null ? getRoot(expression) : buildStubNode(tag);
330
- this.expressions = this.expressions.pop();
331
337
  } else {
332
- target = buildStubNode(tag);
338
+ if (!this.node.flags.hasGap) throw new Error('Node must allow gaps');
339
+
340
+ if (this.expressions.size) {
341
+ const expression = this.expressions.value;
342
+
343
+ if (isArray(expression)) {
344
+ throw new Error('Invalid array interpolation');
345
+ } else {
346
+ target = expression != null ? getRoot(expression) : buildStubNode(tag);
347
+
348
+ this.expressions = this.expressions.pop();
349
+ }
350
+
351
+ // const range = ctx.buildRange(streamFromTree(target));
352
+
353
+ // this node is only interpolated into the tree, not the stream
354
+ // the stream still contains a gap token, even if expressions were specified
355
+ // get rid of the gap token in the stream!
356
+ } else {
357
+ target = buildStubNode(tag);
358
+ }
333
359
  }
334
360
 
361
+ this.tagNodes.set(tag, target);
362
+
335
363
  this.pathNodes.set(this.pathForTag(ref), target);
336
364
  add(this.node, ref, target);
337
365
 
@@ -380,11 +408,7 @@ export const State = class AgastState extends WeakStackFrame {
380
408
  break;
381
409
  }
382
410
 
383
- case LiteralTag: {
384
- this.node.children = btree.push(this.node.children, tag);
385
- break;
386
- }
387
-
411
+ case LiteralTag:
388
412
  case ArrayTag:
389
413
  this.node.children = btree.push(this.node.children, tag);
390
414
  break;
@@ -424,7 +448,7 @@ export const State = class AgastState extends WeakStackFrame {
424
448
  }
425
449
 
426
450
  get isGap() {
427
- return this.tag.type === 'NodeGapTag';
451
+ return this.tag.type === GapTag;
428
452
  }
429
453
 
430
454
  get speculative() {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bablr/agast-vm",
3
3
  "description": "A VM providing DOM-like guarantees about agAST trees",
4
- "version": "0.5.0",
4
+ "version": "0.6.0",
5
5
  "author": "Conrad Buck<conartist6@gmail.com>",
6
6
  "type": "module",
7
7
  "files": [
@@ -12,8 +12,8 @@
12
12
  },
13
13
  "sideEffects": false,
14
14
  "dependencies": {
15
- "@bablr/agast-helpers": "0.4.0",
16
- "@bablr/agast-vm-helpers": "0.4.0",
15
+ "@bablr/agast-helpers": "^0.5.0",
16
+ "@bablr/agast-vm-helpers": "^0.5.0",
17
17
  "@bablr/coroutine": "0.1.0",
18
18
  "@bablr/weak-stack": "0.1.0",
19
19
  "@iter-tools/imm-stack": "1.1.0"
package/lib/node.js DELETED
@@ -1,36 +0,0 @@
1
- import { getRange, getOpenTag, getCloseTag } from '@bablr/agast-helpers/tree';
2
- import { facades, actuals } from './facades.js';
3
-
4
- export const NodeFacade = class AgastNodeFacade {
5
- constructor(path) {
6
- facades.set(path, this);
7
- }
8
-
9
- get language() {
10
- return actuals.get(this).language;
11
- }
12
-
13
- get type() {
14
- return actuals.get(this).type;
15
- }
16
-
17
- get range() {
18
- return getRange(actuals.get(this));
19
- }
20
-
21
- get openTag() {
22
- return getOpenTag(actuals.get(this));
23
- }
24
-
25
- get closeTag() {
26
- return getCloseTag(actuals.get(this));
27
- }
28
-
29
- get flags() {
30
- return actuals.get(this).flags;
31
- }
32
-
33
- get attributes() {
34
- return actuals.get(this).attributes;
35
- }
36
- };