@bablr/agast-vm 0.7.0 → 0.8.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/evaluate.js +33 -6
- package/lib/state.js +107 -65
- package/package.json +4 -4
package/lib/evaluate.js
CHANGED
|
@@ -8,11 +8,11 @@ import {
|
|
|
8
8
|
buildDoctypeTag,
|
|
9
9
|
buildOpenNodeTag,
|
|
10
10
|
buildCloseNodeTag,
|
|
11
|
+
buildInitializerTag,
|
|
11
12
|
} from '@bablr/agast-vm-helpers/internal-builders';
|
|
12
13
|
import { getEmbeddedTag } from '@bablr/agast-vm-helpers/deembed';
|
|
13
14
|
import { StreamIterable, getStreamIterator } from '@bablr/agast-helpers/stream';
|
|
14
15
|
import { printExpression } from '@bablr/agast-helpers/print';
|
|
15
|
-
import { buildArrayInitializerTag } from '@bablr/agast-helpers/tree';
|
|
16
16
|
import {
|
|
17
17
|
DoctypeTag,
|
|
18
18
|
OpenNodeTag,
|
|
@@ -21,12 +21,14 @@ import {
|
|
|
21
21
|
ShiftTag,
|
|
22
22
|
GapTag,
|
|
23
23
|
NullTag,
|
|
24
|
-
|
|
24
|
+
InitializerTag,
|
|
25
25
|
LiteralTag,
|
|
26
26
|
} from '@bablr/agast-helpers/symbols';
|
|
27
27
|
import { State } from './state.js';
|
|
28
28
|
import { Context } from './context.js';
|
|
29
29
|
import { facades } from './facades.js';
|
|
30
|
+
import { add, TagPath } from '@bablr/agast-helpers/path';
|
|
31
|
+
import { buildNullNode, defineAttribute } from '@bablr/agast-helpers/tree';
|
|
30
32
|
|
|
31
33
|
export const agast = (strategy, options = {}) => {
|
|
32
34
|
const ctx = Context.create();
|
|
@@ -112,13 +114,13 @@ function* __agast(ctx, s, instructions) {
|
|
|
112
114
|
break;
|
|
113
115
|
}
|
|
114
116
|
|
|
115
|
-
case
|
|
117
|
+
case InitializerTag: {
|
|
118
|
+
const { isArray } = tag.value;
|
|
116
119
|
const { reference } = s;
|
|
117
120
|
|
|
118
121
|
if (reference?.type !== ReferenceTag) throw new Error();
|
|
119
|
-
if (!reference.value.isArray) throw new Error();
|
|
120
122
|
|
|
121
|
-
returnValue = s.advance(
|
|
123
|
+
returnValue = s.advance(buildInitializerTag(isArray));
|
|
122
124
|
break;
|
|
123
125
|
}
|
|
124
126
|
|
|
@@ -126,7 +128,18 @@ function* __agast(ctx, s, instructions) {
|
|
|
126
128
|
const { index } = tag.value;
|
|
127
129
|
if (s.resultPath.tag.type !== GapTag) throw new Error();
|
|
128
130
|
|
|
129
|
-
|
|
131
|
+
let prevRef = s.resultPath.previousSibling;
|
|
132
|
+
|
|
133
|
+
if (prevRef.tag.type === ShiftTag) {
|
|
134
|
+
if (prevRef.tag.value.index + 1 !== index) throw new Error('bad shift index');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const refPath = TagPath.from(
|
|
138
|
+
s.resultPath.path,
|
|
139
|
+
s.resultPath.childrenIndex - (index - 1) * 2 - 1,
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
if (refPath.tag.type !== ReferenceTag) throw new Error();
|
|
130
143
|
|
|
131
144
|
if (!refPath.tag.value.flags.expression) {
|
|
132
145
|
throw new Error();
|
|
@@ -148,6 +161,20 @@ function* __agast(ctx, s, instructions) {
|
|
|
148
161
|
}
|
|
149
162
|
|
|
150
163
|
case CloseNodeTag: {
|
|
164
|
+
for (const { 0: key, 1: value } of Object.entries(s.node.properties)) {
|
|
165
|
+
if (!Array.isArray(value)) {
|
|
166
|
+
if (value.node === undefined) {
|
|
167
|
+
add(s.node, value.reference, buildNullNode());
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
for (const { 0: key, 1: value } of Object.entries(s.node.attributes)) {
|
|
173
|
+
if (value === undefined) {
|
|
174
|
+
defineAttribute(s.node, key, null);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
151
178
|
returnValue = s.advance(buildCloseNodeTag());
|
|
152
179
|
break;
|
|
153
180
|
}
|
package/lib/state.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import isArray from 'iter-tools-es/methods/is-array';
|
|
2
2
|
import { WeakStackFrame } from '@bablr/weak-stack';
|
|
3
3
|
import {
|
|
4
|
-
Resolver,
|
|
5
4
|
add,
|
|
6
5
|
finalizeNode,
|
|
7
6
|
getRoot,
|
|
@@ -10,9 +9,15 @@ import {
|
|
|
10
9
|
buildNullTag,
|
|
11
10
|
treeFromStream,
|
|
12
11
|
createNode,
|
|
12
|
+
buildReferenceTag,
|
|
13
13
|
} from '@bablr/agast-helpers/tree';
|
|
14
|
-
import * as
|
|
15
|
-
import {
|
|
14
|
+
import * as sumtree from '@bablr/agast-helpers/sumtree';
|
|
15
|
+
import {
|
|
16
|
+
getInitializerChildrenIndex,
|
|
17
|
+
getPropertyChildrenIndex,
|
|
18
|
+
referencesAreEqual,
|
|
19
|
+
TagPath,
|
|
20
|
+
} from '@bablr/agast-helpers/path';
|
|
16
21
|
import {
|
|
17
22
|
DoctypeTag,
|
|
18
23
|
OpenNodeTag,
|
|
@@ -21,13 +26,15 @@ import {
|
|
|
21
26
|
ShiftTag,
|
|
22
27
|
GapTag,
|
|
23
28
|
NullTag,
|
|
24
|
-
|
|
29
|
+
InitializerTag,
|
|
25
30
|
LiteralTag,
|
|
26
31
|
} from '@bablr/agast-helpers/symbols';
|
|
27
32
|
import { facades, actuals } from './facades.js';
|
|
28
33
|
import { Path } from './path.js';
|
|
29
34
|
import { Coroutine } from '@bablr/coroutine';
|
|
30
35
|
|
|
36
|
+
const { hasOwn } = Object;
|
|
37
|
+
|
|
31
38
|
export const StateFacade = class AgastStateFacade {
|
|
32
39
|
constructor(state) {
|
|
33
40
|
facades.set(state, this);
|
|
@@ -49,6 +56,10 @@ export const StateFacade = class AgastStateFacade {
|
|
|
49
56
|
return actuals.get(this).referencePath;
|
|
50
57
|
}
|
|
51
58
|
|
|
59
|
+
get unshiftedReferencePath() {
|
|
60
|
+
return actuals.get(this).unshiftedReferencePath;
|
|
61
|
+
}
|
|
62
|
+
|
|
52
63
|
get context() {
|
|
53
64
|
return facades.get(actuals.get(this).context);
|
|
54
65
|
}
|
|
@@ -92,7 +103,6 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
92
103
|
path = Path.from(createNode()),
|
|
93
104
|
held = null,
|
|
94
105
|
resultPath = null,
|
|
95
|
-
resolver = new Resolver(),
|
|
96
106
|
) {
|
|
97
107
|
super(parent);
|
|
98
108
|
|
|
@@ -103,7 +113,7 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
103
113
|
this.path = path;
|
|
104
114
|
this.held = held;
|
|
105
115
|
this.resultPath = resultPath;
|
|
106
|
-
this.
|
|
116
|
+
this.referencePath = null;
|
|
107
117
|
|
|
108
118
|
if (!this.node) throw new Error();
|
|
109
119
|
|
|
@@ -118,71 +128,84 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
118
128
|
return !!this.held;
|
|
119
129
|
}
|
|
120
130
|
|
|
121
|
-
|
|
122
|
-
|
|
131
|
+
get atReference() {
|
|
132
|
+
return !![ReferenceTag, ShiftTag].includes(this.resultPath?.tag.type);
|
|
133
|
+
}
|
|
123
134
|
|
|
135
|
+
advance(tag) {
|
|
124
136
|
if (!tag) throw new Error();
|
|
125
137
|
|
|
126
138
|
let targetPath = this.path;
|
|
127
139
|
|
|
128
140
|
switch (tag.type) {
|
|
129
141
|
case DoctypeTag: {
|
|
130
|
-
|
|
142
|
+
let { path, node } = this;
|
|
131
143
|
|
|
132
|
-
|
|
133
|
-
node.attributes = tag.value.attributes;
|
|
144
|
+
if (this.resultPath) throw new Error('invalid location for doctype');
|
|
134
145
|
|
|
135
|
-
|
|
136
|
-
|
|
146
|
+
node.children = sumtree.push(node.children, tag);
|
|
147
|
+
node.attributes = tag.value.attributes;
|
|
137
148
|
|
|
138
149
|
targetPath = path;
|
|
139
150
|
break;
|
|
140
151
|
}
|
|
141
152
|
|
|
142
153
|
case ReferenceTag: {
|
|
143
|
-
|
|
154
|
+
let { name, isArray, flags } = tag.value;
|
|
155
|
+
|
|
156
|
+
if (this.atReference) throw new Error('invalid location for reference');
|
|
157
|
+
if (!name) throw new Error('missing reference name');
|
|
158
|
+
if (!/#|@|[.]|[a-zA-Z]+/.test(name)) throw new Error('invalid reference name');
|
|
144
159
|
|
|
145
|
-
this.node.
|
|
146
|
-
|
|
160
|
+
if (this.node.properties[name] && this.node.properties[name]?.node === undefined) {
|
|
161
|
+
let existingReferenceIndex = getInitializerChildrenIndex(this.node, tag);
|
|
162
|
+
let existingReference = sumtree.getAt(existingReferenceIndex, this.node.children);
|
|
163
|
+
if (!referencesAreEqual(tag, existingReference)) {
|
|
164
|
+
throw new Error("reference didn't match initializer");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
147
167
|
|
|
148
|
-
|
|
168
|
+
this.node.children = sumtree.push(this.node.children, tag);
|
|
149
169
|
|
|
150
|
-
if (hasGap && !this.node.flags.hasGap) {
|
|
170
|
+
if (flags.hasGap && !this.node.flags.hasGap) {
|
|
151
171
|
throw new Error('gap reference in gapless node');
|
|
152
172
|
}
|
|
153
173
|
|
|
174
|
+
this.referencePath = TagPath.from(this.path, -1);
|
|
175
|
+
|
|
154
176
|
break;
|
|
155
177
|
}
|
|
156
178
|
|
|
157
179
|
case OpenNodeTag: {
|
|
158
|
-
|
|
180
|
+
let parentNode = this.node;
|
|
159
181
|
|
|
160
182
|
targetPath = this.path;
|
|
161
183
|
|
|
184
|
+
if (!this.atReference && this.parent) {
|
|
185
|
+
throw new Error('Invalid location for OpenNodeTag');
|
|
186
|
+
}
|
|
187
|
+
|
|
162
188
|
if (!this.path.depth) {
|
|
163
189
|
this.node.flags = tag.value.flags;
|
|
164
|
-
this.node.children =
|
|
190
|
+
this.node.children = sumtree.push(this.node.children, tag);
|
|
165
191
|
this.node.type = tag.value.type;
|
|
166
192
|
this.node.language = tag.value.language;
|
|
167
193
|
this.node.attributes = tag.value.attributes || {};
|
|
168
194
|
} else {
|
|
169
195
|
let node = createNode(tag);
|
|
170
|
-
this.path = this.path.push(node,
|
|
196
|
+
this.path = this.path.push(node, sumtree.getSize(parentNode.children) - 2);
|
|
171
197
|
add(parentNode, this.reference, node);
|
|
172
198
|
}
|
|
173
199
|
|
|
174
|
-
this.resolver.advance(tag);
|
|
175
|
-
updatePath(this.path, tag);
|
|
176
|
-
|
|
177
200
|
break;
|
|
178
201
|
}
|
|
179
202
|
|
|
180
203
|
case CloseNodeTag: {
|
|
181
|
-
this.
|
|
182
|
-
|
|
183
|
-
|
|
204
|
+
if (this.atReference) throw new Error('invalid location for close tag');
|
|
205
|
+
|
|
206
|
+
this.node.children = sumtree.push(this.node.children, tag);
|
|
184
207
|
|
|
185
|
-
if (this.node.
|
|
208
|
+
if (this.node.undefinedAttributes?.size)
|
|
186
209
|
throw new Error('Grammar failed to bind all attributes');
|
|
187
210
|
|
|
188
211
|
finalizeNode(this.node);
|
|
@@ -194,21 +217,16 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
194
217
|
case GapTag: {
|
|
195
218
|
let target;
|
|
196
219
|
let lastTagPath = TagPath.from(this.path, -1);
|
|
197
|
-
let refPath = lastTagPath;
|
|
198
220
|
|
|
199
|
-
|
|
200
|
-
|
|
221
|
+
if (!this.atReference) {
|
|
222
|
+
throw new Error('Invalid location for GapTag');
|
|
201
223
|
}
|
|
202
224
|
|
|
203
|
-
this.resolver.advance(tag);
|
|
204
|
-
|
|
205
|
-
let wasHeld = this.held;
|
|
206
|
-
|
|
207
225
|
if (this.held && lastTagPath.tag.type !== ShiftTag) {
|
|
208
226
|
target = this.held.node;
|
|
209
227
|
|
|
210
228
|
this.held = null;
|
|
211
|
-
} else
|
|
229
|
+
} else {
|
|
212
230
|
this.expressions.advance();
|
|
213
231
|
|
|
214
232
|
if (!this.expressions.done) {
|
|
@@ -230,57 +248,83 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
230
248
|
}
|
|
231
249
|
|
|
232
250
|
this.held = null;
|
|
233
|
-
} else {
|
|
234
|
-
this.node.language = null;
|
|
235
|
-
this.node.type = null;
|
|
236
|
-
target = this.node;
|
|
237
251
|
}
|
|
238
252
|
|
|
239
253
|
const range = getRange(target);
|
|
240
254
|
|
|
241
255
|
this.resultPath = range ? range[1] : this.resultPath;
|
|
256
|
+
this.referencePath = null;
|
|
257
|
+
|
|
258
|
+
let lastRefPath = [ShiftTag, ReferenceTag].includes(lastTagPath.tag.type)
|
|
259
|
+
? lastTagPath
|
|
260
|
+
: lastTagPath.previousSibling;
|
|
261
|
+
|
|
262
|
+
if (![ShiftTag, ReferenceTag].includes(lastRefPath.tag.type)) throw new Error();
|
|
263
|
+
|
|
264
|
+
let refTag = lastRefPath.tag;
|
|
265
|
+
|
|
266
|
+
if (refTag.type === ShiftTag) {
|
|
267
|
+
let refIndex = lastRefPath.childrenIndex - lastRefPath.tag.value.index * 2;
|
|
268
|
+
refTag = sumtree.getAt(refIndex, this.node.children);
|
|
269
|
+
}
|
|
242
270
|
|
|
243
|
-
if (
|
|
244
|
-
add(
|
|
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
|
+
);
|
|
245
282
|
}
|
|
246
283
|
|
|
247
|
-
updatePath(this.path, tag);
|
|
248
284
|
break;
|
|
249
285
|
}
|
|
250
286
|
|
|
251
287
|
case NullTag: {
|
|
252
288
|
const { node: parentNode, reference } = this;
|
|
253
|
-
const { name } = reference.value;
|
|
254
|
-
|
|
255
|
-
this.resolver.advance(tag);
|
|
256
|
-
updatePath(this.path, tag);
|
|
257
289
|
|
|
258
|
-
if (!
|
|
290
|
+
if (!this.atReference && this.parent) {
|
|
291
|
+
throw new Error('Invalid location for OpenNodeTag');
|
|
292
|
+
}
|
|
259
293
|
|
|
260
294
|
let stubNode = buildStubNode(tag);
|
|
261
295
|
|
|
262
296
|
add(parentNode, reference, stubNode);
|
|
263
297
|
|
|
264
|
-
targetPath = this.path.push(stubNode,
|
|
298
|
+
targetPath = this.path.push(stubNode, sumtree.getSize(this.node.children) - 2);
|
|
265
299
|
break;
|
|
266
300
|
}
|
|
267
301
|
|
|
268
|
-
case
|
|
302
|
+
case InitializerTag: {
|
|
303
|
+
const { isArray } = tag.value;
|
|
269
304
|
const { node, reference } = this;
|
|
270
|
-
|
|
305
|
+
const { name } = reference.value;
|
|
271
306
|
|
|
272
|
-
|
|
273
|
-
|
|
307
|
+
if (!this.atReference && this.parent) {
|
|
308
|
+
throw new Error('Invalid location for OpenNodeTag');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (node.properties[name] && node.properties[name]?.node === undefined) {
|
|
312
|
+
throw new Error();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
add(node, reference, isArray ? [] : undefined);
|
|
274
316
|
break;
|
|
275
317
|
}
|
|
276
318
|
|
|
277
319
|
case ShiftTag: {
|
|
278
|
-
this.
|
|
279
|
-
updatePath(this.path, tag);
|
|
320
|
+
if (this.atReference) throw new Error('invalid location for shift');
|
|
280
321
|
|
|
281
322
|
const finishedPath = this.resultPath.innerPath;
|
|
282
323
|
const finishedNode = finishedPath.node;
|
|
283
|
-
const ref =
|
|
324
|
+
const ref = TagPath.from(
|
|
325
|
+
this.resultPath,
|
|
326
|
+
this.resultPath.childrenIndex - tag.value.index * 2 + 1,
|
|
327
|
+
).tag;
|
|
284
328
|
|
|
285
329
|
this.held = { node: finishedNode, path: finishedPath };
|
|
286
330
|
|
|
@@ -288,7 +332,9 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
288
332
|
|
|
289
333
|
if (!ref.value.flags.expression) throw new Error();
|
|
290
334
|
|
|
291
|
-
this.node.children =
|
|
335
|
+
this.node.children = sumtree.push(this.node.children, tag);
|
|
336
|
+
|
|
337
|
+
this.referencePath = TagPath.from(this.path, -1);
|
|
292
338
|
|
|
293
339
|
// this.path = finishedPath;
|
|
294
340
|
targetPath = this.path;
|
|
@@ -296,9 +342,9 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
296
342
|
}
|
|
297
343
|
|
|
298
344
|
case LiteralTag:
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
this.node.children =
|
|
345
|
+
if (typeof tag.value !== 'string') throw new Error();
|
|
346
|
+
|
|
347
|
+
this.node.children = sumtree.push(this.node.children, tag);
|
|
302
348
|
break;
|
|
303
349
|
|
|
304
350
|
default:
|
|
@@ -327,11 +373,7 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
327
373
|
}
|
|
328
374
|
|
|
329
375
|
get reference() {
|
|
330
|
-
return this.
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
get referencePath() {
|
|
334
|
-
return this.reference && this.path.referencePath;
|
|
376
|
+
return this.referencePath.tag;
|
|
335
377
|
}
|
|
336
378
|
|
|
337
379
|
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.8.0",
|
|
5
5
|
"author": "Conrad Buck<conartist6@gmail.com>",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -12,10 +12,10 @@
|
|
|
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.7.0",
|
|
16
|
+
"@bablr/agast-vm-helpers": "0.7.0",
|
|
17
17
|
"@bablr/coroutine": "0.1.0",
|
|
18
|
-
"@bablr/weak-stack": "
|
|
18
|
+
"@bablr/weak-stack": "1.0.0",
|
|
19
19
|
"@iter-tools/imm-stack": "1.1.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|