@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 +2 -4
- package/lib/evaluate.js +24 -8
- package/lib/path.js +1 -1
- package/lib/state.js +101 -77
- package/package.json +3 -3
- package/lib/node.js +0 -36
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(
|
|
104
|
-
return
|
|
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 {
|
|
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],
|
|
296
|
+
yield buildWriteEffect(args[0], args[1].value);
|
|
281
297
|
}
|
|
282
298
|
break;
|
|
283
299
|
}
|
package/lib/path.js
CHANGED
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
|
|
39
|
-
|
|
40
|
-
const createNodeWithState = (startTag, options = {}) => {
|
|
40
|
+
const createNodeWithState = (openTag, options = {}) => {
|
|
41
41
|
const { unboundAttributes } = options;
|
|
42
|
-
const node = createNode(
|
|
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 {
|
|
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 (!
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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 (
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
284
|
+
finalizeNode(this.node);
|
|
272
285
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
)}}`,
|
|
278
|
-
);
|
|
286
|
+
this.node = this.parentNode;
|
|
287
|
+
this.path = this.path.parent;
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
279
290
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
-
|
|
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 ===
|
|
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.
|
|
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.
|
|
16
|
-
"@bablr/agast-vm-helpers": "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
|
-
};
|