@bablr/agast-vm 0.6.1 → 0.7.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 +8 -71
- package/lib/evaluate.js +34 -174
- package/lib/index.js +1 -1
- package/lib/path.js +2 -75
- package/lib/state.js +167 -365
- package/lib/utils/iterable.js +13 -0
- package/package.json +6 -4
package/lib/context.js
CHANGED
|
@@ -1,31 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
getCooked as getCookedFromTree,
|
|
4
|
-
sourceTextFor as sourceTextForTree,
|
|
5
|
-
} from '@bablr/agast-helpers/tree';
|
|
6
|
-
import { getCooked as getCookedFromStream } from '@bablr/agast-helpers/stream';
|
|
7
|
-
import { OpenNodeTag, CloseNodeTag } from '@bablr/agast-helpers/symbols';
|
|
8
|
-
import { facades, actuals } from './facades.js';
|
|
9
|
-
|
|
10
|
-
const { isArray } = Array;
|
|
11
|
-
|
|
12
|
-
function* allTagsFor(range, nextTags) {
|
|
13
|
-
if (!range) return;
|
|
14
|
-
const { 0: start, 1: end } = range;
|
|
15
|
-
|
|
16
|
-
const pastEnd = nextTags.get(end);
|
|
1
|
+
import { reifyExpression } from '@bablr/agast-vm-helpers';
|
|
2
|
+
import { sourceTextFor as sourceTextForTree } from '@bablr/agast-helpers/tree';
|
|
17
3
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
}
|
|
4
|
+
import { allTagPathsFor } from './path.js';
|
|
5
|
+
import { facades, actuals } from './facades.js';
|
|
22
6
|
|
|
23
7
|
export const ContextFacade = class AgastContextFacade {
|
|
24
|
-
|
|
8
|
+
getPreviousTagPath(token) {
|
|
25
9
|
return actuals.get(this).prevTags.get(token);
|
|
26
10
|
}
|
|
27
11
|
|
|
28
|
-
|
|
12
|
+
getNextTagPath(token) {
|
|
29
13
|
return actuals.get(this).nextTags.get(token);
|
|
30
14
|
}
|
|
31
15
|
|
|
@@ -33,10 +17,6 @@ export const ContextFacade = class AgastContextFacade {
|
|
|
33
17
|
return actuals.get(this).allTagsFor(range);
|
|
34
18
|
}
|
|
35
19
|
|
|
36
|
-
getCooked(range) {
|
|
37
|
-
return actuals.get(this).getCooked(range);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
20
|
reifyExpression(range) {
|
|
41
21
|
return actuals.get(this).reifyExpression(range);
|
|
42
22
|
}
|
|
@@ -52,10 +32,6 @@ export const ContextFacade = class AgastContextFacade {
|
|
|
52
32
|
nodeForTag(tag) {
|
|
53
33
|
return actuals.get(this).nodeForTag(tag);
|
|
54
34
|
}
|
|
55
|
-
|
|
56
|
-
unbox(value) {
|
|
57
|
-
return actuals.get(this).unbox(value);
|
|
58
|
-
}
|
|
59
35
|
};
|
|
60
36
|
|
|
61
37
|
export const Context = class AgastContext {
|
|
@@ -64,42 +40,18 @@ export const Context = class AgastContext {
|
|
|
64
40
|
}
|
|
65
41
|
|
|
66
42
|
constructor() {
|
|
67
|
-
this.prevTags = new WeakMap();
|
|
68
|
-
this.nextTags = new WeakMap();
|
|
69
43
|
this.tagNodes = new WeakMap();
|
|
70
|
-
this.unboxedValues = new WeakMap();
|
|
71
44
|
this.facade = new ContextFacade();
|
|
72
45
|
|
|
73
46
|
facades.set(this, this.facade);
|
|
74
47
|
}
|
|
75
48
|
|
|
76
|
-
isEmpty(range) {
|
|
77
|
-
const { path, parent } = this;
|
|
78
|
-
|
|
79
|
-
if (range[0]?.type === OpenNodeTag && path !== parent.path) {
|
|
80
|
-
const nextTag = this.nextTags.get(range[0]);
|
|
81
|
-
if (!nextTag || nextTag.type === CloseNodeTag) {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
return range[0] === range[1];
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
49
|
allTagsFor(range) {
|
|
90
|
-
return
|
|
50
|
+
return allTagPathsFor(range);
|
|
91
51
|
}
|
|
92
52
|
|
|
93
53
|
allTagsReverseFor(range) {
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
getPreviousTag(token) {
|
|
98
|
-
return this.prevTags.get(token);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
getNextTag(token) {
|
|
102
|
-
return this.nextTags.get(token);
|
|
54
|
+
throw new Error('not implemented');
|
|
103
55
|
}
|
|
104
56
|
|
|
105
57
|
nodeForTag(tag) {
|
|
@@ -130,22 +82,7 @@ export const Context = class AgastContext {
|
|
|
130
82
|
return start ? [start, end] : null;
|
|
131
83
|
}
|
|
132
84
|
|
|
133
|
-
unbox(value) {
|
|
134
|
-
const { unboxedValues } = this;
|
|
135
|
-
if (!unboxedValues.has(value)) {
|
|
136
|
-
unboxedValues.set(value, reifyExpressionShallow(value));
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return unboxedValues.get(value);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
85
|
reifyExpression(value) {
|
|
143
86
|
return reifyExpression(value);
|
|
144
87
|
}
|
|
145
|
-
|
|
146
|
-
getCooked(nodeOrRange) {
|
|
147
|
-
return isArray(nodeOrRange)
|
|
148
|
-
? getCookedFromStream(this.allTagsFor(nodeOrRange))
|
|
149
|
-
: getCookedFromTree(nodeOrRange);
|
|
150
|
-
}
|
|
151
88
|
};
|
package/lib/evaluate.js
CHANGED
|
@@ -5,21 +5,14 @@ import {
|
|
|
5
5
|
buildShiftTag,
|
|
6
6
|
buildReferenceTag,
|
|
7
7
|
buildLiteralTag,
|
|
8
|
-
buildWriteEffect,
|
|
9
8
|
buildDoctypeTag,
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
buildOpenNodeTag,
|
|
10
|
+
buildCloseNodeTag,
|
|
12
11
|
} from '@bablr/agast-vm-helpers/internal-builders';
|
|
13
|
-
import {
|
|
12
|
+
import { getEmbeddedTag } from '@bablr/agast-vm-helpers/deembed';
|
|
14
13
|
import { StreamIterable, getStreamIterator } from '@bablr/agast-helpers/stream';
|
|
15
14
|
import { printExpression } from '@bablr/agast-helpers/print';
|
|
16
|
-
import {
|
|
17
|
-
getRange,
|
|
18
|
-
getOpenTag,
|
|
19
|
-
buildArrayTag,
|
|
20
|
-
buildFragmentCloseTag,
|
|
21
|
-
buildFragmentOpenTag,
|
|
22
|
-
} from '@bablr/agast-helpers/tree';
|
|
15
|
+
import { buildArrayInitializerTag } from '@bablr/agast-helpers/tree';
|
|
23
16
|
import {
|
|
24
17
|
DoctypeTag,
|
|
25
18
|
OpenNodeTag,
|
|
@@ -28,22 +21,22 @@ import {
|
|
|
28
21
|
ShiftTag,
|
|
29
22
|
GapTag,
|
|
30
23
|
NullTag,
|
|
31
|
-
|
|
24
|
+
ArrayInitializerTag,
|
|
32
25
|
LiteralTag,
|
|
33
|
-
CloseFragmentTag,
|
|
34
|
-
OpenFragmentTag,
|
|
35
26
|
} from '@bablr/agast-helpers/symbols';
|
|
36
|
-
import * as btree from '@bablr/agast-helpers/btree';
|
|
37
27
|
import { State } from './state.js';
|
|
28
|
+
import { Context } from './context.js';
|
|
38
29
|
import { facades } from './facades.js';
|
|
39
30
|
|
|
40
|
-
export const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
31
|
+
export const agast = (strategy, options = {}) => {
|
|
32
|
+
const ctx = Context.create();
|
|
44
33
|
let s = State.from(ctx, options.expressions);
|
|
45
34
|
|
|
46
|
-
|
|
35
|
+
return new StreamIterable(__agast(ctx, s, strategy(facades.get(ctx), facades.get(s))));
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
function* __agast(ctx, s, instructions) {
|
|
39
|
+
const co = new Coroutine(getStreamIterator(instructions));
|
|
47
40
|
|
|
48
41
|
co.advance();
|
|
49
42
|
|
|
@@ -61,35 +54,10 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
61
54
|
const { verb, arguments: args = [] } = instr;
|
|
62
55
|
|
|
63
56
|
switch (verb) {
|
|
64
|
-
case 'branch': {
|
|
65
|
-
s = s.branch();
|
|
66
|
-
|
|
67
|
-
returnValue = facades.get(s);
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
case 'accept': {
|
|
72
|
-
s = s.accept();
|
|
73
|
-
|
|
74
|
-
if (s.depth === 0) {
|
|
75
|
-
yield* s.emit(options);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
returnValue = facades.get(s);
|
|
79
|
-
break;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
case 'reject': {
|
|
83
|
-
s = s.reject();
|
|
84
|
-
|
|
85
|
-
returnValue = facades.get(s);
|
|
86
|
-
break;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
57
|
case 'advance': {
|
|
90
|
-
const { 0:
|
|
58
|
+
const { 0: embeddedTags } = args;
|
|
91
59
|
|
|
92
|
-
const tag =
|
|
60
|
+
const tag = getEmbeddedTag(embeddedTags);
|
|
93
61
|
|
|
94
62
|
if (
|
|
95
63
|
s.held &&
|
|
@@ -102,7 +70,7 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
102
70
|
case DoctypeTag: {
|
|
103
71
|
const { attributes } = tag.value;
|
|
104
72
|
|
|
105
|
-
if (s.path) {
|
|
73
|
+
if (s.path.depth !== 0) {
|
|
106
74
|
throw new Error();
|
|
107
75
|
}
|
|
108
76
|
|
|
@@ -124,56 +92,47 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
124
92
|
}
|
|
125
93
|
|
|
126
94
|
case ReferenceTag: {
|
|
127
|
-
const { name, isArray,
|
|
95
|
+
const { name, isArray, flags } = tag.value;
|
|
128
96
|
|
|
129
|
-
if (s.
|
|
130
|
-
throw new Error('A reference must have a non-reference value');
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (s.node?.flags.token) {
|
|
97
|
+
if (s.node?.flags.token && name !== '@') {
|
|
134
98
|
throw new Error('A token node cannot contain a reference');
|
|
135
99
|
}
|
|
136
100
|
|
|
137
|
-
returnValue = s.advance(buildReferenceTag(name, isArray,
|
|
101
|
+
returnValue = s.advance(buildReferenceTag(name, isArray, flags));
|
|
138
102
|
break;
|
|
139
103
|
}
|
|
140
104
|
|
|
141
105
|
case GapTag: {
|
|
142
|
-
const reference = s.result;
|
|
143
|
-
|
|
144
|
-
if (reference?.type !== ReferenceTag) throw new Error();
|
|
145
|
-
|
|
146
106
|
returnValue = s.advance(buildGapTag());
|
|
147
107
|
break;
|
|
148
108
|
}
|
|
149
109
|
|
|
150
110
|
case NullTag: {
|
|
151
|
-
const reference = s.result;
|
|
152
|
-
|
|
153
|
-
if (reference?.type !== ReferenceTag) throw new Error();
|
|
154
|
-
|
|
155
111
|
returnValue = s.advance(buildNullTag());
|
|
156
112
|
break;
|
|
157
113
|
}
|
|
158
114
|
|
|
159
|
-
case
|
|
160
|
-
const reference = s
|
|
115
|
+
case ArrayInitializerTag: {
|
|
116
|
+
const { reference } = s;
|
|
161
117
|
|
|
162
118
|
if (reference?.type !== ReferenceTag) throw new Error();
|
|
163
119
|
if (!reference.value.isArray) throw new Error();
|
|
164
120
|
|
|
165
|
-
returnValue = s.advance(
|
|
121
|
+
returnValue = s.advance(buildArrayInitializerTag());
|
|
166
122
|
break;
|
|
167
123
|
}
|
|
168
124
|
|
|
169
125
|
case ShiftTag: {
|
|
170
|
-
const
|
|
126
|
+
const { index } = tag.value;
|
|
127
|
+
if (s.resultPath.tag.type !== GapTag) throw new Error();
|
|
128
|
+
|
|
129
|
+
const refPath = s.resultPath.previousSibling;
|
|
171
130
|
|
|
172
|
-
if (!
|
|
131
|
+
if (!refPath.tag.value.flags.expression) {
|
|
173
132
|
throw new Error();
|
|
174
133
|
}
|
|
175
134
|
|
|
176
|
-
returnValue = s.advance(buildShiftTag());
|
|
135
|
+
returnValue = s.advance(buildShiftTag(index));
|
|
177
136
|
break;
|
|
178
137
|
}
|
|
179
138
|
|
|
@@ -184,32 +143,12 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
184
143
|
throw new Error('Expected an absolute-language tag');
|
|
185
144
|
}
|
|
186
145
|
|
|
187
|
-
returnValue = s.advance(
|
|
188
|
-
buildNodeOpenTag(flags, language, type, attributes),
|
|
189
|
-
getEmbeddedExpression(advanceOptions),
|
|
190
|
-
);
|
|
191
|
-
break;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
case OpenFragmentTag: {
|
|
195
|
-
const { flags } = tag.value;
|
|
196
|
-
|
|
197
|
-
returnValue = s.advance(
|
|
198
|
-
buildFragmentOpenTag(flags),
|
|
199
|
-
getEmbeddedExpression(advanceOptions),
|
|
200
|
-
);
|
|
146
|
+
returnValue = s.advance(buildOpenNodeTag(flags, language, type, attributes));
|
|
201
147
|
break;
|
|
202
148
|
}
|
|
203
149
|
|
|
204
150
|
case CloseNodeTag: {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
returnValue = s.advance(buildNodeCloseTag(type, language));
|
|
208
|
-
break;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
case CloseFragmentTag: {
|
|
212
|
-
returnValue = s.advance(buildFragmentCloseTag());
|
|
151
|
+
returnValue = s.advance(buildCloseNodeTag());
|
|
213
152
|
break;
|
|
214
153
|
}
|
|
215
154
|
|
|
@@ -217,90 +156,11 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
217
156
|
throw new Error();
|
|
218
157
|
}
|
|
219
158
|
|
|
220
|
-
yield
|
|
159
|
+
yield returnValue;
|
|
221
160
|
|
|
222
161
|
break;
|
|
223
162
|
}
|
|
224
163
|
|
|
225
|
-
case 'bindAttribute': {
|
|
226
|
-
const { 0: key, 1: value } = args;
|
|
227
|
-
|
|
228
|
-
const { unboundAttributes } = s;
|
|
229
|
-
|
|
230
|
-
if (!unboundAttributes || !unboundAttributes.has(key)) {
|
|
231
|
-
throw new Error('No unbound attribute to bind');
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (!s.node.type) {
|
|
235
|
-
throw new Error('Cannot bind attribute to fragment');
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
if (key === 'span') throw new Error('too late');
|
|
239
|
-
|
|
240
|
-
// if (stateIsDifferent) {
|
|
241
|
-
// // we can't allow effects to cross state branches
|
|
242
|
-
// throw new Error();
|
|
243
|
-
// }
|
|
244
|
-
|
|
245
|
-
unboundAttributes.delete(key);
|
|
246
|
-
|
|
247
|
-
const openTag = getOpenTag(s.node);
|
|
248
|
-
|
|
249
|
-
if (value != null) {
|
|
250
|
-
const { flags, language, type } = openTag.value;
|
|
251
|
-
const attributes = { ...openTag.value.attributes, [key]: value };
|
|
252
|
-
const newOpenTag = buildNodeOpenTag(flags, language, type, attributes);
|
|
253
|
-
|
|
254
|
-
let openNext = ctx.nextTags.get(openTag);
|
|
255
|
-
let startPrev = ctx.prevTags.get(openTag);
|
|
256
|
-
|
|
257
|
-
ctx.prevTags.set(newOpenTag, startPrev);
|
|
258
|
-
ctx.nextTags.set(startPrev, newOpenTag);
|
|
259
|
-
|
|
260
|
-
if (s.node !== s.tagNodes.get(openTag)) throw new Error();
|
|
261
|
-
if (s.path !== s.tagPaths.get(openTag)) throw new Error();
|
|
262
|
-
|
|
263
|
-
s.node.attributes = attributes;
|
|
264
|
-
|
|
265
|
-
s.tagNodes.set(newOpenTag, s.node);
|
|
266
|
-
s.tagPaths.set(newOpenTag, s.path);
|
|
267
|
-
|
|
268
|
-
if (openNext) {
|
|
269
|
-
ctx.nextTags.set(newOpenTag, openNext);
|
|
270
|
-
ctx.prevTags.set(openNext, newOpenTag);
|
|
271
|
-
} else {
|
|
272
|
-
// could this tag be stored anywhere else?
|
|
273
|
-
s.result = newOpenTag;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
s.node.children = btree.replaceAt(0, s.node.children, newOpenTag);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
if (!unboundAttributes.size) {
|
|
280
|
-
yield* s.emit(options);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
returnValue = getRange(s.node);
|
|
284
|
-
break;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
case 'getState': {
|
|
288
|
-
returnValue = facades.get(s);
|
|
289
|
-
break;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
case 'getContext': {
|
|
293
|
-
returnValue = facades.get(ctx);
|
|
294
|
-
break;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
case 'write': {
|
|
298
|
-
if (options.emitEffects) {
|
|
299
|
-
yield buildWriteEffect(args[0], args[1].value);
|
|
300
|
-
}
|
|
301
|
-
break;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
164
|
default: {
|
|
305
165
|
throw new Error(`Unexpected call of {type: ${printExpression(verb)}}`);
|
|
306
166
|
}
|
|
@@ -313,9 +173,9 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
313
173
|
throw new Error('Did not unwind state stack');
|
|
314
174
|
}
|
|
315
175
|
|
|
316
|
-
if (s.path
|
|
176
|
+
if (s.path) {
|
|
317
177
|
throw new Error('Did not unwind path stack');
|
|
318
178
|
}
|
|
319
179
|
|
|
320
|
-
return s.
|
|
321
|
-
}
|
|
180
|
+
return s.resultPath.path.node;
|
|
181
|
+
}
|
package/lib/index.js
CHANGED
package/lib/path.js
CHANGED
|
@@ -1,76 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
import { DoctypeTag, ReferenceTag } from '@bablr/agast-helpers/symbols';
|
|
3
|
-
import { skipToDepth, buildSkips } from './utils/skip.js';
|
|
4
|
-
import { facades, actuals } from './facades.js';
|
|
1
|
+
export { Path } from '@bablr/agast-helpers/path';
|
|
5
2
|
|
|
6
|
-
export
|
|
7
|
-
constructor(path) {
|
|
8
|
-
facades.set(path, this);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
get reference() {
|
|
12
|
-
return actuals.get(this).reference;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
get parent() {
|
|
16
|
-
return facades.get(actuals.get(this).parent);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
get depth() {
|
|
20
|
-
return actuals.get(this).depth;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
at(depth) {
|
|
24
|
-
return facades.get(actuals.get(this).at(depth));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
*parents(includeSelf = false) {
|
|
28
|
-
if (includeSelf) yield this;
|
|
29
|
-
let parent = this;
|
|
30
|
-
while ((parent = parent.parent)) {
|
|
31
|
-
yield parent;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export const Path = class AgastPath extends WeakStackFrame {
|
|
37
|
-
static from(context, tag, childrenIndex) {
|
|
38
|
-
return Path.create(context, tag, childrenIndex);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
constructor(context, reference, childrenIndex = -1) {
|
|
42
|
-
super();
|
|
43
|
-
|
|
44
|
-
if (reference && reference.type !== ReferenceTag && reference.type !== DoctypeTag) {
|
|
45
|
-
throw new Error('Invalid reference for path');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
this.context = context;
|
|
49
|
-
this.reference = reference;
|
|
50
|
-
this.childrenIndex = childrenIndex;
|
|
51
|
-
|
|
52
|
-
buildSkips(this);
|
|
53
|
-
|
|
54
|
-
new PathFacade(this);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
get name() {
|
|
58
|
-
return this.reference?.value.name;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
get isArray() {
|
|
62
|
-
return this.reference?.value.isArray || false;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
at(depth) {
|
|
66
|
-
return skipToDepth(depth, this);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
*parents(includeSelf = false) {
|
|
70
|
-
if (includeSelf) yield this;
|
|
71
|
-
let parent = this;
|
|
72
|
-
while ((parent = parent.parent)) {
|
|
73
|
-
yield parent;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
};
|
|
3
|
+
export { allTagPathsFor, ownTagPathsFor } from '@bablr/agast-helpers/path';
|
package/lib/state.js
CHANGED
|
@@ -1,25 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import isArray from 'iter-tools-es/methods/is-array';
|
|
2
2
|
import { WeakStackFrame } from '@bablr/weak-stack';
|
|
3
3
|
import {
|
|
4
4
|
Resolver,
|
|
5
5
|
add,
|
|
6
|
-
createNode,
|
|
7
|
-
getOpenTag,
|
|
8
|
-
getCloseTag,
|
|
9
|
-
branchNode,
|
|
10
|
-
acceptNode,
|
|
11
6
|
finalizeNode,
|
|
12
7
|
getRoot,
|
|
13
|
-
printType,
|
|
14
8
|
buildStubNode,
|
|
9
|
+
getRange,
|
|
10
|
+
buildNullTag,
|
|
11
|
+
treeFromStream,
|
|
12
|
+
createNode,
|
|
15
13
|
} from '@bablr/agast-helpers/tree';
|
|
16
14
|
import * as btree from '@bablr/agast-helpers/btree';
|
|
17
|
-
import {
|
|
18
|
-
buildBeginningOfStreamToken,
|
|
19
|
-
buildEmbeddedNode,
|
|
20
|
-
buildEffect,
|
|
21
|
-
buildYieldEffect,
|
|
22
|
-
} from '@bablr/agast-vm-helpers/internal-builders';
|
|
15
|
+
import { TagPath, updatePath } from '@bablr/agast-helpers/path';
|
|
23
16
|
import {
|
|
24
17
|
DoctypeTag,
|
|
25
18
|
OpenNodeTag,
|
|
@@ -28,25 +21,12 @@ import {
|
|
|
28
21
|
ShiftTag,
|
|
29
22
|
GapTag,
|
|
30
23
|
NullTag,
|
|
31
|
-
|
|
24
|
+
ArrayInitializerTag,
|
|
32
25
|
LiteralTag,
|
|
33
|
-
OpenFragmentTag,
|
|
34
|
-
CloseFragmentTag,
|
|
35
26
|
} from '@bablr/agast-helpers/symbols';
|
|
36
27
|
import { facades, actuals } from './facades.js';
|
|
37
28
|
import { Path } from './path.js';
|
|
38
|
-
import {
|
|
39
|
-
|
|
40
|
-
const { hasOwn } = Object;
|
|
41
|
-
|
|
42
|
-
const createNodeWithState = (openTag, options = {}) => {
|
|
43
|
-
const { unboundAttributes } = options;
|
|
44
|
-
const node = createNode(openTag);
|
|
45
|
-
nodeStates.set(node, {
|
|
46
|
-
unboundAttributes: new Set(unboundAttributes || []),
|
|
47
|
-
});
|
|
48
|
-
return node;
|
|
49
|
-
};
|
|
29
|
+
import { Coroutine } from '@bablr/coroutine';
|
|
50
30
|
|
|
51
31
|
export const StateFacade = class AgastStateFacade {
|
|
52
32
|
constructor(state) {
|
|
@@ -57,8 +37,16 @@ export const StateFacade = class AgastStateFacade {
|
|
|
57
37
|
return State.from(actuals.get(context));
|
|
58
38
|
}
|
|
59
39
|
|
|
60
|
-
get
|
|
61
|
-
return actuals.get(this).
|
|
40
|
+
get resultPath() {
|
|
41
|
+
return actuals.get(this).resultPath;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get reference() {
|
|
45
|
+
return actuals.get(this).reference;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get referencePath() {
|
|
49
|
+
return actuals.get(this).referencePath;
|
|
62
50
|
}
|
|
63
51
|
|
|
64
52
|
get context() {
|
|
@@ -66,7 +54,7 @@ export const StateFacade = class AgastStateFacade {
|
|
|
66
54
|
}
|
|
67
55
|
|
|
68
56
|
get path() {
|
|
69
|
-
return
|
|
57
|
+
return actuals.get(this).path;
|
|
70
58
|
}
|
|
71
59
|
|
|
72
60
|
get node() {
|
|
@@ -89,16 +77,8 @@ export const StateFacade = class AgastStateFacade {
|
|
|
89
77
|
return this.context;
|
|
90
78
|
}
|
|
91
79
|
|
|
92
|
-
|
|
93
|
-
return
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
pathForTag(tag) {
|
|
97
|
-
return facades.get(actuals.get(this).pathForTag(tag));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
nodeForTag(tag) {
|
|
101
|
-
return actuals.get(this).nodeForTag(tag);
|
|
80
|
+
get parent() {
|
|
81
|
+
return facades.get(actuals.get(this).parent);
|
|
102
82
|
}
|
|
103
83
|
};
|
|
104
84
|
|
|
@@ -106,346 +86,230 @@ export const nodeStates = new WeakMap();
|
|
|
106
86
|
|
|
107
87
|
export const State = class AgastState extends WeakStackFrame {
|
|
108
88
|
constructor(
|
|
89
|
+
parent,
|
|
109
90
|
context,
|
|
110
|
-
expressions =
|
|
111
|
-
path =
|
|
112
|
-
node = null,
|
|
113
|
-
result = buildBeginningOfStreamToken(),
|
|
114
|
-
emitted = null,
|
|
91
|
+
expressions = [],
|
|
92
|
+
path = Path.from(createNode()),
|
|
115
93
|
held = null,
|
|
94
|
+
resultPath = null,
|
|
116
95
|
resolver = new Resolver(),
|
|
117
|
-
internalContext = {
|
|
118
|
-
pathNodes: new WeakMap(),
|
|
119
|
-
tagPaths: new WeakMap(),
|
|
120
|
-
tagNodes: new WeakMap(),
|
|
121
|
-
},
|
|
122
96
|
) {
|
|
123
|
-
super();
|
|
97
|
+
super(parent);
|
|
124
98
|
|
|
125
|
-
if (!context) throw new Error('invalid args to tagState');
|
|
99
|
+
if (!context || !path || !expressions) throw new Error('invalid args to tagState');
|
|
126
100
|
|
|
127
101
|
this.context = context;
|
|
128
|
-
this.expressions = expressions;
|
|
102
|
+
this.expressions = new Coroutine(expressions[Symbol.iterator]());
|
|
129
103
|
this.path = path;
|
|
130
|
-
this.node = node;
|
|
131
|
-
this.result = result;
|
|
132
|
-
this.emitted = emitted;
|
|
133
104
|
this.held = held;
|
|
105
|
+
this.resultPath = resultPath;
|
|
134
106
|
this.resolver = resolver;
|
|
135
|
-
|
|
107
|
+
|
|
108
|
+
if (!this.node) throw new Error();
|
|
136
109
|
|
|
137
110
|
new StateFacade(this);
|
|
138
111
|
}
|
|
139
112
|
|
|
140
113
|
static from(context, expressions = []) {
|
|
141
|
-
return State.create(context,
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
get pathNodes() {
|
|
145
|
-
return this.internalContext.pathNodes;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
get tagPaths() {
|
|
149
|
-
return this.internalContext.tagPaths;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
get tagNodes() {
|
|
153
|
-
return this.context.tagNodes;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
get unboundAttributes() {
|
|
157
|
-
return nodeStates.get(this.node).unboundAttributes;
|
|
114
|
+
return State.create(context, expressions);
|
|
158
115
|
}
|
|
159
116
|
|
|
160
117
|
get holding() {
|
|
161
118
|
return !!this.held;
|
|
162
119
|
}
|
|
163
120
|
|
|
164
|
-
|
|
165
|
-
return this.pathNodes.get(path);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
pathForTag(ref) {
|
|
169
|
-
return this.tagPaths.get(ref);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
nodeForTag(tag) {
|
|
173
|
-
return this.tagNodes.get(tag);
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
advance(tag, options = {}) {
|
|
121
|
+
advance(tag) {
|
|
177
122
|
const ctx = this.context;
|
|
178
|
-
const { prevTags, nextTags } = ctx;
|
|
179
123
|
|
|
180
|
-
if (tag)
|
|
181
|
-
if (prevTags.has(tag)) {
|
|
182
|
-
throw new Error('Double emit');
|
|
183
|
-
}
|
|
124
|
+
if (!tag) throw new Error();
|
|
184
125
|
|
|
185
|
-
|
|
186
|
-
this.result?.type === ReferenceTag &&
|
|
187
|
-
![OpenNodeTag, GapTag, NullTag, ArrayTag].includes(tag.type)
|
|
188
|
-
) {
|
|
189
|
-
throw new Error(`${printType(tag.type)} is not a valid reference target`);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
prevTags.set(tag, this.result);
|
|
193
|
-
nextTags.set(this.result, tag);
|
|
126
|
+
let targetPath = this.path;
|
|
194
127
|
|
|
195
|
-
|
|
128
|
+
switch (tag.type) {
|
|
129
|
+
case DoctypeTag: {
|
|
130
|
+
const { path, node } = this;
|
|
196
131
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
this.path = Path.from(ctx, tag);
|
|
132
|
+
node.children = btree.push(node.children, tag);
|
|
133
|
+
node.attributes = tag.value.attributes;
|
|
200
134
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
135
|
+
this.resolver.advance(tag);
|
|
136
|
+
updatePath(this.path, tag);
|
|
204
137
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
this.node = createNodeWithState(tag, options);
|
|
209
|
-
|
|
210
|
-
const reference = this.result;
|
|
138
|
+
targetPath = path;
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
211
141
|
|
|
212
|
-
|
|
142
|
+
case ReferenceTag: {
|
|
143
|
+
this.resolver.advance(tag);
|
|
213
144
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
reference.type !== ReferenceTag &&
|
|
217
|
-
reference.type !== ShiftTag &&
|
|
218
|
-
reference.type !== OpenNodeTag &&
|
|
219
|
-
!reference.value.type
|
|
220
|
-
) {
|
|
221
|
-
throw new Error('Invalid location for OpenNodeTag');
|
|
222
|
-
}
|
|
223
|
-
} else {
|
|
224
|
-
this.path = this.path.push(ctx, null, btree.getSum(this.node.children));
|
|
225
|
-
}
|
|
145
|
+
this.node.children = btree.push(this.node.children, tag);
|
|
146
|
+
updatePath(this.path, tag);
|
|
226
147
|
|
|
227
|
-
|
|
228
|
-
this.pathNodes.set(this.path, this.node);
|
|
148
|
+
const { hasGap } = tag.value;
|
|
229
149
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
break;
|
|
150
|
+
if (hasGap && !this.node.flags.hasGap) {
|
|
151
|
+
throw new Error('gap reference in gapless node');
|
|
233
152
|
}
|
|
234
153
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
this.node = createNodeWithState(tag, options);
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
238
156
|
|
|
239
|
-
|
|
157
|
+
case OpenNodeTag: {
|
|
158
|
+
const parentNode = this.node;
|
|
240
159
|
|
|
241
|
-
|
|
242
|
-
this.node.children = btree.push(this.node.children, reference);
|
|
160
|
+
targetPath = this.path;
|
|
243
161
|
|
|
162
|
+
if (!this.path.depth) {
|
|
163
|
+
this.node.flags = tag.value.flags;
|
|
244
164
|
this.node.children = btree.push(this.node.children, tag);
|
|
245
|
-
|
|
246
|
-
this.
|
|
247
|
-
this.
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
this.
|
|
251
|
-
|
|
165
|
+
this.node.type = tag.value.type;
|
|
166
|
+
this.node.language = tag.value.language;
|
|
167
|
+
this.node.attributes = tag.value.attributes || {};
|
|
168
|
+
} else {
|
|
169
|
+
let node = createNode(tag);
|
|
170
|
+
this.path = this.path.push(node, btree.getSum(parentNode.children) - 2);
|
|
171
|
+
add(parentNode, this.reference, node);
|
|
252
172
|
}
|
|
253
173
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const { flags, type: openType } = openTag.value;
|
|
257
|
-
const closeTag = tag;
|
|
258
|
-
const { type } = closeTag.value;
|
|
259
|
-
|
|
260
|
-
this.node.children = btree.push(this.node.children, tag);
|
|
261
|
-
|
|
262
|
-
if (this.node.unboundAttributes?.size)
|
|
263
|
-
throw new Error('Grammar failed to bind all attributes');
|
|
264
|
-
|
|
265
|
-
if (!type) throw new Error(`CloseNodeTag must have type`);
|
|
266
|
-
|
|
267
|
-
if (type !== openType)
|
|
268
|
-
throw new Error(
|
|
269
|
-
`Grammar close {type: ${printType(type)}} did not match open {type: ${printType(
|
|
270
|
-
openType,
|
|
271
|
-
)}}`,
|
|
272
|
-
);
|
|
273
|
-
|
|
274
|
-
if (!flags.escape && !flags.trivia) {
|
|
275
|
-
add(this.parentNode, this.path.reference, this.node);
|
|
276
|
-
} else if (this.parentNode) {
|
|
277
|
-
this.parentNode.children = btree.push(
|
|
278
|
-
this.parentNode.children,
|
|
279
|
-
buildEmbeddedNode(this.node),
|
|
280
|
-
);
|
|
281
|
-
}
|
|
174
|
+
this.resolver.advance(tag);
|
|
175
|
+
updatePath(this.path, tag);
|
|
282
176
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
finalizeNode(this.node);
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
287
179
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
180
|
+
case CloseNodeTag: {
|
|
181
|
+
this.resolver.advance(tag);
|
|
182
|
+
this.node.children = btree.push(this.node.children, tag);
|
|
183
|
+
updatePath(this.path, tag);
|
|
292
184
|
|
|
293
|
-
|
|
294
|
-
|
|
185
|
+
if (this.node.unboundAttributes?.size)
|
|
186
|
+
throw new Error('Grammar failed to bind all attributes');
|
|
295
187
|
|
|
296
|
-
|
|
188
|
+
finalizeNode(this.node);
|
|
297
189
|
|
|
298
|
-
|
|
299
|
-
|
|
190
|
+
this.path = this.path.parent;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
300
193
|
|
|
301
|
-
|
|
194
|
+
case GapTag: {
|
|
195
|
+
let target;
|
|
196
|
+
let lastTagPath = TagPath.from(this.path, -1);
|
|
197
|
+
let refPath = lastTagPath;
|
|
302
198
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
break;
|
|
199
|
+
while (refPath && refPath.tag.type !== ReferenceTag) {
|
|
200
|
+
refPath = refPath.previousSibling;
|
|
306
201
|
}
|
|
307
202
|
|
|
308
|
-
|
|
309
|
-
this.node.children = btree.push(this.node.children, tag);
|
|
310
|
-
|
|
311
|
-
const { isArray, name, hasGap } = tag.value;
|
|
312
|
-
|
|
313
|
-
if (hasGap && !this.node.flags.hasGap) {
|
|
314
|
-
throw new Error('gap reference in gapless node');
|
|
315
|
-
}
|
|
203
|
+
this.resolver.advance(tag);
|
|
316
204
|
|
|
317
|
-
|
|
318
|
-
this.node.properties[name] = [];
|
|
319
|
-
} else {
|
|
320
|
-
this.path = this.path.push(ctx, tag, btree.getSum(this.node.children));
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
this.tagPaths.set(tag, this.path);
|
|
324
|
-
break;
|
|
325
|
-
}
|
|
205
|
+
let wasHeld = this.held;
|
|
326
206
|
|
|
327
|
-
|
|
328
|
-
this.
|
|
207
|
+
if (this.held && lastTagPath.tag.type !== ShiftTag) {
|
|
208
|
+
target = this.held.node;
|
|
329
209
|
|
|
330
|
-
|
|
331
|
-
|
|
210
|
+
this.held = null;
|
|
211
|
+
} else if (refPath) {
|
|
212
|
+
this.expressions.advance();
|
|
332
213
|
|
|
333
|
-
if (
|
|
214
|
+
if (!this.expressions.done) {
|
|
215
|
+
const expression = this.expressions.value;
|
|
334
216
|
|
|
335
|
-
|
|
336
|
-
|
|
217
|
+
if (isArray(expression)) {
|
|
218
|
+
throw new Error('Invalid array interpolation');
|
|
219
|
+
}
|
|
337
220
|
|
|
338
|
-
|
|
221
|
+
if (expression == null) {
|
|
222
|
+
target = treeFromStream(buildNullTag());
|
|
223
|
+
} else {
|
|
224
|
+
target = getRoot(expression);
|
|
225
|
+
}
|
|
339
226
|
} else {
|
|
340
227
|
if (!this.node.flags.hasGap) throw new Error('Node must allow gaps');
|
|
341
228
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if (isArray(expression)) {
|
|
346
|
-
throw new Error('Invalid array interpolation');
|
|
347
|
-
} else {
|
|
348
|
-
target = expression != null ? getRoot(expression) : buildStubNode(tag);
|
|
229
|
+
target = buildStubNode(tag);
|
|
230
|
+
}
|
|
349
231
|
|
|
350
|
-
|
|
351
|
-
|
|
232
|
+
this.held = null;
|
|
233
|
+
} else {
|
|
234
|
+
this.node.language = null;
|
|
235
|
+
this.node.type = null;
|
|
236
|
+
target = this.node;
|
|
237
|
+
}
|
|
352
238
|
|
|
353
|
-
|
|
239
|
+
const range = getRange(target);
|
|
354
240
|
|
|
355
|
-
|
|
356
|
-
// the stream still contains a gap token, even if expressions were specified
|
|
357
|
-
// get rid of the gap token in the stream!
|
|
358
|
-
} else {
|
|
359
|
-
target = buildStubNode(tag);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
241
|
+
this.resultPath = range ? range[1] : this.resultPath;
|
|
362
242
|
|
|
363
|
-
|
|
243
|
+
if (refPath) {
|
|
244
|
+
add(this.path.node, refPath.tag, target, wasHeld);
|
|
245
|
+
}
|
|
364
246
|
|
|
365
|
-
|
|
366
|
-
|
|
247
|
+
updatePath(this.path, tag);
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
367
250
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
}
|
|
251
|
+
case NullTag: {
|
|
252
|
+
const { node: parentNode, reference } = this;
|
|
253
|
+
const { name } = reference.value;
|
|
371
254
|
|
|
372
|
-
|
|
373
|
-
|
|
255
|
+
this.resolver.advance(tag);
|
|
256
|
+
updatePath(this.path, tag);
|
|
374
257
|
|
|
375
|
-
|
|
376
|
-
const { isArray, name } = this.result.value;
|
|
258
|
+
if (!name) throw new Error();
|
|
377
259
|
|
|
378
|
-
|
|
260
|
+
let stubNode = buildStubNode(tag);
|
|
379
261
|
|
|
380
|
-
|
|
381
|
-
properties[name] = newNode;
|
|
382
|
-
}
|
|
262
|
+
add(parentNode, reference, stubNode);
|
|
383
263
|
|
|
384
|
-
|
|
264
|
+
targetPath = this.path.push(stubNode, btree.getSum(this.node.children) - 2);
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
385
267
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
268
|
+
case ArrayInitializerTag: {
|
|
269
|
+
const { node, reference } = this;
|
|
270
|
+
this.resolver.advance(tag);
|
|
389
271
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
const { properties } = this.node;
|
|
272
|
+
add(node, reference, []);
|
|
273
|
+
updatePath(this.path, tag);
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
395
276
|
|
|
396
|
-
|
|
277
|
+
case ShiftTag: {
|
|
278
|
+
this.resolver.advance(tag);
|
|
279
|
+
updatePath(this.path, tag);
|
|
397
280
|
|
|
398
|
-
|
|
281
|
+
const finishedPath = this.resultPath.innerPath;
|
|
282
|
+
const finishedNode = finishedPath.node;
|
|
283
|
+
const ref = this.resultPath.previousSibling.tag;
|
|
399
284
|
|
|
400
|
-
|
|
285
|
+
this.held = { node: finishedNode, path: finishedPath };
|
|
401
286
|
|
|
402
|
-
|
|
403
|
-
node = btree.getAt(-1, node);
|
|
404
|
-
properties[ref.value.name] = btree.pop(properties[ref.value.name]);
|
|
405
|
-
} else {
|
|
406
|
-
properties[ref.value.name] = null;
|
|
407
|
-
}
|
|
287
|
+
if (!ref.value.name) throw new Error();
|
|
408
288
|
|
|
409
|
-
|
|
410
|
-
break;
|
|
411
|
-
}
|
|
289
|
+
if (!ref.value.flags.expression) throw new Error();
|
|
412
290
|
|
|
413
|
-
|
|
414
|
-
case ArrayTag:
|
|
415
|
-
this.node.children = btree.push(this.node.children, tag);
|
|
416
|
-
break;
|
|
291
|
+
this.node.children = btree.push(this.node.children, tag);
|
|
417
292
|
|
|
418
|
-
|
|
419
|
-
|
|
293
|
+
// this.path = finishedPath;
|
|
294
|
+
targetPath = this.path;
|
|
295
|
+
break;
|
|
420
296
|
}
|
|
297
|
+
|
|
298
|
+
case LiteralTag:
|
|
299
|
+
this.resolver.advance(tag);
|
|
300
|
+
updatePath(this.path, tag);
|
|
301
|
+
this.node.children = btree.push(this.node.children, tag);
|
|
302
|
+
break;
|
|
303
|
+
|
|
304
|
+
default:
|
|
305
|
+
throw new Error();
|
|
421
306
|
}
|
|
422
307
|
|
|
423
|
-
this.
|
|
308
|
+
this.resultPath = TagPath.from(targetPath, -1);
|
|
424
309
|
|
|
425
310
|
return tag;
|
|
426
311
|
}
|
|
427
312
|
|
|
428
|
-
*emit(options) {
|
|
429
|
-
const { nextTags } = this.context;
|
|
430
|
-
if (!this.depth) {
|
|
431
|
-
let emittable = this.emitted ? nextTags.get(this.emitted) : this.result;
|
|
432
|
-
|
|
433
|
-
while (
|
|
434
|
-
emittable &&
|
|
435
|
-
!(
|
|
436
|
-
emittable.type === OpenNodeTag &&
|
|
437
|
-
emittable.value.type &&
|
|
438
|
-
nodeStates.get(this.nodeForTag(emittable)).unboundAttributes?.size
|
|
439
|
-
)
|
|
440
|
-
) {
|
|
441
|
-
yield options.emitEffects ? buildYieldEffect(emittable) : emittable;
|
|
442
|
-
|
|
443
|
-
this.emitted = emittable;
|
|
444
|
-
emittable = nextTags.get(this.emitted);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
313
|
get ctx() {
|
|
450
314
|
return this.context;
|
|
451
315
|
}
|
|
@@ -459,80 +323,18 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
459
323
|
}
|
|
460
324
|
|
|
461
325
|
get parentNode() {
|
|
462
|
-
return this.
|
|
326
|
+
return this.path.parent.node;
|
|
463
327
|
}
|
|
464
328
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
this;
|
|
468
|
-
const { pathNodes } = internalContext;
|
|
469
|
-
|
|
470
|
-
const newNode = node && branchNode(node);
|
|
471
|
-
|
|
472
|
-
const nodeState = nodeStates.get(node);
|
|
473
|
-
|
|
474
|
-
pathNodes.set(path, newNode);
|
|
475
|
-
pathNodes.set(newNode, path);
|
|
476
|
-
|
|
477
|
-
nodeStates.set(newNode, { ...nodeState });
|
|
478
|
-
|
|
479
|
-
const nodeOpen = getOpenTag(node);
|
|
480
|
-
const nodeClose = getCloseTag(node);
|
|
481
|
-
if (nodeOpen) this.tagNodes.set(nodeOpen, newNode);
|
|
482
|
-
if (nodeClose) this.tagNodes.set(nodeClose, newNode);
|
|
483
|
-
|
|
484
|
-
return this.push(
|
|
485
|
-
context,
|
|
486
|
-
expressions,
|
|
487
|
-
path,
|
|
488
|
-
newNode,
|
|
489
|
-
result,
|
|
490
|
-
emitted,
|
|
491
|
-
held,
|
|
492
|
-
resolver.branch(),
|
|
493
|
-
internalContext,
|
|
494
|
-
);
|
|
329
|
+
get reference() {
|
|
330
|
+
return this.resolver.reference;
|
|
495
331
|
}
|
|
496
332
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
if (!parent) {
|
|
501
|
-
return null;
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
if (this.node && parent.node) {
|
|
505
|
-
acceptNode(parent.node, this.node);
|
|
506
|
-
const nodeState = nodeStates.get(this.node);
|
|
507
|
-
Object.assign(nodeStates.get(parent.node), nodeState);
|
|
508
|
-
} else {
|
|
509
|
-
parent.node = this.node;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
// emitted isn't used here and probably doesn't need to be part of state
|
|
513
|
-
|
|
514
|
-
parent.expressions = this.expressions;
|
|
515
|
-
parent.result = this.result;
|
|
516
|
-
parent.held = this.held;
|
|
517
|
-
parent.path = this.path;
|
|
518
|
-
parent.node = this.node;
|
|
519
|
-
parent.resolver = this.resolver;
|
|
520
|
-
|
|
521
|
-
return parent;
|
|
333
|
+
get referencePath() {
|
|
334
|
+
return this.reference && this.path.referencePath;
|
|
522
335
|
}
|
|
523
336
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
if (!parent) throw new Error('rejected root state');
|
|
528
|
-
|
|
529
|
-
context.nextTags.delete(parent.result);
|
|
530
|
-
|
|
531
|
-
pathNodes.set(parent.path, parent.node);
|
|
532
|
-
|
|
533
|
-
if (getOpenTag(parent.node)) tagNodes.set(getOpenTag(parent.node), parent.node);
|
|
534
|
-
if (getCloseTag(parent.node)) tagNodes.set(getCloseTag(parent.node), parent.node);
|
|
535
|
-
|
|
536
|
-
return parent;
|
|
337
|
+
get node() {
|
|
338
|
+
return this.path?.node;
|
|
537
339
|
}
|
|
538
340
|
};
|
package/package.json
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
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.7.0",
|
|
5
5
|
"author": "Conrad Buck<conartist6@gmail.com>",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"files": [
|
|
7
|
+
"files": [
|
|
8
|
+
"lib"
|
|
9
|
+
],
|
|
8
10
|
"exports": {
|
|
9
11
|
".": "./lib/index.js"
|
|
10
12
|
},
|
|
11
13
|
"sideEffects": false,
|
|
12
14
|
"dependencies": {
|
|
13
|
-
"@bablr/agast-helpers": "
|
|
14
|
-
"@bablr/agast-vm-helpers": "
|
|
15
|
+
"@bablr/agast-helpers": "0.6.0",
|
|
16
|
+
"@bablr/agast-vm-helpers": "0.6.0",
|
|
15
17
|
"@bablr/coroutine": "0.1.0",
|
|
16
18
|
"@bablr/weak-stack": "0.1.0",
|
|
17
19
|
"@iter-tools/imm-stack": "1.1.0"
|