@bablr/agast-vm 0.2.0 → 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/lib/context.js +28 -1
- package/lib/evaluate.js +85 -78
- package/lib/node.js +99 -0
- package/lib/path.js +9 -131
- package/lib/state.js +25 -5
- package/package.json +3 -8
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;
|
|
@@ -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
|
|
|
@@ -129,10 +140,26 @@ export const Context = class AgastContext {
|
|
|
129
140
|
return ownTerminalsFor(range, this.prevTerminals, this.tagNodes);
|
|
130
141
|
}
|
|
131
142
|
|
|
143
|
+
getPreviousTerminal(token) {
|
|
144
|
+
return this.prevTerminals.get(token);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
getNextTerminal(token) {
|
|
148
|
+
return this.nextTerminals.get(token);
|
|
149
|
+
}
|
|
150
|
+
|
|
132
151
|
nodeForTag(tag) {
|
|
133
152
|
return this.tagNodes.get(tag);
|
|
134
153
|
}
|
|
135
154
|
|
|
155
|
+
pathForTag(ref) {
|
|
156
|
+
return this.tagPaths.get(ref);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
nodeForPath(path) {
|
|
160
|
+
return this.pathNodes.get(path);
|
|
161
|
+
}
|
|
162
|
+
|
|
136
163
|
sourceTextFor(range) {
|
|
137
164
|
return sourceTextFor(this.allTerminalsFor(range));
|
|
138
165
|
}
|
package/lib/evaluate.js
CHANGED
|
@@ -13,7 +13,8 @@ import {
|
|
|
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
|
|
|
@@ -84,7 +85,7 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
84
85
|
case 'DoctypeTag': {
|
|
85
86
|
const { attributes } = terminal.value;
|
|
86
87
|
const doctypeTag = buildDoctypeTag(attributes);
|
|
87
|
-
const rootPath = Path.from(ctx);
|
|
88
|
+
const rootPath = Path.from(ctx, doctypeTag);
|
|
88
89
|
|
|
89
90
|
if (s.path) {
|
|
90
91
|
throw new Error();
|
|
@@ -124,17 +125,18 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
124
125
|
throw new Error('A reference must have a non-reference value');
|
|
125
126
|
}
|
|
126
127
|
|
|
127
|
-
if (
|
|
128
|
+
if (s.node?.flags.token) {
|
|
128
129
|
throw new Error();
|
|
129
130
|
}
|
|
130
131
|
|
|
131
|
-
if (s.
|
|
132
|
-
|
|
132
|
+
if (s.path.depth) {
|
|
133
|
+
s.node.resolver.consume(tag);
|
|
133
134
|
}
|
|
134
135
|
|
|
135
|
-
s.node.resolver.consume(tag);
|
|
136
|
-
|
|
137
136
|
s.path = s.path.push(ctx, tag);
|
|
137
|
+
s.node = null;
|
|
138
|
+
|
|
139
|
+
ctx.tagPaths.set(tag, s.path);
|
|
138
140
|
|
|
139
141
|
yield* s.emit(tag);
|
|
140
142
|
|
|
@@ -147,24 +149,47 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
147
149
|
|
|
148
150
|
if (reference?.type !== 'Reference') throw new Error();
|
|
149
151
|
|
|
150
|
-
s.path = s.path.parent;
|
|
151
|
-
|
|
152
152
|
const gapTag = buildGap();
|
|
153
153
|
|
|
154
154
|
s.held = null;
|
|
155
155
|
|
|
156
|
+
ctx.tagPaths.set(gapTag, s.path);
|
|
157
|
+
|
|
158
|
+
s.node = s.parentNode;
|
|
159
|
+
s.path = s.path.parent;
|
|
160
|
+
|
|
156
161
|
yield* s.emit(gapTag);
|
|
157
162
|
|
|
158
163
|
returnValue = gapTag;
|
|
159
164
|
break;
|
|
160
165
|
}
|
|
161
166
|
|
|
167
|
+
case 'Null': {
|
|
168
|
+
const reference = s.result;
|
|
169
|
+
|
|
170
|
+
if (reference?.type !== 'Reference') throw new Error();
|
|
171
|
+
|
|
172
|
+
const null_ = buildNull();
|
|
173
|
+
|
|
174
|
+
ctx.tagPaths.set(null_, s.path);
|
|
175
|
+
|
|
176
|
+
s.node = s.parentNode;
|
|
177
|
+
s.path = s.path.parent;
|
|
178
|
+
|
|
179
|
+
yield* s.emit(null_);
|
|
180
|
+
|
|
181
|
+
returnValue = null_;
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
|
|
162
185
|
case 'Shift': {
|
|
163
|
-
const { tagNodes, prevTerminals } = ctx;
|
|
164
186
|
const tag = buildShift();
|
|
165
187
|
|
|
166
|
-
const finishedNode =
|
|
167
|
-
const
|
|
188
|
+
const finishedNode = ctx.nodeForTag(s.result);
|
|
189
|
+
const ref = ctx.getPreviousTerminal(finishedNode.openTag);
|
|
190
|
+
const finishedPath = ctx.pathForTag(ref);
|
|
191
|
+
|
|
192
|
+
ctx.pathNodes.set(finishedPath, null);
|
|
168
193
|
|
|
169
194
|
s.held = { node: finishedNode, path: finishedPath };
|
|
170
195
|
|
|
@@ -180,21 +205,6 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
180
205
|
break;
|
|
181
206
|
}
|
|
182
207
|
|
|
183
|
-
case 'Null': {
|
|
184
|
-
const reference = s.result;
|
|
185
|
-
|
|
186
|
-
if (reference?.type !== 'Reference') throw new Error();
|
|
187
|
-
|
|
188
|
-
s.path = s.path.parent;
|
|
189
|
-
|
|
190
|
-
const null_ = buildNull();
|
|
191
|
-
|
|
192
|
-
yield* s.emit(null_);
|
|
193
|
-
|
|
194
|
-
returnValue = null_;
|
|
195
|
-
break;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
208
|
case 'OpenNodeTag': {
|
|
199
209
|
const { flags, language, type, intrinsicValue, attributes } = terminal.value;
|
|
200
210
|
const { unboundAttributes } = options || {};
|
|
@@ -202,10 +212,13 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
202
212
|
const openTag = buildNodeOpenTag(flags, language, type, intrinsicValue, attributes);
|
|
203
213
|
|
|
204
214
|
if (!type) {
|
|
205
|
-
s.node = Node.from(
|
|
215
|
+
s.node = Node.from(openTag);
|
|
216
|
+
ctx.pathNodes.set(s.path, s.node);
|
|
217
|
+
ctx.pathNodes.set(s.node, s.path);
|
|
206
218
|
ctx.tagNodes.set(openTag, s.node);
|
|
219
|
+
ctx.tagPaths.set(openTag, s.path);
|
|
207
220
|
} else {
|
|
208
|
-
if (!flags.trivia && !flags.escape
|
|
221
|
+
if (!flags.trivia && !flags.escape) {
|
|
209
222
|
if (
|
|
210
223
|
reference.type !== 'Reference' &&
|
|
211
224
|
reference.type !== 'Shift' &&
|
|
@@ -215,37 +228,35 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
215
228
|
}
|
|
216
229
|
}
|
|
217
230
|
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
if (!(openFlags.trivia || openFlags.escape) && !s.path.depth) {
|
|
221
|
-
const tag = buildReference('root', false);
|
|
222
|
-
s.path = s.path.push(ctx, tag);
|
|
223
|
-
s.node.resolver.consume(tag);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
const newNode = s.node.push(s.path, openTag);
|
|
231
|
+
const newNode = new Node(openTag);
|
|
227
232
|
|
|
228
233
|
newNode.unboundAttributes = new Set(unboundAttributes);
|
|
229
234
|
|
|
235
|
+
s.node = newNode;
|
|
236
|
+
if (flags.trivia || flags.escape) {
|
|
237
|
+
s.path = s.path.push(ctx, null);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
ctx.pathNodes.set(newNode, s.path);
|
|
241
|
+
ctx.pathNodes.set(s.path, newNode);
|
|
230
242
|
ctx.tagNodes.set(openTag, newNode);
|
|
243
|
+
ctx.tagPaths.set(openTag, s.path);
|
|
231
244
|
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
|
|
245
|
+
if (intrinsicValue) {
|
|
246
|
+
newNode.closeTag = newNode.openTag;
|
|
247
|
+
s.node = s.parentNode;
|
|
235
248
|
s.path = s.path.parent;
|
|
236
249
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
properties.set(refName, []);
|
|
246
|
-
}
|
|
247
|
-
properties.get(refName).push([openTag, openTag]);
|
|
250
|
+
const { properties } = s.node;
|
|
251
|
+
const { name: refName, isArray } = reference.value;
|
|
252
|
+
|
|
253
|
+
if (!isArray) {
|
|
254
|
+
properties.set(refName, [openTag, openTag]);
|
|
255
|
+
} else {
|
|
256
|
+
if (!properties.has(refName)) {
|
|
257
|
+
properties.set(refName, []);
|
|
248
258
|
}
|
|
259
|
+
properties.get(refName).push([openTag, openTag]);
|
|
249
260
|
}
|
|
250
261
|
}
|
|
251
262
|
}
|
|
@@ -269,7 +280,7 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
269
280
|
|
|
270
281
|
if (!type) throw new Error(`CloseNodeTag must have type`);
|
|
271
282
|
|
|
272
|
-
if (
|
|
283
|
+
if (type !== openType)
|
|
273
284
|
throw new Error(
|
|
274
285
|
`Grammar close {type: ${type}} did not match open {type: ${openType}}`,
|
|
275
286
|
);
|
|
@@ -277,30 +288,26 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
277
288
|
if (!flags.escape && !flags.trivia) {
|
|
278
289
|
const { name: refName, isArray } = s.path.reference.value;
|
|
279
290
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (!properties.has(refName)) {
|
|
288
|
-
properties.set(refName, []);
|
|
289
|
-
}
|
|
290
|
-
properties.get(refName).push([openTag, closeTag]);
|
|
291
|
+
const { properties } = ctx.nodeForPath(s.path.parent);
|
|
292
|
+
|
|
293
|
+
if (!isArray) {
|
|
294
|
+
properties.set(refName, [openTag, closeTag]);
|
|
295
|
+
} else {
|
|
296
|
+
if (!properties.has(refName)) {
|
|
297
|
+
properties.set(refName, []);
|
|
291
298
|
}
|
|
299
|
+
properties.get(refName).push([openTag, closeTag]);
|
|
292
300
|
}
|
|
293
301
|
}
|
|
294
302
|
|
|
295
303
|
ctx.tagNodes.set(closeTag, s.node);
|
|
304
|
+
ctx.tagPaths.set(closeTag, s.path);
|
|
296
305
|
|
|
297
306
|
s.node.closeTag = closeTag;
|
|
298
307
|
|
|
299
|
-
s.node = s.
|
|
308
|
+
s.node = s.parentNode;
|
|
300
309
|
|
|
301
|
-
|
|
302
|
-
s.path = s.path.parent;
|
|
303
|
-
}
|
|
310
|
+
s.path = s.path.parent;
|
|
304
311
|
}
|
|
305
312
|
|
|
306
313
|
yield* s.emit(closeTag, flags.expression);
|
|
@@ -347,25 +354,25 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
347
354
|
if (value != null) {
|
|
348
355
|
const { flags, language, type, intrinsicValue } = openTag.value;
|
|
349
356
|
const attributes = { ...openTag.value.attributes, [key]: value };
|
|
350
|
-
const
|
|
357
|
+
const newOpenTag = buildNodeOpenTag(flags, language, type, intrinsicValue, attributes);
|
|
351
358
|
|
|
352
|
-
let
|
|
359
|
+
let openNext = ctx.nextTerminals.get(openTag);
|
|
353
360
|
let startPrev = ctx.prevTerminals.get(openTag);
|
|
354
361
|
|
|
355
|
-
ctx.prevTerminals.set(
|
|
356
|
-
ctx.nextTerminals.set(startPrev,
|
|
362
|
+
ctx.prevTerminals.set(newOpenTag, startPrev);
|
|
363
|
+
ctx.nextTerminals.set(startPrev, newOpenTag);
|
|
357
364
|
|
|
358
|
-
ctx.tagNodes.set(
|
|
365
|
+
ctx.tagNodes.set(newOpenTag, ctx.tagNodes.get(openTag));
|
|
359
366
|
|
|
360
|
-
if (
|
|
361
|
-
ctx.nextTerminals.set(
|
|
362
|
-
ctx.prevTerminals.set(
|
|
367
|
+
if (openNext) {
|
|
368
|
+
ctx.nextTerminals.set(newOpenTag, openNext);
|
|
369
|
+
ctx.prevTerminals.set(openNext, newOpenTag);
|
|
363
370
|
} else {
|
|
364
371
|
// could this terminal be stored anywhere else?
|
|
365
|
-
s.result =
|
|
372
|
+
s.result = newOpenTag;
|
|
366
373
|
}
|
|
367
374
|
|
|
368
|
-
s.node.openTag =
|
|
375
|
+
s.node.openTag = newOpenTag;
|
|
369
376
|
}
|
|
370
377
|
|
|
371
378
|
if (!unboundAttributes.size) {
|
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,58 +1,7 @@
|
|
|
1
1
|
import { WeakStackFrame } from '@bablr/weak-stack';
|
|
2
|
-
import { Resolver } from '@bablr/agast-helpers/tree';
|
|
3
2
|
import { skipToDepth, buildSkips } from './utils/skip.js';
|
|
4
3
|
import { facades, actuals } from './facades.js';
|
|
5
4
|
|
|
6
|
-
export const NodeFacade = class AgastNodeFacade {
|
|
7
|
-
constructor(path) {
|
|
8
|
-
facades.set(path, this);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
get language() {
|
|
12
|
-
return actuals.get(this).language;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
get type() {
|
|
16
|
-
return actuals.get(this).type;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
get path() {
|
|
20
|
-
return actuals.get(this).path;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
get parent() {
|
|
24
|
-
return facades.get(actuals.get(this).parent);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
get range() {
|
|
28
|
-
return actuals.get(this).range;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
get openTag() {
|
|
32
|
-
return actuals.get(this).openTag;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
get closeTag() {
|
|
36
|
-
return actuals.get(this).closeTag;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
get depth() {
|
|
40
|
-
return actuals.get(this).depth;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
get flags() {
|
|
44
|
-
return actuals.get(this).flags;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
get attributes() {
|
|
48
|
-
return actuals.get(this).attributes;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
at(depth) {
|
|
52
|
-
return facades.get(actuals.get(this).at(depth));
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
|
|
56
5
|
export const PathFacade = class AgastPathFacade {
|
|
57
6
|
constructor(path) {
|
|
58
7
|
facades.set(path, this);
|
|
@@ -83,85 +32,6 @@ export const PathFacade = class AgastPathFacade {
|
|
|
83
32
|
}
|
|
84
33
|
};
|
|
85
34
|
|
|
86
|
-
export const Node = class AgastNode extends WeakStackFrame {
|
|
87
|
-
static from(path, openTag) {
|
|
88
|
-
return AgastNode.create(path, openTag);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
constructor(
|
|
92
|
-
path,
|
|
93
|
-
openTag,
|
|
94
|
-
closeTag = null,
|
|
95
|
-
properties = new Map(),
|
|
96
|
-
resolver = new Resolver(),
|
|
97
|
-
unboundAttributes = null,
|
|
98
|
-
) {
|
|
99
|
-
super();
|
|
100
|
-
|
|
101
|
-
this.path = path;
|
|
102
|
-
this.openTag = openTag;
|
|
103
|
-
this.closeTag = closeTag;
|
|
104
|
-
this.properties = properties;
|
|
105
|
-
this.resolver = resolver;
|
|
106
|
-
this.unboundAttributes = unboundAttributes;
|
|
107
|
-
|
|
108
|
-
buildSkips(this);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
get language() {
|
|
112
|
-
return this.openTag.value?.language;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
get type() {
|
|
116
|
-
return this.openTag.value?.type || null;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
get flags() {
|
|
120
|
-
return this.openTag.value?.flags || {};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
get attributes() {
|
|
124
|
-
return this.openTag.value?.attributes || {};
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
at(depth) {
|
|
128
|
-
return skipToDepth(depth, this);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
*parents(includeSelf = false) {
|
|
132
|
-
if (includeSelf) yield this;
|
|
133
|
-
let parent = this;
|
|
134
|
-
while ((parent = parent.parent)) {
|
|
135
|
-
yield parent;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
branch() {
|
|
140
|
-
const { path, openTag, closeTag, properties, resolver, unboundAttributes } = this;
|
|
141
|
-
|
|
142
|
-
return this.replace(
|
|
143
|
-
path,
|
|
144
|
-
openTag,
|
|
145
|
-
closeTag,
|
|
146
|
-
new Map(properties), // there is probably a better way
|
|
147
|
-
resolver.branch(),
|
|
148
|
-
new Set(unboundAttributes),
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
accept(node) {
|
|
153
|
-
this.path = node.path;
|
|
154
|
-
this.openTag = node.openTag;
|
|
155
|
-
this.closeTag = node.closeTag;
|
|
156
|
-
this.properties = node.properties;
|
|
157
|
-
this.unboundAttributes = node.unboundAttributes;
|
|
158
|
-
|
|
159
|
-
this.resolver.accept(node.resolver);
|
|
160
|
-
|
|
161
|
-
return this;
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
|
|
165
35
|
export const Path = class AgastPath extends WeakStackFrame {
|
|
166
36
|
static from(context, tag) {
|
|
167
37
|
return Path.create(context, tag);
|
|
@@ -170,7 +40,7 @@ export const Path = class AgastPath extends WeakStackFrame {
|
|
|
170
40
|
constructor(context, reference) {
|
|
171
41
|
super();
|
|
172
42
|
|
|
173
|
-
if (reference && reference.type !== 'Reference') {
|
|
43
|
+
if (reference && reference.type !== 'Reference' && reference.type !== 'DoctypeTag') {
|
|
174
44
|
throw new Error('Invalid reference for path');
|
|
175
45
|
}
|
|
176
46
|
|
|
@@ -182,6 +52,14 @@ export const Path = class AgastPath extends WeakStackFrame {
|
|
|
182
52
|
new PathFacade(this);
|
|
183
53
|
}
|
|
184
54
|
|
|
55
|
+
get name() {
|
|
56
|
+
return this.reference?.value.name || '[anonymous]';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
get isArray() {
|
|
60
|
+
return this.reference?.value.isArray || false;
|
|
61
|
+
}
|
|
62
|
+
|
|
185
63
|
at(depth) {
|
|
186
64
|
return skipToDepth(depth, this);
|
|
187
65
|
}
|
package/lib/state.js
CHANGED
|
@@ -28,6 +28,10 @@ export const StateFacade = class AgastStateFacade {
|
|
|
28
28
|
return actuals.get(this).node;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
get parentNode() {
|
|
32
|
+
return actuals.get(this).parentNode;
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
get holding() {
|
|
32
36
|
return actuals.get(this).holding;
|
|
33
37
|
}
|
|
@@ -73,7 +77,7 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
*emit(terminal, suppressEmit) {
|
|
76
|
-
const { prevTerminals, nextTerminals
|
|
80
|
+
const { prevTerminals, nextTerminals } = this.context;
|
|
77
81
|
|
|
78
82
|
if (terminal) {
|
|
79
83
|
if (prevTerminals.has(terminal)) {
|
|
@@ -107,7 +111,7 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
107
111
|
!(
|
|
108
112
|
emittable.type === 'OpenNodeTag' &&
|
|
109
113
|
emittable.value.type &&
|
|
110
|
-
|
|
114
|
+
this.context.nodeForTag(emittable).unboundAttributes?.size
|
|
111
115
|
)
|
|
112
116
|
) {
|
|
113
117
|
yield emittable;
|
|
@@ -131,20 +135,36 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
131
135
|
return !!this.parent;
|
|
132
136
|
}
|
|
133
137
|
|
|
138
|
+
get parentNode() {
|
|
139
|
+
return this.ctx.nodeForPath(this.path.parent);
|
|
140
|
+
}
|
|
141
|
+
|
|
134
142
|
branch() {
|
|
135
143
|
const { context, path, node, result, emitted, held } = this;
|
|
136
144
|
|
|
137
|
-
|
|
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);
|
|
149
|
+
|
|
150
|
+
return this.push(context, path, newNode, result, emitted, held);
|
|
138
151
|
}
|
|
139
152
|
|
|
140
153
|
accept() {
|
|
141
|
-
const { parent } = this;
|
|
154
|
+
const { parent, context } = this;
|
|
142
155
|
|
|
143
156
|
if (!parent) {
|
|
144
157
|
return null;
|
|
145
158
|
}
|
|
146
159
|
|
|
147
|
-
|
|
160
|
+
if (this.node && parent.node) {
|
|
161
|
+
parent.node.accept(this.node, context);
|
|
162
|
+
} else {
|
|
163
|
+
parent.node = this.node;
|
|
164
|
+
}
|
|
165
|
+
|
|
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);
|
|
148
168
|
|
|
149
169
|
// emitted isn't used here and probably doesn't need to be part of state
|
|
150
170
|
|
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
|
},
|