@bablr/agast-vm 0.1.3 → 0.3.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 +4 -14
- package/lib/context.js +36 -3
- package/lib/evaluate.js +119 -97
- package/lib/node.js +99 -0
- package/lib/path.js +12 -152
- package/lib/state.js +36 -49
- package/lib/utils/skip.js +39 -0
- package/package.json +3 -8
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ The agAST VM provides consistency guarantees when with CSTML documents to parse
|
|
|
4
4
|
|
|
5
5
|
## API
|
|
6
6
|
|
|
7
|
-
The VM responds to several instructions, but its primary API is `advance(token)`, where `token` may be a `
|
|
7
|
+
The VM responds to several instructions, but its primary API is `advance(token)`, where `token` may be a `OpenNodeTag`, `CloseNodeTag`, `Literal`, `Reference`, or `Gap`.
|
|
8
8
|
|
|
9
9
|
The VM requires the basic invariants of CSTML to be followed, for example that `Reference` must be followed by either a `OpenNodeTag` or a `Gap`. In fact, `agast-vm` is the reference implementation of these invariants.
|
|
10
10
|
|
|
@@ -15,17 +15,7 @@ Finally the VM supports `bindAttribute(key, value)`. A node's attributes start u
|
|
|
15
15
|
Here are the basic types used by the VM:
|
|
16
16
|
|
|
17
17
|
```ts
|
|
18
|
-
type Token =
|
|
19
|
-
|
|
20
|
-
type OpenFragmentTag {
|
|
21
|
-
type: 'OpenFragmentTag',
|
|
22
|
-
value: null
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
type CloseFragmentTag {
|
|
26
|
-
type: 'CloseFragmentTag',
|
|
27
|
-
value: null
|
|
28
|
-
}
|
|
18
|
+
type Token = OpenNodeTag | CloseNodeTag | Literal | Reference | Gap;
|
|
29
19
|
|
|
30
20
|
type OpenNodeTag {
|
|
31
21
|
type: 'OpenNodeTag',
|
|
@@ -35,8 +25,8 @@ type OpenNodeTag {
|
|
|
35
25
|
trivia: boolean,
|
|
36
26
|
escape: boolean
|
|
37
27
|
},
|
|
38
|
-
language: string,
|
|
39
|
-
type: string,
|
|
28
|
+
language: string | null,
|
|
29
|
+
type: string | null, // null type indicates a fragment
|
|
40
30
|
attributes: { [key: string]: boolean | number | string }
|
|
41
31
|
}
|
|
42
32
|
}
|
package/lib/context.js
CHANGED
|
@@ -11,7 +11,8 @@ function* ownTerminalsFor(range, nextTerminals, tagNodes) {
|
|
|
11
11
|
|
|
12
12
|
for (let term = start; term && term !== pastEnd; term = nextTerminals.get(term)) {
|
|
13
13
|
if (!(term === range[0] || term === range[1]) && term.type === 'OpenNodeTag') {
|
|
14
|
-
term =
|
|
14
|
+
term = tagNodes.get(term).closeTag;
|
|
15
|
+
continue;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
yield term;
|
|
@@ -24,7 +25,7 @@ function* allTerminalsFor(range, nextTerminals) {
|
|
|
24
25
|
|
|
25
26
|
const pastEnd = nextTerminals.get(end);
|
|
26
27
|
|
|
27
|
-
for (let tag = start; tag !== pastEnd; tag = nextTerminals.get(tag)) {
|
|
28
|
+
for (let tag = start; tag && tag !== pastEnd; tag = nextTerminals.get(tag)) {
|
|
28
29
|
yield tag;
|
|
29
30
|
}
|
|
30
31
|
}
|
|
@@ -70,6 +71,14 @@ export const ContextFacade = class AgastContextFacade {
|
|
|
70
71
|
return actuals.get(this).nodeForTag(tag);
|
|
71
72
|
}
|
|
72
73
|
|
|
74
|
+
pathForTag(tag) {
|
|
75
|
+
return actuals.get(this).pathForTag(tag);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
nodeForPath(path) {
|
|
79
|
+
return actuals.get(this).nodeForPath(path);
|
|
80
|
+
}
|
|
81
|
+
|
|
73
82
|
unbox(value) {
|
|
74
83
|
return actuals.get(this).unbox(value);
|
|
75
84
|
}
|
|
@@ -84,6 +93,8 @@ export const Context = class AgastContext {
|
|
|
84
93
|
this.prevTerminals = new WeakMap();
|
|
85
94
|
this.nextTerminals = new WeakMap();
|
|
86
95
|
this.tagNodes = new WeakMap();
|
|
96
|
+
this.tagPaths = new WeakMap();
|
|
97
|
+
this.pathNodes = new WeakMap();
|
|
87
98
|
this.unboxedValues = new WeakMap();
|
|
88
99
|
this.facade = new ContextFacade();
|
|
89
100
|
|
|
@@ -91,7 +102,13 @@ export const Context = class AgastContext {
|
|
|
91
102
|
}
|
|
92
103
|
|
|
93
104
|
getProperty(result, name) {
|
|
94
|
-
|
|
105
|
+
let startTag = result[0] || result;
|
|
106
|
+
|
|
107
|
+
if (startTag.type === 'Reference') {
|
|
108
|
+
startTag = this.nextTerminals.get(startTag);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return this.tagNodes.get(startTag).properties.get(name);
|
|
95
112
|
}
|
|
96
113
|
|
|
97
114
|
isEmpty(range) {
|
|
@@ -123,10 +140,26 @@ export const Context = class AgastContext {
|
|
|
123
140
|
return ownTerminalsFor(range, this.prevTerminals, this.tagNodes);
|
|
124
141
|
}
|
|
125
142
|
|
|
143
|
+
getPreviousTerminal(token) {
|
|
144
|
+
return this.prevTerminals.get(token);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
getNextTerminal(token) {
|
|
148
|
+
return this.nextTerminals.get(token);
|
|
149
|
+
}
|
|
150
|
+
|
|
126
151
|
nodeForTag(tag) {
|
|
127
152
|
return this.tagNodes.get(tag);
|
|
128
153
|
}
|
|
129
154
|
|
|
155
|
+
pathForTag(ref) {
|
|
156
|
+
return this.tagPaths.get(ref);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
nodeForPath(path) {
|
|
160
|
+
return this.pathNodes.get(path);
|
|
161
|
+
}
|
|
162
|
+
|
|
130
163
|
sourceTextFor(range) {
|
|
131
164
|
return sourceTextFor(this.allTerminalsFor(range));
|
|
132
165
|
}
|
package/lib/evaluate.js
CHANGED
|
@@ -2,24 +2,26 @@ import { Coroutine } from '@bablr/coroutine';
|
|
|
2
2
|
import {
|
|
3
3
|
buildNull,
|
|
4
4
|
buildGap,
|
|
5
|
+
buildShift,
|
|
5
6
|
buildReference,
|
|
6
7
|
buildLiteral,
|
|
8
|
+
buildWriteEffect,
|
|
7
9
|
buildDoctypeTag,
|
|
8
10
|
buildNodeOpenTag,
|
|
9
|
-
buildFragmentOpenTag,
|
|
10
|
-
buildFragmentCloseTag,
|
|
11
11
|
buildNodeCloseTag,
|
|
12
12
|
} from '@bablr/agast-helpers/builders';
|
|
13
13
|
import { StreamIterable, getStreamIterator } from '@bablr/agast-helpers/stream';
|
|
14
14
|
import { printExpression } from '@bablr/agast-helpers/print';
|
|
15
15
|
import { reifyExpression } from '@bablr/agast-vm-helpers';
|
|
16
|
-
import { Path
|
|
16
|
+
import { Path } from './path.js';
|
|
17
|
+
import { Node } from './node.js';
|
|
17
18
|
import { State } from './state.js';
|
|
18
19
|
import { facades } from './facades.js';
|
|
19
20
|
|
|
20
|
-
export const evaluate = (ctx, strategy) =>
|
|
21
|
+
export const evaluate = (ctx, strategy, options) =>
|
|
22
|
+
new StreamIterable(__evaluate(ctx, strategy, options));
|
|
21
23
|
|
|
22
|
-
const __evaluate = function* agast(ctx, strategy) {
|
|
24
|
+
const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
23
25
|
let s = State.from(ctx);
|
|
24
26
|
|
|
25
27
|
const co = new Coroutine(getStreamIterator(strategy(facades.get(ctx), facades.get(s))));
|
|
@@ -37,7 +39,7 @@ const __evaluate = function* agast(ctx, strategy) {
|
|
|
37
39
|
const instr = reifyExpression(sourceInstr);
|
|
38
40
|
let returnValue = undefined;
|
|
39
41
|
|
|
40
|
-
const { verb } = instr;
|
|
42
|
+
const { verb, arguments: args = [] } = instr;
|
|
41
43
|
|
|
42
44
|
switch (verb) {
|
|
43
45
|
case 'branch': {
|
|
@@ -66,13 +68,24 @@ const __evaluate = function* agast(ctx, strategy) {
|
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
case 'advance': {
|
|
69
|
-
const {
|
|
71
|
+
const { 0: terminal, 1: options } = args;
|
|
72
|
+
|
|
73
|
+
if (
|
|
74
|
+
s.held &&
|
|
75
|
+
!(
|
|
76
|
+
terminal.type === 'OpenNodeTag' ||
|
|
77
|
+
terminal.type === 'Reference' ||
|
|
78
|
+
terminal.type === 'Gap'
|
|
79
|
+
)
|
|
80
|
+
) {
|
|
81
|
+
throw new Error('Cannot advance while holding');
|
|
82
|
+
}
|
|
70
83
|
|
|
71
84
|
switch (terminal?.type || 'Null') {
|
|
72
85
|
case 'DoctypeTag': {
|
|
73
86
|
const { attributes } = terminal.value;
|
|
74
87
|
const doctypeTag = buildDoctypeTag(attributes);
|
|
75
|
-
const rootPath = Path.from(ctx);
|
|
88
|
+
const rootPath = Path.from(ctx, doctypeTag);
|
|
76
89
|
|
|
77
90
|
if (s.path) {
|
|
78
91
|
throw new Error();
|
|
@@ -109,20 +122,21 @@ const __evaluate = function* agast(ctx, strategy) {
|
|
|
109
122
|
const tag = buildReference(name, isArray);
|
|
110
123
|
|
|
111
124
|
if (s.result.type === 'Reference') {
|
|
112
|
-
throw new Error('
|
|
125
|
+
throw new Error('A reference must have a non-reference value');
|
|
113
126
|
}
|
|
114
127
|
|
|
115
|
-
if (
|
|
128
|
+
if (s.node?.flags.token) {
|
|
116
129
|
throw new Error();
|
|
117
130
|
}
|
|
118
131
|
|
|
119
|
-
if (s.
|
|
120
|
-
|
|
132
|
+
if (s.path.depth) {
|
|
133
|
+
s.node.resolver.consume(tag);
|
|
121
134
|
}
|
|
122
135
|
|
|
123
|
-
s.node.resolver.consume(tag);
|
|
124
|
-
|
|
125
136
|
s.path = s.path.push(ctx, tag);
|
|
137
|
+
s.node = null;
|
|
138
|
+
|
|
139
|
+
ctx.tagPaths.set(tag, s.path);
|
|
126
140
|
|
|
127
141
|
yield* s.emit(tag);
|
|
128
142
|
|
|
@@ -135,10 +149,15 @@ const __evaluate = function* agast(ctx, strategy) {
|
|
|
135
149
|
|
|
136
150
|
if (reference?.type !== 'Reference') throw new Error();
|
|
137
151
|
|
|
138
|
-
s.path = s.path.parent;
|
|
139
|
-
|
|
140
152
|
const gapTag = buildGap();
|
|
141
153
|
|
|
154
|
+
s.held = null;
|
|
155
|
+
|
|
156
|
+
ctx.tagPaths.set(gapTag, s.path);
|
|
157
|
+
|
|
158
|
+
s.node = s.parentNode;
|
|
159
|
+
s.path = s.path.parent;
|
|
160
|
+
|
|
142
161
|
yield* s.emit(gapTag);
|
|
143
162
|
|
|
144
163
|
returnValue = gapTag;
|
|
@@ -150,33 +169,39 @@ const __evaluate = function* agast(ctx, strategy) {
|
|
|
150
169
|
|
|
151
170
|
if (reference?.type !== 'Reference') throw new Error();
|
|
152
171
|
|
|
153
|
-
s.path = s.path.parent;
|
|
154
|
-
|
|
155
172
|
const null_ = buildNull();
|
|
156
173
|
|
|
174
|
+
ctx.tagPaths.set(null_, s.path);
|
|
175
|
+
|
|
176
|
+
s.node = s.parentNode;
|
|
177
|
+
s.path = s.path.parent;
|
|
178
|
+
|
|
157
179
|
yield* s.emit(null_);
|
|
158
180
|
|
|
159
181
|
returnValue = null_;
|
|
160
182
|
break;
|
|
161
183
|
}
|
|
162
184
|
|
|
163
|
-
case '
|
|
164
|
-
const
|
|
185
|
+
case 'Shift': {
|
|
186
|
+
const tag = buildShift();
|
|
165
187
|
|
|
166
|
-
|
|
188
|
+
const finishedNode = ctx.nodeForTag(s.result);
|
|
189
|
+
const ref = ctx.getPreviousTerminal(finishedNode.openTag);
|
|
190
|
+
const finishedPath = ctx.pathForTag(ref);
|
|
167
191
|
|
|
168
|
-
|
|
192
|
+
ctx.pathNodes.set(finishedPath, null);
|
|
169
193
|
|
|
170
|
-
|
|
171
|
-
break;
|
|
172
|
-
}
|
|
194
|
+
s.held = { node: finishedNode, path: finishedPath };
|
|
173
195
|
|
|
174
|
-
|
|
175
|
-
|
|
196
|
+
if (!finishedNode.openTag.value.flags.expression) {
|
|
197
|
+
throw new Error();
|
|
198
|
+
}
|
|
176
199
|
|
|
177
|
-
|
|
200
|
+
s.path = finishedPath;
|
|
178
201
|
|
|
179
|
-
|
|
202
|
+
yield* s.emit(tag);
|
|
203
|
+
|
|
204
|
+
returnValue = tag;
|
|
180
205
|
break;
|
|
181
206
|
}
|
|
182
207
|
|
|
@@ -186,36 +211,42 @@ const __evaluate = function* agast(ctx, strategy) {
|
|
|
186
211
|
const reference = s.result;
|
|
187
212
|
const openTag = buildNodeOpenTag(flags, language, type, intrinsicValue, attributes);
|
|
188
213
|
|
|
189
|
-
if (!
|
|
190
|
-
|
|
191
|
-
|
|
214
|
+
if (!type) {
|
|
215
|
+
s.node = Node.from(openTag);
|
|
216
|
+
ctx.pathNodes.set(s.path, s.node);
|
|
217
|
+
ctx.pathNodes.set(s.node, s.path);
|
|
218
|
+
ctx.tagNodes.set(openTag, s.node);
|
|
219
|
+
ctx.tagPaths.set(openTag, s.path);
|
|
220
|
+
} else {
|
|
221
|
+
if (!flags.trivia && !flags.escape) {
|
|
222
|
+
if (
|
|
223
|
+
reference.type !== 'Reference' &&
|
|
224
|
+
reference.type !== 'Shift' &&
|
|
225
|
+
reference.type !== 'OpenFragmentTag'
|
|
226
|
+
) {
|
|
227
|
+
throw new Error('Invalid location for OpenNodeTag');
|
|
228
|
+
}
|
|
192
229
|
}
|
|
193
|
-
}
|
|
194
230
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
if (!(openFlags.trivia || openFlags.escape) && !s.path.depth) {
|
|
198
|
-
const tag = buildReference('root', false);
|
|
199
|
-
s.path = s.path.push(ctx, tag);
|
|
200
|
-
s.node.resolver.consume(tag);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (flags.expression && !flags.intrinsic) {
|
|
204
|
-
s.expressionDepth++;
|
|
205
|
-
}
|
|
231
|
+
const newNode = new Node(openTag);
|
|
206
232
|
|
|
207
|
-
|
|
233
|
+
newNode.unboundAttributes = new Set(unboundAttributes);
|
|
208
234
|
|
|
209
|
-
|
|
235
|
+
s.node = newNode;
|
|
236
|
+
if (flags.trivia || flags.escape) {
|
|
237
|
+
s.path = s.path.push(ctx, null);
|
|
238
|
+
}
|
|
210
239
|
|
|
211
|
-
|
|
240
|
+
ctx.pathNodes.set(newNode, s.path);
|
|
241
|
+
ctx.pathNodes.set(s.path, newNode);
|
|
242
|
+
ctx.tagNodes.set(openTag, newNode);
|
|
243
|
+
ctx.tagPaths.set(openTag, s.path);
|
|
212
244
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
245
|
+
if (intrinsicValue) {
|
|
246
|
+
newNode.closeTag = newNode.openTag;
|
|
247
|
+
s.node = s.parentNode;
|
|
248
|
+
s.path = s.path.parent;
|
|
217
249
|
|
|
218
|
-
if (s.path.depth > 1) {
|
|
219
250
|
const { properties } = s.node;
|
|
220
251
|
const { name: refName, isArray } = reference.value;
|
|
221
252
|
|
|
@@ -241,24 +272,23 @@ const __evaluate = function* agast(ctx, strategy) {
|
|
|
241
272
|
const { openTag } = s.node;
|
|
242
273
|
const { flags, type: openType } = openTag.value;
|
|
243
274
|
|
|
244
|
-
|
|
245
|
-
throw new Error('Grammar failed to bind all attributes');
|
|
275
|
+
const closeTag = buildNodeCloseTag(type, language);
|
|
246
276
|
|
|
247
|
-
if (
|
|
277
|
+
if (openType) {
|
|
278
|
+
if (s.node.unboundAttributes?.size)
|
|
279
|
+
throw new Error('Grammar failed to bind all attributes');
|
|
248
280
|
|
|
249
|
-
|
|
250
|
-
throw new Error(
|
|
251
|
-
`Grammar close {type: ${type}} did not match open {type: ${openType}}`,
|
|
252
|
-
);
|
|
281
|
+
if (!type) throw new Error(`CloseNodeTag must have type`);
|
|
253
282
|
|
|
254
|
-
|
|
283
|
+
if (type !== openType)
|
|
284
|
+
throw new Error(
|
|
285
|
+
`Grammar close {type: ${type}} did not match open {type: ${openType}}`,
|
|
286
|
+
);
|
|
255
287
|
|
|
256
|
-
|
|
257
|
-
|
|
288
|
+
if (!flags.escape && !flags.trivia) {
|
|
289
|
+
const { name: refName, isArray } = s.path.reference.value;
|
|
258
290
|
|
|
259
|
-
|
|
260
|
-
if (s.path.depth > 2) {
|
|
261
|
-
const { properties } = s.node.parent;
|
|
291
|
+
const { properties } = ctx.nodeForPath(s.path.parent);
|
|
262
292
|
|
|
263
293
|
if (!isArray) {
|
|
264
294
|
properties.set(refName, [openTag, closeTag]);
|
|
@@ -269,19 +299,14 @@ const __evaluate = function* agast(ctx, strategy) {
|
|
|
269
299
|
properties.get(refName).push([openTag, closeTag]);
|
|
270
300
|
}
|
|
271
301
|
}
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (flags.expression) {
|
|
275
|
-
s.expressionDepth--;
|
|
276
|
-
}
|
|
277
302
|
|
|
278
|
-
|
|
303
|
+
ctx.tagNodes.set(closeTag, s.node);
|
|
304
|
+
ctx.tagPaths.set(closeTag, s.path);
|
|
279
305
|
|
|
280
|
-
|
|
306
|
+
s.node.closeTag = closeTag;
|
|
281
307
|
|
|
282
|
-
|
|
308
|
+
s.node = s.parentNode;
|
|
283
309
|
|
|
284
|
-
if (!(flags.trivia || flags.escape)) {
|
|
285
310
|
s.path = s.path.parent;
|
|
286
311
|
}
|
|
287
312
|
|
|
@@ -298,18 +323,8 @@ const __evaluate = function* agast(ctx, strategy) {
|
|
|
298
323
|
break;
|
|
299
324
|
}
|
|
300
325
|
|
|
301
|
-
case 'shift': {
|
|
302
|
-
s.shift();
|
|
303
|
-
break;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
case 'unshift': {
|
|
307
|
-
s.unshift();
|
|
308
|
-
break;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
326
|
case 'bindAttribute': {
|
|
312
|
-
const {
|
|
327
|
+
const { 0: key, 1: value } = args;
|
|
313
328
|
|
|
314
329
|
const { unboundAttributes } = s.node;
|
|
315
330
|
|
|
@@ -317,8 +332,8 @@ const __evaluate = function* agast(ctx, strategy) {
|
|
|
317
332
|
throw new Error('No unbound attribute to bind');
|
|
318
333
|
}
|
|
319
334
|
|
|
320
|
-
if (s.node.openTag.type
|
|
321
|
-
throw new Error();
|
|
335
|
+
if (!s.node.openTag.value.type) {
|
|
336
|
+
throw new Error('Cannot bind attribute to fragment');
|
|
322
337
|
}
|
|
323
338
|
|
|
324
339
|
if (key === 'span') throw new Error('too late');
|
|
@@ -339,25 +354,25 @@ const __evaluate = function* agast(ctx, strategy) {
|
|
|
339
354
|
if (value != null) {
|
|
340
355
|
const { flags, language, type, intrinsicValue } = openTag.value;
|
|
341
356
|
const attributes = { ...openTag.value.attributes, [key]: value };
|
|
342
|
-
const
|
|
357
|
+
const newOpenTag = buildNodeOpenTag(flags, language, type, intrinsicValue, attributes);
|
|
343
358
|
|
|
344
|
-
let
|
|
359
|
+
let openNext = ctx.nextTerminals.get(openTag);
|
|
345
360
|
let startPrev = ctx.prevTerminals.get(openTag);
|
|
346
361
|
|
|
347
|
-
ctx.prevTerminals.set(
|
|
348
|
-
ctx.nextTerminals.set(startPrev,
|
|
362
|
+
ctx.prevTerminals.set(newOpenTag, startPrev);
|
|
363
|
+
ctx.nextTerminals.set(startPrev, newOpenTag);
|
|
349
364
|
|
|
350
|
-
ctx.tagNodes.set(
|
|
365
|
+
ctx.tagNodes.set(newOpenTag, ctx.tagNodes.get(openTag));
|
|
351
366
|
|
|
352
|
-
if (
|
|
353
|
-
ctx.nextTerminals.set(
|
|
354
|
-
ctx.prevTerminals.set(
|
|
367
|
+
if (openNext) {
|
|
368
|
+
ctx.nextTerminals.set(newOpenTag, openNext);
|
|
369
|
+
ctx.prevTerminals.set(openNext, newOpenTag);
|
|
355
370
|
} else {
|
|
356
371
|
// could this terminal be stored anywhere else?
|
|
357
|
-
s.result =
|
|
372
|
+
s.result = newOpenTag;
|
|
358
373
|
}
|
|
359
374
|
|
|
360
|
-
s.node.openTag =
|
|
375
|
+
s.node.openTag = newOpenTag;
|
|
361
376
|
}
|
|
362
377
|
|
|
363
378
|
if (!unboundAttributes.size) {
|
|
@@ -378,6 +393,13 @@ const __evaluate = function* agast(ctx, strategy) {
|
|
|
378
393
|
break;
|
|
379
394
|
}
|
|
380
395
|
|
|
396
|
+
case 'write': {
|
|
397
|
+
if (options.emitEffects) {
|
|
398
|
+
yield buildWriteEffect(args[0], args[1]);
|
|
399
|
+
}
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
|
|
381
403
|
default: {
|
|
382
404
|
throw new Error(`Unexpected call of {type: ${printExpression(verb)}}`);
|
|
383
405
|
}
|
package/lib/node.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Resolver } 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 actuals.get(this).range;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
get openTag() {
|
|
22
|
+
return actuals.get(this).openTag;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get closeTag() {
|
|
26
|
+
return actuals.get(this).closeTag;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get flags() {
|
|
30
|
+
return actuals.get(this).flags;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get attributes() {
|
|
34
|
+
return actuals.get(this).attributes;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const Node = class AgastNode {
|
|
39
|
+
static from(openTag) {
|
|
40
|
+
return new AgastNode(openTag);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
constructor(
|
|
44
|
+
openTag,
|
|
45
|
+
closeTag = null,
|
|
46
|
+
properties = new Map(),
|
|
47
|
+
resolver = new Resolver(),
|
|
48
|
+
unboundAttributes = null,
|
|
49
|
+
) {
|
|
50
|
+
this.openTag = openTag;
|
|
51
|
+
this.closeTag = closeTag;
|
|
52
|
+
this.properties = properties;
|
|
53
|
+
this.resolver = resolver;
|
|
54
|
+
this.unboundAttributes = unboundAttributes;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
get language() {
|
|
58
|
+
return this.openTag.value?.language;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
get type() {
|
|
62
|
+
return this.openTag.value?.type || null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
get flags() {
|
|
66
|
+
return this.openTag.value?.flags || {};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
get attributes() {
|
|
70
|
+
return this.openTag.value?.attributes || {};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
get range() {
|
|
74
|
+
return [this.openTag, this.closeTag];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
branch() {
|
|
78
|
+
const { openTag, closeTag, properties, resolver, unboundAttributes } = this;
|
|
79
|
+
|
|
80
|
+
return new Node(
|
|
81
|
+
openTag,
|
|
82
|
+
closeTag,
|
|
83
|
+
new Map(properties), // there is probably a better way
|
|
84
|
+
resolver.branch(),
|
|
85
|
+
new Set(unboundAttributes),
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
accept(node) {
|
|
90
|
+
this.openTag = node.openTag;
|
|
91
|
+
this.closeTag = node.closeTag;
|
|
92
|
+
this.properties = node.properties;
|
|
93
|
+
this.unboundAttributes = node.unboundAttributes;
|
|
94
|
+
|
|
95
|
+
this.resolver.accept(node.resolver);
|
|
96
|
+
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
};
|
package/lib/path.js
CHANGED
|
@@ -1,61 +1,7 @@
|
|
|
1
1
|
import { WeakStackFrame } from '@bablr/weak-stack';
|
|
2
|
-
import {
|
|
3
|
-
import { findRight } from './utils/array.js';
|
|
2
|
+
import { skipToDepth, buildSkips } from './utils/skip.js';
|
|
4
3
|
import { facades, actuals } from './facades.js';
|
|
5
4
|
|
|
6
|
-
const skipLevels = 3;
|
|
7
|
-
const skipShiftExponentGrowth = 4;
|
|
8
|
-
const skipAmounts = new Array(skipLevels)
|
|
9
|
-
.fill(null)
|
|
10
|
-
.map((_, i) => 2 >> (i * skipShiftExponentGrowth));
|
|
11
|
-
const skipsByPath = new WeakMap();
|
|
12
|
-
|
|
13
|
-
export const NodeFacade = class AgastNodeFacade {
|
|
14
|
-
constructor(path) {
|
|
15
|
-
facades.set(path, this);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
get language() {
|
|
19
|
-
return actuals.get(this).language;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
get type() {
|
|
23
|
-
return actuals.get(this).type;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
get path() {
|
|
27
|
-
return actuals.get(this).path;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
get parent() {
|
|
31
|
-
return facades.get(actuals.get(this).parent);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
get range() {
|
|
35
|
-
return actuals.get(this).range;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
get openTag() {
|
|
39
|
-
return actuals.get(this).openTag;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
get closeTag() {
|
|
43
|
-
return actuals.get(this).closeTag;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
get depth() {
|
|
47
|
-
return actuals.get(this).depth;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
get flags() {
|
|
51
|
-
return actuals.get(this).flags;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
get attributes() {
|
|
55
|
-
return actuals.get(this).attributes;
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
|
|
59
5
|
export const PathFacade = class AgastPathFacade {
|
|
60
6
|
constructor(path) {
|
|
61
7
|
facades.set(path, this);
|
|
@@ -74,17 +20,7 @@ export const PathFacade = class AgastPathFacade {
|
|
|
74
20
|
}
|
|
75
21
|
|
|
76
22
|
at(depth) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (depth > this.depth) throw new Error();
|
|
80
|
-
|
|
81
|
-
let d = actuals.get(parent).depth;
|
|
82
|
-
for (; d > depth; ) {
|
|
83
|
-
const skips = skipsByPath.get(actuals.get(this));
|
|
84
|
-
parent = (skips && findRight(skips, (skip) => d - skip > depth)) || parent.parent;
|
|
85
|
-
d = actuals.get(parent).depth;
|
|
86
|
-
}
|
|
87
|
-
return parent;
|
|
23
|
+
return facades.get(actuals.get(this).at(depth));
|
|
88
24
|
}
|
|
89
25
|
|
|
90
26
|
*parents(includeSelf = false) {
|
|
@@ -96,67 +32,6 @@ export const PathFacade = class AgastPathFacade {
|
|
|
96
32
|
}
|
|
97
33
|
};
|
|
98
34
|
|
|
99
|
-
export const Node = class AgastNode extends WeakStackFrame {
|
|
100
|
-
static from(openTag) {
|
|
101
|
-
return AgastNode.create(openTag);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
constructor(
|
|
105
|
-
openTag,
|
|
106
|
-
closeTag = null,
|
|
107
|
-
properties = new Map(),
|
|
108
|
-
resolver = new Resolver(),
|
|
109
|
-
unboundAttributes = null,
|
|
110
|
-
) {
|
|
111
|
-
super();
|
|
112
|
-
|
|
113
|
-
this.openTag = openTag;
|
|
114
|
-
this.closeTag = closeTag;
|
|
115
|
-
this.properties = properties;
|
|
116
|
-
this.resolver = resolver;
|
|
117
|
-
this.unboundAttributes = unboundAttributes;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
get language() {
|
|
121
|
-
return this.openTag.value?.language;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
get type() {
|
|
125
|
-
return this.openTag.value?.type || Symbol.for('@bablr/fragment');
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
get flags() {
|
|
129
|
-
return this.openTag.value?.flags || {};
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
get attributes() {
|
|
133
|
-
return this.openTag.value?.attributes || {};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
branch() {
|
|
137
|
-
const { openTag, closeTag, properties, resolver, unboundAttributes } = this;
|
|
138
|
-
|
|
139
|
-
return this.replace(
|
|
140
|
-
openTag,
|
|
141
|
-
closeTag,
|
|
142
|
-
new Map(properties), // there is probably a better way
|
|
143
|
-
resolver.branch(),
|
|
144
|
-
new Set(unboundAttributes),
|
|
145
|
-
);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
accept(node) {
|
|
149
|
-
this.openTag = node.openTag;
|
|
150
|
-
this.closeTag = node.closeTag;
|
|
151
|
-
this.properties = node.properties;
|
|
152
|
-
this.unboundAttributes = node.unboundAttributes;
|
|
153
|
-
|
|
154
|
-
this.resolver.accept(node.resolver);
|
|
155
|
-
|
|
156
|
-
return this;
|
|
157
|
-
}
|
|
158
|
-
};
|
|
159
|
-
|
|
160
35
|
export const Path = class AgastPath extends WeakStackFrame {
|
|
161
36
|
static from(context, tag) {
|
|
162
37
|
return Path.create(context, tag);
|
|
@@ -165,43 +40,28 @@ export const Path = class AgastPath extends WeakStackFrame {
|
|
|
165
40
|
constructor(context, reference) {
|
|
166
41
|
super();
|
|
167
42
|
|
|
168
|
-
if (reference && reference.type !== 'Reference') {
|
|
43
|
+
if (reference && reference.type !== 'Reference' && reference.type !== 'DoctypeTag') {
|
|
169
44
|
throw new Error('Invalid reference for path');
|
|
170
45
|
}
|
|
171
46
|
|
|
172
47
|
this.context = context;
|
|
173
48
|
this.reference = reference;
|
|
174
49
|
|
|
175
|
-
|
|
176
|
-
let skipAmount = skipAmounts[skipIdx];
|
|
177
|
-
let skips;
|
|
178
|
-
while ((this.depth & skipAmount) === skipAmount) {
|
|
179
|
-
if (!skips) {
|
|
180
|
-
skips = [];
|
|
181
|
-
skipsByPath.set(this, skips);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
skips[skipIdx] = this.at(this.depth - skipAmount);
|
|
185
|
-
|
|
186
|
-
skipIdx++;
|
|
187
|
-
skipAmount = skipAmounts[skipIdx];
|
|
188
|
-
}
|
|
50
|
+
buildSkips(this);
|
|
189
51
|
|
|
190
52
|
new PathFacade(this);
|
|
191
53
|
}
|
|
192
54
|
|
|
193
|
-
|
|
194
|
-
|
|
55
|
+
get name() {
|
|
56
|
+
return this.reference?.value.name || '[anonymous]';
|
|
57
|
+
}
|
|
195
58
|
|
|
196
|
-
|
|
59
|
+
get isArray() {
|
|
60
|
+
return this.reference?.value.isArray || false;
|
|
61
|
+
}
|
|
197
62
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const skips = skipsByPath.get(this);
|
|
201
|
-
parent = (skips && findRight(skips, (skip) => d - skip > depth)) || parent.parent;
|
|
202
|
-
d = parent.depth;
|
|
203
|
-
}
|
|
204
|
-
return parent;
|
|
63
|
+
at(depth) {
|
|
64
|
+
return skipToDepth(depth, this);
|
|
205
65
|
}
|
|
206
66
|
|
|
207
67
|
*parents(includeSelf = false) {
|
package/lib/state.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { WeakStackFrame } from '@bablr/weak-stack';
|
|
2
2
|
import { startsDocument } from '@bablr/agast-helpers/stream';
|
|
3
3
|
import { facades, actuals } from './facades.js';
|
|
4
|
+
import { buildBeginningOfStreamToken } from '@bablr/agast-helpers/builders';
|
|
4
5
|
|
|
5
6
|
export const StateFacade = class AgastStateFacade {
|
|
6
7
|
constructor(state) {
|
|
@@ -27,6 +28,10 @@ export const StateFacade = class AgastStateFacade {
|
|
|
27
28
|
return actuals.get(this).node;
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
get parentNode() {
|
|
32
|
+
return actuals.get(this).parentNode;
|
|
33
|
+
}
|
|
34
|
+
|
|
30
35
|
get holding() {
|
|
31
36
|
return actuals.get(this).holding;
|
|
32
37
|
}
|
|
@@ -45,10 +50,9 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
45
50
|
context,
|
|
46
51
|
path = null,
|
|
47
52
|
node = null,
|
|
48
|
-
result =
|
|
53
|
+
result = buildBeginningOfStreamToken(),
|
|
49
54
|
emitted = null,
|
|
50
55
|
held = null,
|
|
51
|
-
expressionDepth = 0,
|
|
52
56
|
) {
|
|
53
57
|
super();
|
|
54
58
|
|
|
@@ -60,7 +64,6 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
60
64
|
this.result = result;
|
|
61
65
|
this.emitted = emitted;
|
|
62
66
|
this.held = held;
|
|
63
|
-
this.expressionDepth = expressionDepth;
|
|
64
67
|
|
|
65
68
|
new StateFacade(this);
|
|
66
69
|
}
|
|
@@ -73,41 +76,8 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
73
76
|
return !!this.held;
|
|
74
77
|
}
|
|
75
78
|
|
|
76
|
-
shift() {
|
|
77
|
-
const { tagNodes, prevTerminals, nextTerminals } = this.context;
|
|
78
|
-
|
|
79
|
-
const finishedNode = tagNodes.get(this.result);
|
|
80
|
-
|
|
81
|
-
if (!finishedNode.openTag.value.flags.expression) {
|
|
82
|
-
throw new Error();
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
this.result = prevTerminals.get(finishedNode.openTag);
|
|
86
|
-
|
|
87
|
-
nextTerminals.delete(this.result);
|
|
88
|
-
|
|
89
|
-
this.held = finishedNode;
|
|
90
|
-
|
|
91
|
-
// put the first expression node into the holding register
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
unshift() {
|
|
95
|
-
const { tagNodes, prevTerminals, nextTerminals } = this.context;
|
|
96
|
-
|
|
97
|
-
if (!this.held) {
|
|
98
|
-
throw new Error('cannot unshift when no expression is in the holding register');
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
nextTerminals.set(this.result, this.held.openTag);
|
|
102
|
-
prevTerminals.set(this.held.openTag, this.result);
|
|
103
|
-
|
|
104
|
-
this.result = this.held.closeTag;
|
|
105
|
-
|
|
106
|
-
this.held = null;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
79
|
*emit(terminal, suppressEmit) {
|
|
110
|
-
const { prevTerminals, nextTerminals
|
|
80
|
+
const { prevTerminals, nextTerminals } = this.context;
|
|
111
81
|
|
|
112
82
|
if (terminal) {
|
|
113
83
|
if (prevTerminals.has(terminal)) {
|
|
@@ -118,13 +88,11 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
118
88
|
this.result?.type === 'Reference' &&
|
|
119
89
|
!['OpenNodeTag', 'Gap', 'Null'].includes(terminal.type)
|
|
120
90
|
) {
|
|
121
|
-
throw new Error(
|
|
91
|
+
throw new Error(`${terminal.type} is not a valid reference target`);
|
|
122
92
|
}
|
|
123
93
|
|
|
124
94
|
prevTerminals.set(terminal, this.result);
|
|
125
|
-
|
|
126
|
-
nextTerminals.set(this.result, terminal);
|
|
127
|
-
}
|
|
95
|
+
nextTerminals.set(this.result, terminal);
|
|
128
96
|
|
|
129
97
|
this.result = terminal;
|
|
130
98
|
|
|
@@ -135,12 +103,16 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
135
103
|
}
|
|
136
104
|
}
|
|
137
105
|
|
|
138
|
-
if (!this.depth && !
|
|
106
|
+
if (!this.depth && !suppressEmit) {
|
|
139
107
|
let emittable = nextTerminals.get(this.emitted);
|
|
140
108
|
|
|
141
109
|
while (
|
|
142
110
|
emittable &&
|
|
143
|
-
!(
|
|
111
|
+
!(
|
|
112
|
+
emittable.type === 'OpenNodeTag' &&
|
|
113
|
+
emittable.value.type &&
|
|
114
|
+
this.context.nodeForTag(emittable).unboundAttributes?.size
|
|
115
|
+
)
|
|
144
116
|
) {
|
|
145
117
|
yield emittable;
|
|
146
118
|
this.emitted = emittable;
|
|
@@ -163,27 +135,42 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
163
135
|
return !!this.parent;
|
|
164
136
|
}
|
|
165
137
|
|
|
138
|
+
get parentNode() {
|
|
139
|
+
return this.ctx.nodeForPath(this.path.parent);
|
|
140
|
+
}
|
|
141
|
+
|
|
166
142
|
branch() {
|
|
167
|
-
const { context, path, node, result, emitted, held
|
|
143
|
+
const { context, path, node, result, emitted, held } = this;
|
|
144
|
+
|
|
145
|
+
const newNode = node && node.branch();
|
|
146
|
+
|
|
147
|
+
if (newNode?.openTag) context.tagNodes.set(newNode.openTag, newNode);
|
|
148
|
+
if (newNode?.closeTag) context.tagNodes.set(newNode.closeTag, newNode);
|
|
168
149
|
|
|
169
|
-
return this.push(context, path,
|
|
150
|
+
return this.push(context, path, newNode, result, emitted, held);
|
|
170
151
|
}
|
|
171
152
|
|
|
172
153
|
accept() {
|
|
173
|
-
const { parent } = this;
|
|
154
|
+
const { parent, context } = this;
|
|
174
155
|
|
|
175
156
|
if (!parent) {
|
|
176
|
-
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (this.node && parent.node) {
|
|
161
|
+
parent.node.accept(this.node, context);
|
|
162
|
+
} else {
|
|
163
|
+
parent.node = this.node;
|
|
177
164
|
}
|
|
178
165
|
|
|
179
|
-
|
|
166
|
+
if (this.node?.openTag) context.tagNodes.set(parent.node.openTag, parent.node);
|
|
167
|
+
if (this.node?.closeTag) context.tagNodes.set(parent.node.closeTag, parent.node);
|
|
180
168
|
|
|
181
169
|
// emitted isn't used here and probably doesn't need to be part of state
|
|
182
170
|
|
|
183
171
|
parent.result = this.result;
|
|
184
172
|
parent.held = this.held;
|
|
185
173
|
parent.path = this.path;
|
|
186
|
-
parent.expressionDepth = this.expressionDepth;
|
|
187
174
|
|
|
188
175
|
return parent;
|
|
189
176
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { findRight } from './array.js';
|
|
2
|
+
|
|
3
|
+
const skipLevels = 3;
|
|
4
|
+
const skipShiftExponentGrowth = 4;
|
|
5
|
+
const skipAmounts = new Array(skipLevels)
|
|
6
|
+
.fill(null)
|
|
7
|
+
.map((_, i) => 2 >> (i * skipShiftExponentGrowth));
|
|
8
|
+
const skipsByFrame = new WeakMap();
|
|
9
|
+
|
|
10
|
+
export const buildSkips = (frame) => {
|
|
11
|
+
let skipIdx = 0;
|
|
12
|
+
let skipAmount = skipAmounts[skipIdx];
|
|
13
|
+
let skips;
|
|
14
|
+
while ((frame.depth & skipAmount) === skipAmount) {
|
|
15
|
+
if (!skips) {
|
|
16
|
+
skips = [];
|
|
17
|
+
skipsByFrame.set(frame, skips);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
skips[skipIdx] = frame.at(frame.depth - skipAmount);
|
|
21
|
+
|
|
22
|
+
skipIdx++;
|
|
23
|
+
skipAmount = skipAmounts[skipIdx];
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const skipToDepth = (depth, frame) => {
|
|
28
|
+
let parent = frame;
|
|
29
|
+
|
|
30
|
+
if (depth > frame.depth) throw new Error();
|
|
31
|
+
|
|
32
|
+
let d = frame.depth;
|
|
33
|
+
for (; d > depth; ) {
|
|
34
|
+
const skips = skipsByFrame.get(frame);
|
|
35
|
+
parent = (skips && findRight(skips, (skip) => d - skip > depth)) || parent.parent;
|
|
36
|
+
d = parent.depth;
|
|
37
|
+
}
|
|
38
|
+
return parent;
|
|
39
|
+
};
|
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.3.0",
|
|
5
5
|
"author": "Conrad Buck<conartist6@gmail.com>",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -11,24 +11,19 @@
|
|
|
11
11
|
".": "./lib/index.js"
|
|
12
12
|
},
|
|
13
13
|
"sideEffects": false,
|
|
14
|
-
"scripts": {
|
|
15
|
-
"test": "node ./test/runner.js"
|
|
16
|
-
},
|
|
17
14
|
"dependencies": {
|
|
18
|
-
"@bablr/agast-helpers": "0.
|
|
19
|
-
"@bablr/agast-vm-helpers": "0.
|
|
15
|
+
"@bablr/agast-helpers": "0.2.0",
|
|
16
|
+
"@bablr/agast-vm-helpers": "0.2.0",
|
|
20
17
|
"@bablr/coroutine": "0.1.0",
|
|
21
18
|
"@bablr/weak-stack": "0.1.0"
|
|
22
19
|
},
|
|
23
20
|
"devDependencies": {
|
|
24
21
|
"@bablr/agast-vm-strategy-passthrough": "github:bablr-lang/agast-vm-strategy-passthrough#2bd3a0c7311037af92c5b81941c79161499f6c9e",
|
|
25
22
|
"@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#49f5952efed27f94ee9b94340eb1563c440bf64e",
|
|
26
|
-
"@bablr/strategy_enhancer-debug-log": "0.1.1",
|
|
27
23
|
"enhanced-resolve": "^5.12.0",
|
|
28
24
|
"eslint": "^8.32.0",
|
|
29
25
|
"eslint-import-resolver-enhanced-resolve": "^1.0.5",
|
|
30
26
|
"eslint-plugin-import": "^2.27.5",
|
|
31
|
-
"expect": "29.7.0",
|
|
32
27
|
"iter-tools-es": "^7.3.1",
|
|
33
28
|
"prettier": "^2.6.2"
|
|
34
29
|
},
|