@bablr/agast-vm 0.8.0 → 0.9.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/README.md +14 -49
- package/lib/evaluate.js +116 -147
- package/lib/index.js +2 -1
- package/lib/node.js +176 -0
- package/lib/path.js +160 -1
- package/lib/state.js +256 -146
- package/package.json +3 -6
- package/lib/context.js +0 -88
- package/lib/utils/array.js +0 -7
- package/lib/utils/iterable.js +0 -13
- package/lib/utils/skip.js +0 -39
package/lib/state.js
CHANGED
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
import isArray from 'iter-tools-es/methods/is-array';
|
|
2
|
-
import { WeakStackFrame } from '@bablr/weak-stack';
|
|
3
1
|
import {
|
|
4
|
-
|
|
2
|
+
addProperty,
|
|
5
3
|
finalizeNode,
|
|
6
|
-
getRoot,
|
|
7
4
|
buildStubNode,
|
|
8
5
|
getRange,
|
|
9
|
-
buildNullTag,
|
|
10
|
-
treeFromStream,
|
|
11
6
|
createNode,
|
|
12
|
-
|
|
7
|
+
buildNullNode,
|
|
8
|
+
buildOpenNodeTag,
|
|
9
|
+
shiftProperty,
|
|
10
|
+
buildProperty,
|
|
11
|
+
add,
|
|
12
|
+
buildBindingTag,
|
|
13
|
+
buildAttributeDefinition,
|
|
13
14
|
} from '@bablr/agast-helpers/tree';
|
|
14
|
-
import * as sumtree from '@bablr/agast-helpers/
|
|
15
|
+
import * as sumtree from '@bablr/agast-helpers/children';
|
|
16
|
+
import * as btree from '@bablr/agast-helpers/btree';
|
|
15
17
|
import {
|
|
16
18
|
getInitializerChildrenIndex,
|
|
17
|
-
|
|
19
|
+
getOpenTag,
|
|
20
|
+
getOriginalFirstNode,
|
|
21
|
+
isGapNode,
|
|
22
|
+
isStubNode,
|
|
18
23
|
referencesAreEqual,
|
|
19
24
|
TagPath,
|
|
20
25
|
} from '@bablr/agast-helpers/path';
|
|
@@ -28,52 +33,85 @@ import {
|
|
|
28
33
|
NullTag,
|
|
29
34
|
InitializerTag,
|
|
30
35
|
LiteralTag,
|
|
36
|
+
AttributeDefinition,
|
|
37
|
+
BindingTag,
|
|
38
|
+
Property,
|
|
31
39
|
} from '@bablr/agast-helpers/symbols';
|
|
32
40
|
import { facades, actuals } from './facades.js';
|
|
33
|
-
import { Path } from './path.js';
|
|
34
|
-
import {
|
|
41
|
+
import { Path, PathFacade, TagPathFacade } from './path.js';
|
|
42
|
+
import { get, has, immSet, isObject } from '@bablr/agast-helpers/object';
|
|
43
|
+
import { NodeFacade } from './node.js';
|
|
35
44
|
|
|
45
|
+
const { isArray } = Array;
|
|
36
46
|
const { hasOwn } = Object;
|
|
37
47
|
|
|
48
|
+
const defineAttribute = (node, tag) => {
|
|
49
|
+
if (tag.type !== AttributeDefinition) throw new Error();
|
|
50
|
+
|
|
51
|
+
let { path, value } = tag.value;
|
|
52
|
+
let openTag = getOpenTag(node);
|
|
53
|
+
let { attributes } = node;
|
|
54
|
+
|
|
55
|
+
if (!has(attributes, path) && get(attributes, path) !== undefined)
|
|
56
|
+
throw new Error('Can only define undefined attributes');
|
|
57
|
+
|
|
58
|
+
if (value === undefined) throw new Error('cannot define attribute to undefined');
|
|
59
|
+
|
|
60
|
+
let { flags, type } = openTag.value;
|
|
61
|
+
attributes = immSet(attributes, path, value);
|
|
62
|
+
let newOpenTag = buildOpenNodeTag(flags, type, attributes);
|
|
63
|
+
|
|
64
|
+
node.attributes = attributes;
|
|
65
|
+
|
|
66
|
+
node.children = sumtree.replaceAt(0, node.children, newOpenTag);
|
|
67
|
+
node.children = sumtree.push(node.children, tag);
|
|
68
|
+
};
|
|
69
|
+
|
|
38
70
|
export const StateFacade = class AgastStateFacade {
|
|
39
71
|
constructor(state) {
|
|
40
72
|
facades.set(state, this);
|
|
41
73
|
}
|
|
42
74
|
|
|
43
|
-
static
|
|
44
|
-
return State.
|
|
75
|
+
static create() {
|
|
76
|
+
return State.create();
|
|
45
77
|
}
|
|
46
78
|
|
|
47
79
|
get resultPath() {
|
|
48
|
-
|
|
80
|
+
let { resultPath } = actuals.get(this);
|
|
81
|
+
return resultPath && new TagPathFacade(resultPath);
|
|
49
82
|
}
|
|
50
83
|
|
|
51
|
-
get
|
|
52
|
-
return actuals.get(this).
|
|
84
|
+
get referenceTag() {
|
|
85
|
+
return actuals.get(this).referenceTag;
|
|
53
86
|
}
|
|
54
87
|
|
|
55
|
-
get
|
|
56
|
-
|
|
88
|
+
get referenceTagPath() {
|
|
89
|
+
let { referenceTagPath } = actuals.get(this);
|
|
90
|
+
return referenceTagPath && new TagPathFacade(referenceTagPath);
|
|
57
91
|
}
|
|
58
92
|
|
|
59
|
-
get
|
|
60
|
-
|
|
93
|
+
get path() {
|
|
94
|
+
let { path } = actuals.get(this);
|
|
95
|
+
return path && new PathFacade(path);
|
|
61
96
|
}
|
|
62
97
|
|
|
63
|
-
get
|
|
64
|
-
|
|
98
|
+
get node() {
|
|
99
|
+
let { node } = actuals.get(this);
|
|
100
|
+
return node && new NodeFacade(node);
|
|
65
101
|
}
|
|
66
102
|
|
|
67
|
-
get
|
|
68
|
-
return actuals.get(this).
|
|
103
|
+
get done() {
|
|
104
|
+
return actuals.get(this).done;
|
|
69
105
|
}
|
|
70
106
|
|
|
71
|
-
get
|
|
72
|
-
|
|
107
|
+
get parentNode() {
|
|
108
|
+
let { parentNode } = actuals.get(this);
|
|
109
|
+
return parentNode && new NodeFacade(parentNode);
|
|
73
110
|
}
|
|
74
111
|
|
|
75
|
-
get
|
|
76
|
-
|
|
112
|
+
get held() {
|
|
113
|
+
let { held } = actuals.get(this);
|
|
114
|
+
return held && new NodeFacade(held.node);
|
|
77
115
|
}
|
|
78
116
|
|
|
79
117
|
get holding() {
|
|
@@ -84,10 +122,6 @@ export const StateFacade = class AgastStateFacade {
|
|
|
84
122
|
return actuals.get(this).depth;
|
|
85
123
|
}
|
|
86
124
|
|
|
87
|
-
get ctx() {
|
|
88
|
-
return this.context;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
125
|
get parent() {
|
|
92
126
|
return facades.get(actuals.get(this).parent);
|
|
93
127
|
}
|
|
@@ -95,33 +129,24 @@ export const StateFacade = class AgastStateFacade {
|
|
|
95
129
|
|
|
96
130
|
export const nodeStates = new WeakMap();
|
|
97
131
|
|
|
98
|
-
export const State = class AgastState
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
held = null,
|
|
105
|
-
resultPath = null,
|
|
106
|
-
) {
|
|
107
|
-
super(parent);
|
|
108
|
-
|
|
109
|
-
if (!context || !path || !expressions) throw new Error('invalid args to tagState');
|
|
110
|
-
|
|
111
|
-
this.context = context;
|
|
112
|
-
this.expressions = new Coroutine(expressions[Symbol.iterator]());
|
|
113
|
-
this.path = path;
|
|
132
|
+
export const State = class AgastState {
|
|
133
|
+
static create() {
|
|
134
|
+
return new State();
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
constructor(held = null, path = null, resultPath = null) {
|
|
114
138
|
this.held = held;
|
|
139
|
+
this.path = path;
|
|
115
140
|
this.resultPath = resultPath;
|
|
116
|
-
this.
|
|
117
|
-
|
|
118
|
-
|
|
141
|
+
this.done = false;
|
|
142
|
+
this.referenceTagPath = null;
|
|
143
|
+
this.doctype = null;
|
|
119
144
|
|
|
120
145
|
new StateFacade(this);
|
|
121
146
|
}
|
|
122
147
|
|
|
123
|
-
static from(
|
|
124
|
-
return State
|
|
148
|
+
static from(held) {
|
|
149
|
+
return new State(held);
|
|
125
150
|
}
|
|
126
151
|
|
|
127
152
|
get holding() {
|
|
@@ -129,33 +154,48 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
129
154
|
}
|
|
130
155
|
|
|
131
156
|
get atReference() {
|
|
132
|
-
return
|
|
157
|
+
return [ReferenceTag, ShiftTag].includes(this.resultPath?.tag.type);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
get atBinding() {
|
|
161
|
+
return this.resultPath?.tag.type === BindingTag;
|
|
133
162
|
}
|
|
134
163
|
|
|
135
164
|
advance(tag) {
|
|
136
165
|
if (!tag) throw new Error();
|
|
137
166
|
|
|
167
|
+
if (this.done) throw new Error();
|
|
168
|
+
|
|
138
169
|
let targetPath = this.path;
|
|
139
170
|
|
|
140
171
|
switch (tag.type) {
|
|
141
172
|
case DoctypeTag: {
|
|
142
|
-
let { path
|
|
173
|
+
let { path } = this;
|
|
143
174
|
|
|
144
|
-
if (this.resultPath) throw new Error('invalid location for doctype');
|
|
175
|
+
if (this.resultPath || this.docType) throw new Error('invalid location for doctype');
|
|
145
176
|
|
|
146
|
-
|
|
147
|
-
node.attributes = tag.value.attributes;
|
|
177
|
+
this.doctype = tag;
|
|
148
178
|
|
|
149
179
|
targetPath = path;
|
|
150
180
|
break;
|
|
151
181
|
}
|
|
152
182
|
|
|
153
183
|
case ReferenceTag: {
|
|
154
|
-
let {
|
|
184
|
+
let { type, isArray, name, flags } = tag.value;
|
|
155
185
|
|
|
156
186
|
if (this.atReference) throw new Error('invalid location for reference');
|
|
157
|
-
if (!name) throw new Error(
|
|
158
|
-
if (
|
|
187
|
+
if (!name && !type) throw new Error();
|
|
188
|
+
if (type && !['.', '#', '@'].includes(type)) throw new Error();
|
|
189
|
+
if (['#', '@'].includes(type) && isArray) throw new Error();
|
|
190
|
+
|
|
191
|
+
if (this.held && sumtree.getAt(2, this.node.children)?.type === InitializerTag) {
|
|
192
|
+
if (
|
|
193
|
+
!referencesAreEqual(sumtree.getAt(1, this.node.children), tag) ||
|
|
194
|
+
sumtree.getSize(this.node.children) > 3
|
|
195
|
+
) {
|
|
196
|
+
throw new Error();
|
|
197
|
+
}
|
|
198
|
+
}
|
|
159
199
|
|
|
160
200
|
if (this.node.properties[name] && this.node.properties[name]?.node === undefined) {
|
|
161
201
|
let existingReferenceIndex = getInitializerChildrenIndex(this.node, tag);
|
|
@@ -171,42 +211,102 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
171
211
|
throw new Error('gap reference in gapless node');
|
|
172
212
|
}
|
|
173
213
|
|
|
174
|
-
this.
|
|
214
|
+
this.referenceTagPath = TagPath.from(this.path, -1);
|
|
175
215
|
|
|
176
216
|
break;
|
|
177
217
|
}
|
|
178
218
|
|
|
219
|
+
case BindingTag: {
|
|
220
|
+
if (!this.atReference && this.parent) {
|
|
221
|
+
throw new Error('Invalid location for BindingTag');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
this.node.children = sumtree.push(this.node.children, tag);
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
|
|
179
228
|
case OpenNodeTag: {
|
|
180
229
|
let parentNode = this.node;
|
|
181
230
|
|
|
182
231
|
targetPath = this.path;
|
|
183
232
|
|
|
184
|
-
if (!this.
|
|
233
|
+
if (!this.atBinding && this.parent) {
|
|
185
234
|
throw new Error('Invalid location for OpenNodeTag');
|
|
186
235
|
}
|
|
187
236
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
237
|
+
let bindingTag = parentNode && sumtree.getAt(-1, parentNode.children);
|
|
238
|
+
|
|
239
|
+
if (bindingTag && !bindingTag.value.languagePath) throw new Error();
|
|
240
|
+
|
|
241
|
+
let node = createNode(tag);
|
|
242
|
+
|
|
243
|
+
if (!this.path) {
|
|
244
|
+
this.path = Path.create(node);
|
|
245
|
+
|
|
246
|
+
if (!tag.value.type && this.doctype) {
|
|
247
|
+
node.children = sumtree.push(node.children, this.doctype);
|
|
248
|
+
}
|
|
194
249
|
} else {
|
|
195
|
-
let node = createNode(tag);
|
|
196
250
|
this.path = this.path.push(node, sumtree.getSize(parentNode.children) - 2);
|
|
197
|
-
add(parentNode, this.
|
|
251
|
+
add(parentNode, this.referenceTag, node, bindingTag);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
node.children = sumtree.push(node.children, tag);
|
|
255
|
+
|
|
256
|
+
let undefinedAttributes = 0;
|
|
257
|
+
|
|
258
|
+
let queue = [this.node.attributes];
|
|
259
|
+
while (queue.length) {
|
|
260
|
+
for (let value of Object.values(queue[queue.length - 1])) {
|
|
261
|
+
if (value === undefined) {
|
|
262
|
+
undefinedAttributes++;
|
|
263
|
+
} else if (isArray(value) || isObject(value)) {
|
|
264
|
+
queue.push(value);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
queue.pop();
|
|
198
268
|
}
|
|
199
269
|
|
|
270
|
+
nodeStates.set(this.node, { undefinedAttributes });
|
|
271
|
+
|
|
200
272
|
break;
|
|
201
273
|
}
|
|
202
274
|
|
|
203
275
|
case CloseNodeTag: {
|
|
204
276
|
if (this.atReference) throw new Error('invalid location for close tag');
|
|
205
277
|
|
|
278
|
+
if (this.node.type) {
|
|
279
|
+
for (const value of Object.values(this.node.properties)) {
|
|
280
|
+
if (!Array.isArray(value) && value.node === undefined) {
|
|
281
|
+
addProperty(
|
|
282
|
+
this.node,
|
|
283
|
+
buildProperty(value.reference, buildBindingTag().value, buildNullNode()),
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
let path = [];
|
|
290
|
+
let queue = [this.node.attributes];
|
|
291
|
+
while (queue.length) {
|
|
292
|
+
for (const { 0: key, 1: value } of Object.entries(queue[queue.length - 1])) {
|
|
293
|
+
if (value === undefined) {
|
|
294
|
+
path.push(key);
|
|
295
|
+
defineAttribute(this.node, buildAttributeDefinition(path, null));
|
|
296
|
+
path.pop(key);
|
|
297
|
+
} else if (isArray(value) || isObject(value)) {
|
|
298
|
+
queue.push(value);
|
|
299
|
+
path.push(key);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
queue.pop();
|
|
303
|
+
}
|
|
304
|
+
|
|
206
305
|
this.node.children = sumtree.push(this.node.children, tag);
|
|
207
306
|
|
|
208
|
-
if (this.
|
|
209
|
-
|
|
307
|
+
if (!this.path) {
|
|
308
|
+
this.done = true;
|
|
309
|
+
}
|
|
210
310
|
|
|
211
311
|
finalizeNode(this.node);
|
|
212
312
|
|
|
@@ -214,46 +314,43 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
214
314
|
break;
|
|
215
315
|
}
|
|
216
316
|
|
|
217
|
-
case
|
|
317
|
+
case Property: {
|
|
218
318
|
let target;
|
|
319
|
+
let property = tag.value;
|
|
320
|
+
let { node } = this.path;
|
|
219
321
|
let lastTagPath = TagPath.from(this.path, -1);
|
|
220
322
|
|
|
221
|
-
if (!this.
|
|
222
|
-
throw new Error('Invalid location for
|
|
323
|
+
if (!this.atBinding && !isStubNode(property.node)) {
|
|
324
|
+
throw new Error('Invalid location for Property');
|
|
223
325
|
}
|
|
224
326
|
|
|
225
|
-
if (this.held
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
this.held = null;
|
|
229
|
-
} else {
|
|
230
|
-
this.expressions.advance();
|
|
231
|
-
|
|
232
|
-
if (!this.expressions.done) {
|
|
233
|
-
const expression = this.expressions.value;
|
|
234
|
-
|
|
235
|
-
if (isArray(expression)) {
|
|
236
|
-
throw new Error('Invalid array interpolation');
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
if (expression == null) {
|
|
240
|
-
target = treeFromStream(buildNullTag());
|
|
241
|
-
} else {
|
|
242
|
-
target = getRoot(expression);
|
|
243
|
-
}
|
|
327
|
+
if (this.held) {
|
|
328
|
+
if (isGapNode(property.node)) {
|
|
329
|
+
property = buildProperty(property.reference, this.held.binding, this.held.node);
|
|
244
330
|
} else {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
331
|
+
let { height } = lastTagPath.previousSibling.tag.value;
|
|
332
|
+
let matchesHeld =
|
|
333
|
+
isGapNode(property.node) ||
|
|
334
|
+
this.held.node === property.node ||
|
|
335
|
+
this.held.node === getOriginalFirstNode(property.node, (height ?? 1) - 1);
|
|
336
|
+
if (!matchesHeld) {
|
|
337
|
+
throw new Error();
|
|
338
|
+
}
|
|
248
339
|
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (this.held && lastTagPath.previousSibling.tag.type !== ShiftTag) {
|
|
343
|
+
target = this.held.node;
|
|
344
|
+
} else {
|
|
345
|
+
// if (!this.node.flags.hasGap) throw new Error('Node must allow gaps');
|
|
249
346
|
|
|
250
|
-
|
|
347
|
+
target = buildStubNode(tag);
|
|
251
348
|
}
|
|
252
349
|
|
|
253
350
|
const range = getRange(target);
|
|
254
351
|
|
|
255
352
|
this.resultPath = range ? range[1] : this.resultPath;
|
|
256
|
-
this.
|
|
353
|
+
this.referenceTagPath = null;
|
|
257
354
|
|
|
258
355
|
let lastRefPath = [ShiftTag, ReferenceTag].includes(lastTagPath.tag.type)
|
|
259
356
|
? lastTagPath
|
|
@@ -261,80 +358,88 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
261
358
|
|
|
262
359
|
if (![ShiftTag, ReferenceTag].includes(lastRefPath.tag.type)) throw new Error();
|
|
263
360
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
refTag = sumtree.getAt(refIndex, this.node.children);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (refTag) {
|
|
272
|
-
add(
|
|
273
|
-
this.path.node,
|
|
274
|
-
refTag,
|
|
275
|
-
target,
|
|
276
|
-
lastRefPath.tag.type === ShiftTag
|
|
277
|
-
? lastRefPath.tag.value.index
|
|
278
|
-
: refTag.value.flags.expression
|
|
279
|
-
? 0
|
|
280
|
-
: null,
|
|
281
|
-
);
|
|
361
|
+
if (lastRefPath.tag.type === ShiftTag) {
|
|
362
|
+
shiftProperty(node, property);
|
|
363
|
+
} else {
|
|
364
|
+
addProperty(node, property);
|
|
282
365
|
}
|
|
283
366
|
|
|
367
|
+
this.held = null;
|
|
284
368
|
break;
|
|
285
369
|
}
|
|
286
370
|
|
|
371
|
+
case GapTag:
|
|
287
372
|
case NullTag: {
|
|
288
|
-
const { node: parentNode,
|
|
373
|
+
const { node: parentNode, referenceTag } = this;
|
|
374
|
+
|
|
375
|
+
if (this.held) throw new Error();
|
|
289
376
|
|
|
290
377
|
if (!this.atReference && this.parent) {
|
|
291
|
-
throw new Error('Invalid location for
|
|
378
|
+
throw new Error('Invalid location for NullTag');
|
|
292
379
|
}
|
|
293
380
|
|
|
294
381
|
let stubNode = buildStubNode(tag);
|
|
295
382
|
|
|
296
|
-
add(parentNode,
|
|
383
|
+
add(parentNode, referenceTag, stubNode);
|
|
384
|
+
|
|
385
|
+
targetPath = this.path.push(stubNode, sumtree.getSize(this.node.children) - 3);
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
case AttributeDefinition: {
|
|
390
|
+
if (this.held) throw new Error('invalid place for an atrribute binding');
|
|
391
|
+
|
|
392
|
+
let nodeState = nodeStates.get(this.node);
|
|
393
|
+
|
|
394
|
+
nodeState.undefinedAttributes--;
|
|
395
|
+
|
|
396
|
+
// add undefined attributes from value
|
|
397
|
+
|
|
398
|
+
defineAttribute(this.node, tag);
|
|
297
399
|
|
|
298
|
-
targetPath = this.path.push(stubNode, sumtree.getSize(this.node.children) - 2);
|
|
299
400
|
break;
|
|
300
401
|
}
|
|
301
402
|
|
|
302
403
|
case InitializerTag: {
|
|
303
404
|
const { isArray } = tag.value;
|
|
304
|
-
const { node,
|
|
305
|
-
const { name } =
|
|
405
|
+
const { node, referenceTag } = this;
|
|
406
|
+
const { name } = referenceTag.value;
|
|
306
407
|
|
|
307
408
|
if (!this.atReference && this.parent) {
|
|
308
|
-
throw new Error('Invalid location for
|
|
409
|
+
throw new Error('Invalid location for InitializerTag');
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (this.held && sumtree.getAt(2, node.children)?.type === InitializerTag) {
|
|
413
|
+
throw new Error();
|
|
309
414
|
}
|
|
310
415
|
|
|
311
|
-
if (
|
|
416
|
+
if (
|
|
417
|
+
hasOwn(node.properties, name) &&
|
|
418
|
+
(isArray
|
|
419
|
+
? btree.getSize(node.properties[name])
|
|
420
|
+
: node.properties[name]?.node !== undefined)
|
|
421
|
+
) {
|
|
312
422
|
throw new Error();
|
|
313
423
|
}
|
|
314
424
|
|
|
315
|
-
add(node,
|
|
425
|
+
add(node, referenceTag, isArray ? [] : undefined);
|
|
316
426
|
break;
|
|
317
427
|
}
|
|
318
428
|
|
|
319
429
|
case ShiftTag: {
|
|
320
|
-
if (this.
|
|
321
|
-
|
|
322
|
-
const finishedPath = this.resultPath.innerPath;
|
|
323
|
-
const finishedNode = finishedPath.node;
|
|
324
|
-
const ref = TagPath.from(
|
|
325
|
-
this.resultPath,
|
|
326
|
-
this.resultPath.childrenIndex - tag.value.index * 2 + 1,
|
|
327
|
-
).tag;
|
|
430
|
+
if (this.resultPath.tag.type !== Property) throw new Error('invalid location for shift');
|
|
328
431
|
|
|
329
|
-
|
|
432
|
+
let heldProperty = this.resultPath.tag.value;
|
|
330
433
|
|
|
331
|
-
if (!
|
|
434
|
+
if (!this.held) {
|
|
435
|
+
this.held = heldProperty;
|
|
436
|
+
}
|
|
332
437
|
|
|
333
|
-
if (!
|
|
438
|
+
if (!heldProperty.reference.flags.expression) throw new Error();
|
|
334
439
|
|
|
335
440
|
this.node.children = sumtree.push(this.node.children, tag);
|
|
336
441
|
|
|
337
|
-
this.
|
|
442
|
+
this.referenceTagPath = TagPath.from(this.path, -1);
|
|
338
443
|
|
|
339
444
|
// this.path = finishedPath;
|
|
340
445
|
targetPath = this.path;
|
|
@@ -351,15 +456,11 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
351
456
|
throw new Error();
|
|
352
457
|
}
|
|
353
458
|
|
|
354
|
-
this.resultPath = TagPath.from(targetPath, -1);
|
|
459
|
+
this.resultPath = targetPath && TagPath.from(targetPath, -1);
|
|
355
460
|
|
|
356
461
|
return tag;
|
|
357
462
|
}
|
|
358
463
|
|
|
359
|
-
get ctx() {
|
|
360
|
-
return this.context;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
464
|
get isGap() {
|
|
364
465
|
return this.tag.type === GapTag;
|
|
365
466
|
}
|
|
@@ -372,8 +473,17 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
372
473
|
return this.path.parent.node;
|
|
373
474
|
}
|
|
374
475
|
|
|
375
|
-
get
|
|
376
|
-
return this.
|
|
476
|
+
get referenceTag() {
|
|
477
|
+
return this.referenceTagPath?.tag;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
get bindingPath() {
|
|
481
|
+
let path = this.referenceTagPath.nextSibling;
|
|
482
|
+
return path.tag.type === BindingTag ? path : null;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
get binding() {
|
|
486
|
+
return this.bindingPath?.tag;
|
|
377
487
|
}
|
|
378
488
|
|
|
379
489
|
get node() {
|
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.9.0",
|
|
5
5
|
"author": "Conrad Buck<conartist6@gmail.com>",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -12,11 +12,8 @@
|
|
|
12
12
|
},
|
|
13
13
|
"sideEffects": false,
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@bablr/agast-helpers": "0.
|
|
16
|
-
"@bablr/agast-vm-helpers": "0.
|
|
17
|
-
"@bablr/coroutine": "0.1.0",
|
|
18
|
-
"@bablr/weak-stack": "1.0.0",
|
|
19
|
-
"@iter-tools/imm-stack": "1.1.0"
|
|
15
|
+
"@bablr/agast-helpers": "0.8.0",
|
|
16
|
+
"@bablr/agast-vm-helpers": "0.8.0"
|
|
20
17
|
},
|
|
21
18
|
"devDependencies": {
|
|
22
19
|
"@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#49f5952efed27f94ee9b94340eb1563c440bf64e",
|