@bablr/agast-vm 0.4.2 → 0.5.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 +35 -34
- package/lib/evaluate.js +61 -43
- package/lib/path.js +6 -4
- package/lib/state.js +114 -87
- package/package.json +3 -4
package/lib/context.js
CHANGED
|
@@ -7,32 +7,33 @@ import {
|
|
|
7
7
|
getCooked as getCookedFromStream,
|
|
8
8
|
sourceTextFor as sourceTextForStream,
|
|
9
9
|
} from '@bablr/agast-helpers/stream';
|
|
10
|
+
import { OpenNodeTag, CloseNodeTag } from '@bablr/agast-helpers/symbols';
|
|
10
11
|
import { facades, actuals } from './facades.js';
|
|
11
12
|
|
|
12
13
|
const { isArray } = Array;
|
|
13
14
|
|
|
14
|
-
function*
|
|
15
|
+
function* allTagsFor(range, nextTags) {
|
|
15
16
|
if (!range) return;
|
|
16
17
|
const { 0: start, 1: end } = range;
|
|
17
18
|
|
|
18
|
-
const pastEnd =
|
|
19
|
+
const pastEnd = nextTags.get(end);
|
|
19
20
|
|
|
20
|
-
for (let tag = start; tag && tag !== pastEnd; tag =
|
|
21
|
+
for (let tag = start; tag && tag !== pastEnd; tag = nextTags.get(tag)) {
|
|
21
22
|
yield tag;
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export const ContextFacade = class AgastContextFacade {
|
|
26
|
-
|
|
27
|
-
return actuals.get(this).
|
|
27
|
+
getPreviousTag(token) {
|
|
28
|
+
return actuals.get(this).prevTags.get(token);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
return actuals.get(this).
|
|
31
|
+
getNextTag(token) {
|
|
32
|
+
return actuals.get(this).nextTags.get(token);
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
return actuals.get(this).
|
|
35
|
+
allTagsFor(range) {
|
|
36
|
+
return actuals.get(this).allTagsFor(range);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
getCooked(range) {
|
|
@@ -47,8 +48,8 @@ export const ContextFacade = class AgastContextFacade {
|
|
|
47
48
|
return actuals.get(this).sourceTextFor(range);
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
buildRange(
|
|
51
|
-
return actuals.get(this).buildRange(
|
|
51
|
+
buildRange(tags) {
|
|
52
|
+
return actuals.get(this).buildRange(tags);
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
unbox(value) {
|
|
@@ -62,8 +63,8 @@ export const Context = class AgastContext {
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
constructor() {
|
|
65
|
-
this.
|
|
66
|
-
this.
|
|
66
|
+
this.prevTags = new WeakMap();
|
|
67
|
+
this.nextTags = new WeakMap();
|
|
67
68
|
this.unboxedValues = new WeakMap();
|
|
68
69
|
this.facade = new ContextFacade();
|
|
69
70
|
|
|
@@ -73,9 +74,9 @@ export const Context = class AgastContext {
|
|
|
73
74
|
isEmpty(range) {
|
|
74
75
|
const { path, parent } = this;
|
|
75
76
|
|
|
76
|
-
if (range[0]?.type ===
|
|
77
|
-
const nextTag = this.
|
|
78
|
-
if (!nextTag || nextTag.type ===
|
|
77
|
+
if (range[0]?.type === OpenNodeTag && path !== parent.path) {
|
|
78
|
+
const nextTag = this.nextTags.get(range[0]);
|
|
79
|
+
if (!nextTag || nextTag.type === CloseNodeTag) {
|
|
79
80
|
return null;
|
|
80
81
|
}
|
|
81
82
|
} else {
|
|
@@ -83,44 +84,44 @@ export const Context = class AgastContext {
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
return
|
|
87
|
+
allTagsFor(range) {
|
|
88
|
+
return allTagsFor(range, this.nextTags);
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
return
|
|
91
|
+
allTagsReverseFor(range) {
|
|
92
|
+
return allTagsFor([...range].reverse(), this.prevTags);
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
|
|
95
|
-
return this.
|
|
95
|
+
getPreviousTag(token) {
|
|
96
|
+
return this.prevTags.get(token);
|
|
96
97
|
}
|
|
97
98
|
|
|
98
|
-
|
|
99
|
-
return this.
|
|
99
|
+
getNextTag(token) {
|
|
100
|
+
return this.nextTags.get(token);
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
sourceTextFor(nodeOrRange) {
|
|
103
104
|
return isArray(nodeOrRange)
|
|
104
|
-
? sourceTextForStream(this.
|
|
105
|
+
? sourceTextForStream(this.allTagsFor(nodeOrRange))
|
|
105
106
|
: sourceTextForTree(nodeOrRange);
|
|
106
107
|
}
|
|
107
108
|
|
|
108
|
-
buildRange(
|
|
109
|
-
const {
|
|
109
|
+
buildRange(tags) {
|
|
110
|
+
const { prevTags, nextTags } = this;
|
|
110
111
|
|
|
111
112
|
let start, end;
|
|
112
|
-
for (const
|
|
113
|
-
if (
|
|
113
|
+
for (const tag of tags) {
|
|
114
|
+
if (prevTags.has(tag) || nextTags.has(tag)) {
|
|
114
115
|
throw new Error('buildRange must not overwrite linkages');
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
if (end) {
|
|
118
|
-
|
|
119
|
-
|
|
119
|
+
prevTags.set(tag, end);
|
|
120
|
+
nextTags.set(end, tag);
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
start = start ||
|
|
123
|
-
end =
|
|
123
|
+
start = start || tag;
|
|
124
|
+
end = tag || end;
|
|
124
125
|
}
|
|
125
126
|
return start ? [start, end] : null;
|
|
126
127
|
}
|
|
@@ -140,7 +141,7 @@ export const Context = class AgastContext {
|
|
|
140
141
|
|
|
141
142
|
getCooked(nodeOrRange) {
|
|
142
143
|
return isArray(nodeOrRange)
|
|
143
|
-
? getCookedFromStream(this.
|
|
144
|
+
? getCookedFromStream(this.allTagsFor(nodeOrRange))
|
|
144
145
|
: getCookedFromTree(nodeOrRange);
|
|
145
146
|
}
|
|
146
147
|
};
|
package/lib/evaluate.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Coroutine } from '@bablr/coroutine';
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
buildNullTag,
|
|
4
|
+
buildGapTag,
|
|
5
|
+
buildShiftTag,
|
|
6
|
+
buildReferenceTag,
|
|
7
|
+
buildLiteralTag,
|
|
8
8
|
buildWriteEffect,
|
|
9
9
|
buildDoctypeTag,
|
|
10
10
|
buildNodeOpenTag,
|
|
@@ -13,7 +13,19 @@ import {
|
|
|
13
13
|
import { getEmbeddedExpression } from '@bablr/agast-vm-helpers/deembed';
|
|
14
14
|
import { StreamIterable, getStreamIterator } from '@bablr/agast-helpers/stream';
|
|
15
15
|
import { printExpression } from '@bablr/agast-helpers/print';
|
|
16
|
-
import { getRange, getOpenTag } from '@bablr/agast-helpers/tree';
|
|
16
|
+
import { getRange, getOpenTag, buildArrayTag } from '@bablr/agast-helpers/tree';
|
|
17
|
+
import {
|
|
18
|
+
DoctypeTag,
|
|
19
|
+
OpenNodeTag,
|
|
20
|
+
CloseNodeTag,
|
|
21
|
+
ReferenceTag,
|
|
22
|
+
ShiftTag,
|
|
23
|
+
GapTag,
|
|
24
|
+
NullTag,
|
|
25
|
+
ArrayTag,
|
|
26
|
+
LiteralTag,
|
|
27
|
+
} from '@bablr/agast-helpers/symbols';
|
|
28
|
+
import * as btree from '@bablr/agast-helpers/btree';
|
|
17
29
|
import { State } from './state.js';
|
|
18
30
|
import { facades } from './facades.js';
|
|
19
31
|
|
|
@@ -67,24 +79,20 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
67
79
|
}
|
|
68
80
|
|
|
69
81
|
case 'advance': {
|
|
70
|
-
const { 0:
|
|
82
|
+
const { 0: embeddedTag, 1: options } = args;
|
|
71
83
|
|
|
72
|
-
const
|
|
84
|
+
const tag = embeddedTag.value;
|
|
73
85
|
|
|
74
86
|
if (
|
|
75
87
|
s.held &&
|
|
76
|
-
!(
|
|
77
|
-
terminal.type === 'OpenNodeTag' ||
|
|
78
|
-
terminal.type === 'Reference' ||
|
|
79
|
-
terminal.type === 'Gap'
|
|
80
|
-
)
|
|
88
|
+
!(tag.type === OpenNodeTag || tag.type === ReferenceTag || tag.type === GapTag)
|
|
81
89
|
) {
|
|
82
90
|
throw new Error('Cannot advance while holding');
|
|
83
91
|
}
|
|
84
92
|
|
|
85
|
-
switch (
|
|
86
|
-
case
|
|
87
|
-
const { attributes } =
|
|
93
|
+
switch (tag?.type || NullTag) {
|
|
94
|
+
case DoctypeTag: {
|
|
95
|
+
const { attributes } = tag.value;
|
|
88
96
|
|
|
89
97
|
if (s.path) {
|
|
90
98
|
throw new Error();
|
|
@@ -94,7 +102,7 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
94
102
|
break;
|
|
95
103
|
}
|
|
96
104
|
|
|
97
|
-
case
|
|
105
|
+
case LiteralTag: {
|
|
98
106
|
if (!s.node.flags.token) {
|
|
99
107
|
throw new Error('literals must occur inside tokens');
|
|
100
108
|
}
|
|
@@ -103,14 +111,14 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
103
111
|
throw new Error('Cannot consume input while hold register is full');
|
|
104
112
|
}
|
|
105
113
|
|
|
106
|
-
returnValue = s.advance(
|
|
114
|
+
returnValue = s.advance(buildLiteralTag(tag.value));
|
|
107
115
|
break;
|
|
108
116
|
}
|
|
109
117
|
|
|
110
|
-
case
|
|
111
|
-
const { name, isArray } =
|
|
118
|
+
case ReferenceTag: {
|
|
119
|
+
const { name, isArray } = tag.value;
|
|
112
120
|
|
|
113
|
-
if (s.result.type ===
|
|
121
|
+
if (s.result.type === ReferenceTag) {
|
|
114
122
|
throw new Error('A reference must have a non-reference value');
|
|
115
123
|
}
|
|
116
124
|
|
|
@@ -118,41 +126,51 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
118
126
|
throw new Error('A token node cannot contain a reference');
|
|
119
127
|
}
|
|
120
128
|
|
|
121
|
-
returnValue = s.advance(
|
|
129
|
+
returnValue = s.advance(buildReferenceTag(name, isArray));
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
case GapTag: {
|
|
134
|
+
const reference = s.result;
|
|
135
|
+
|
|
136
|
+
if (reference?.type !== ReferenceTag) throw new Error();
|
|
137
|
+
|
|
138
|
+
returnValue = s.advance(buildGapTag());
|
|
122
139
|
break;
|
|
123
140
|
}
|
|
124
141
|
|
|
125
|
-
case
|
|
142
|
+
case NullTag: {
|
|
126
143
|
const reference = s.result;
|
|
127
144
|
|
|
128
|
-
if (reference?.type !==
|
|
145
|
+
if (reference?.type !== ReferenceTag) throw new Error();
|
|
129
146
|
|
|
130
|
-
returnValue = s.advance(
|
|
147
|
+
returnValue = s.advance(buildNullTag());
|
|
131
148
|
break;
|
|
132
149
|
}
|
|
133
150
|
|
|
134
|
-
case
|
|
151
|
+
case ArrayTag: {
|
|
135
152
|
const reference = s.result;
|
|
136
153
|
|
|
137
|
-
if (reference?.type !==
|
|
154
|
+
if (reference?.type !== ReferenceTag) throw new Error();
|
|
155
|
+
if (!reference.value.isArray) throw new Error();
|
|
138
156
|
|
|
139
|
-
returnValue = s.advance(
|
|
157
|
+
returnValue = s.advance(buildArrayTag());
|
|
140
158
|
break;
|
|
141
159
|
}
|
|
142
160
|
|
|
143
|
-
case
|
|
161
|
+
case ShiftTag: {
|
|
144
162
|
const finishedNode = s.nodeForTag(s.result);
|
|
145
163
|
|
|
146
164
|
if (!getOpenTag(finishedNode).value.flags.expression) {
|
|
147
165
|
throw new Error();
|
|
148
166
|
}
|
|
149
167
|
|
|
150
|
-
returnValue = s.advance(
|
|
168
|
+
returnValue = s.advance(buildShiftTag());
|
|
151
169
|
break;
|
|
152
170
|
}
|
|
153
171
|
|
|
154
|
-
case
|
|
155
|
-
const { flags, language, type, attributes } =
|
|
172
|
+
case OpenNodeTag: {
|
|
173
|
+
const { flags, language, type, attributes } = tag.value;
|
|
156
174
|
|
|
157
175
|
if (language && !language.startsWith('https://')) {
|
|
158
176
|
throw new Error('Expected an absolute-language tag');
|
|
@@ -165,8 +183,8 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
165
183
|
break;
|
|
166
184
|
}
|
|
167
185
|
|
|
168
|
-
case
|
|
169
|
-
const { type, language } =
|
|
186
|
+
case CloseNodeTag: {
|
|
187
|
+
const { type, language } = tag.value;
|
|
170
188
|
|
|
171
189
|
returnValue = s.advance(buildNodeCloseTag(type, language));
|
|
172
190
|
break;
|
|
@@ -207,18 +225,18 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
207
225
|
|
|
208
226
|
unboundAttributes.delete(key);
|
|
209
227
|
|
|
210
|
-
const openTag = s.node
|
|
228
|
+
const openTag = getOpenTag(s.node);
|
|
211
229
|
|
|
212
230
|
if (value != null) {
|
|
213
231
|
const { flags, language, type } = openTag.value;
|
|
214
232
|
const attributes = { ...openTag.value.attributes, [key]: value };
|
|
215
233
|
const newOpenTag = buildNodeOpenTag(flags, language, type, attributes);
|
|
216
234
|
|
|
217
|
-
let openNext = ctx.
|
|
218
|
-
let startPrev = ctx.
|
|
235
|
+
let openNext = ctx.nextTags.get(openTag);
|
|
236
|
+
let startPrev = ctx.prevTags.get(openTag);
|
|
219
237
|
|
|
220
|
-
ctx.
|
|
221
|
-
ctx.
|
|
238
|
+
ctx.prevTags.set(newOpenTag, startPrev);
|
|
239
|
+
ctx.nextTags.set(startPrev, newOpenTag);
|
|
222
240
|
|
|
223
241
|
if (s.node !== s.tagNodes.get(openTag)) throw new Error();
|
|
224
242
|
if (s.path !== s.tagPaths.get(openTag)) throw new Error();
|
|
@@ -229,14 +247,14 @@ const __evaluate = function* agast(ctx, strategy, options = {}) {
|
|
|
229
247
|
s.tagPaths.set(newOpenTag, s.path);
|
|
230
248
|
|
|
231
249
|
if (openNext) {
|
|
232
|
-
ctx.
|
|
233
|
-
ctx.
|
|
250
|
+
ctx.nextTags.set(newOpenTag, openNext);
|
|
251
|
+
ctx.prevTags.set(openNext, newOpenTag);
|
|
234
252
|
} else {
|
|
235
|
-
// could this
|
|
253
|
+
// could this tag be stored anywhere else?
|
|
236
254
|
s.result = newOpenTag;
|
|
237
255
|
}
|
|
238
256
|
|
|
239
|
-
s.node.children
|
|
257
|
+
s.node.children = btree.replaceAt(0, s.node.children, newOpenTag);
|
|
240
258
|
}
|
|
241
259
|
|
|
242
260
|
if (!unboundAttributes.size) {
|
package/lib/path.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { WeakStackFrame } from '@bablr/weak-stack';
|
|
2
|
+
import { DoctypeTag, ReferenceTag } from '@bablr/agast-helpers/symbols';
|
|
2
3
|
import { skipToDepth, buildSkips } from './utils/skip.js';
|
|
3
4
|
import { facades, actuals } from './facades.js';
|
|
4
5
|
|
|
@@ -33,19 +34,20 @@ export const PathFacade = class AgastPathFacade {
|
|
|
33
34
|
};
|
|
34
35
|
|
|
35
36
|
export const Path = class AgastPath extends WeakStackFrame {
|
|
36
|
-
static from(context, tag) {
|
|
37
|
-
return Path.create(context, tag);
|
|
37
|
+
static from(context, tag, childrenIndex) {
|
|
38
|
+
return Path.create(context, tag, childrenIndex);
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
constructor(context, reference) {
|
|
41
|
+
constructor(context, reference, childrenIndex = -1) {
|
|
41
42
|
super();
|
|
42
43
|
|
|
43
|
-
if (reference && reference.type !==
|
|
44
|
+
if (reference && reference.type !== ReferenceTag && reference.type !== DoctypeTag) {
|
|
44
45
|
throw new Error('Invalid reference for path');
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
this.context = context;
|
|
48
49
|
this.reference = reference;
|
|
50
|
+
this.childrenIndex = childrenIndex;
|
|
49
51
|
|
|
50
52
|
buildSkips(this);
|
|
51
53
|
|
package/lib/state.js
CHANGED
|
@@ -10,13 +10,26 @@ import {
|
|
|
10
10
|
acceptNode,
|
|
11
11
|
finalizeNode,
|
|
12
12
|
getRoot,
|
|
13
|
+
printType,
|
|
13
14
|
} from '@bablr/agast-helpers/tree';
|
|
15
|
+
import * as btree from '@bablr/agast-helpers/btree';
|
|
14
16
|
import {
|
|
15
17
|
buildBeginningOfStreamToken,
|
|
16
18
|
buildEmbeddedNode,
|
|
17
19
|
nodeFlags,
|
|
18
20
|
} from '@bablr/agast-vm-helpers/internal-builders';
|
|
19
21
|
import * as sym from '@bablr/agast-helpers/symbols';
|
|
22
|
+
import {
|
|
23
|
+
DoctypeTag,
|
|
24
|
+
OpenNodeTag,
|
|
25
|
+
CloseNodeTag,
|
|
26
|
+
ReferenceTag,
|
|
27
|
+
ShiftTag,
|
|
28
|
+
GapTag,
|
|
29
|
+
NullTag,
|
|
30
|
+
ArrayTag,
|
|
31
|
+
LiteralTag,
|
|
32
|
+
} from '@bablr/agast-helpers/symbols';
|
|
20
33
|
import { facades, actuals } from './facades.js';
|
|
21
34
|
import { Path } from './path.js';
|
|
22
35
|
|
|
@@ -28,7 +41,6 @@ const createNodeWithState = (startTag, options = {}) => {
|
|
|
28
41
|
const { unboundAttributes } = options;
|
|
29
42
|
const node = createNode(startTag);
|
|
30
43
|
nodeStates.set(node, {
|
|
31
|
-
resolver: new Resolver(node),
|
|
32
44
|
unboundAttributes: new Set(unboundAttributes || []),
|
|
33
45
|
});
|
|
34
46
|
return node;
|
|
@@ -37,21 +49,23 @@ const createNodeWithState = (startTag, options = {}) => {
|
|
|
37
49
|
const symbolTypeFor = (type) => {
|
|
38
50
|
// prettier-ignore
|
|
39
51
|
switch (type) {
|
|
40
|
-
case
|
|
41
|
-
case
|
|
52
|
+
case NullTag: return sym.null;
|
|
53
|
+
case GapTag: return sym.gap;
|
|
42
54
|
default: throw new Error();
|
|
43
55
|
}
|
|
44
56
|
};
|
|
45
57
|
|
|
58
|
+
const { freeze } = Object;
|
|
59
|
+
|
|
46
60
|
const buildStubNode = (tag) => {
|
|
47
|
-
return {
|
|
61
|
+
return freeze({
|
|
48
62
|
flags: nodeFlags,
|
|
49
63
|
language: null,
|
|
50
64
|
type: symbolTypeFor(tag.type),
|
|
51
|
-
children: [tag],
|
|
52
|
-
properties: {},
|
|
53
|
-
attributes: {},
|
|
54
|
-
};
|
|
65
|
+
children: freeze([tag]),
|
|
66
|
+
properties: freeze({}),
|
|
67
|
+
attributes: freeze({}),
|
|
68
|
+
});
|
|
55
69
|
};
|
|
56
70
|
|
|
57
71
|
export const StateFacade = class AgastStateFacade {
|
|
@@ -119,6 +133,7 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
119
133
|
result = buildBeginningOfStreamToken(),
|
|
120
134
|
emitted = null,
|
|
121
135
|
held = null,
|
|
136
|
+
resolver = new Resolver(),
|
|
122
137
|
internalContext = {
|
|
123
138
|
pathNodes: new WeakMap(),
|
|
124
139
|
tagPaths: new WeakMap(),
|
|
@@ -136,6 +151,7 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
136
151
|
this.result = result;
|
|
137
152
|
this.emitted = emitted;
|
|
138
153
|
this.held = held;
|
|
154
|
+
this.resolver = resolver;
|
|
139
155
|
this.internalContext = internalContext;
|
|
140
156
|
|
|
141
157
|
new StateFacade(this);
|
|
@@ -161,10 +177,6 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
161
177
|
return nodeStates.get(this.node).unboundAttributes;
|
|
162
178
|
}
|
|
163
179
|
|
|
164
|
-
get resolver() {
|
|
165
|
-
return nodeStates.get(this.node).resolver;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
180
|
get holding() {
|
|
169
181
|
return !!this.held;
|
|
170
182
|
}
|
|
@@ -181,56 +193,58 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
181
193
|
return this.tagNodes.get(tag);
|
|
182
194
|
}
|
|
183
195
|
|
|
184
|
-
advance(
|
|
196
|
+
advance(tag, options = {}) {
|
|
185
197
|
const ctx = this.context;
|
|
186
|
-
const {
|
|
198
|
+
const { prevTags, nextTags } = ctx;
|
|
187
199
|
|
|
188
|
-
if (
|
|
189
|
-
if (
|
|
200
|
+
if (tag) {
|
|
201
|
+
if (prevTags.has(tag)) {
|
|
190
202
|
throw new Error('Double emit');
|
|
191
203
|
}
|
|
192
204
|
|
|
193
205
|
if (
|
|
194
|
-
this.result?.type ===
|
|
195
|
-
![
|
|
206
|
+
this.result?.type === ReferenceTag &&
|
|
207
|
+
![OpenNodeTag, GapTag, NullTag, ArrayTag].includes(tag.type)
|
|
196
208
|
) {
|
|
197
|
-
throw new Error(`${
|
|
209
|
+
throw new Error(`${tag.type} is not a valid reference target`);
|
|
198
210
|
}
|
|
199
211
|
|
|
200
|
-
|
|
201
|
-
|
|
212
|
+
prevTags.set(tag, this.result);
|
|
213
|
+
nextTags.set(this.result, tag);
|
|
214
|
+
|
|
215
|
+
this.resolver.advance(tag);
|
|
202
216
|
|
|
203
|
-
switch (
|
|
204
|
-
case
|
|
205
|
-
this.path = Path.from(ctx,
|
|
217
|
+
switch (tag.type) {
|
|
218
|
+
case DoctypeTag: {
|
|
219
|
+
this.path = Path.from(ctx, tag);
|
|
206
220
|
|
|
207
|
-
this.tagPaths.set(
|
|
221
|
+
this.tagPaths.set(tag, this.path);
|
|
208
222
|
break;
|
|
209
223
|
}
|
|
210
224
|
|
|
211
|
-
case
|
|
212
|
-
const openTag =
|
|
213
|
-
const { type, flags } =
|
|
214
|
-
this.node = createNodeWithState(
|
|
225
|
+
case OpenNodeTag: {
|
|
226
|
+
const openTag = tag;
|
|
227
|
+
const { type, flags } = tag.value;
|
|
228
|
+
this.node = createNodeWithState(tag, options);
|
|
215
229
|
|
|
216
230
|
const reference = this.result;
|
|
217
231
|
|
|
218
|
-
this.node.children.push(
|
|
232
|
+
this.node.children = btree.push(this.node.children, tag);
|
|
219
233
|
|
|
220
234
|
if (!type) {
|
|
221
235
|
this.node.attributes = this.result.value.attributes;
|
|
222
236
|
} else {
|
|
223
237
|
if (!flags.trivia && !flags.escape) {
|
|
224
238
|
if (
|
|
225
|
-
reference.type !==
|
|
226
|
-
reference.type !==
|
|
227
|
-
reference.type !==
|
|
239
|
+
reference.type !== ReferenceTag &&
|
|
240
|
+
reference.type !== ShiftTag &&
|
|
241
|
+
reference.type !== OpenNodeTag &&
|
|
228
242
|
!reference.value.type
|
|
229
243
|
) {
|
|
230
244
|
throw new Error('Invalid location for OpenNodeTag');
|
|
231
245
|
}
|
|
232
246
|
} else {
|
|
233
|
-
this.path = this.path.push(ctx, null);
|
|
247
|
+
this.path = this.path.push(ctx, null, btree.getSum(this.node.children));
|
|
234
248
|
}
|
|
235
249
|
}
|
|
236
250
|
|
|
@@ -242,13 +256,13 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
242
256
|
break;
|
|
243
257
|
}
|
|
244
258
|
|
|
245
|
-
case
|
|
246
|
-
const openTag = this.node
|
|
259
|
+
case CloseNodeTag: {
|
|
260
|
+
const openTag = getOpenTag(this.node);
|
|
247
261
|
const { flags, type: openType } = openTag.value;
|
|
248
|
-
const closeTag =
|
|
262
|
+
const closeTag = tag;
|
|
249
263
|
const { type } = closeTag.value;
|
|
250
264
|
|
|
251
|
-
this.node.children.push(
|
|
265
|
+
this.node.children = btree.push(this.node.children, tag);
|
|
252
266
|
|
|
253
267
|
if (openType) {
|
|
254
268
|
if (this.node.unboundAttributes?.size)
|
|
@@ -258,24 +272,18 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
258
272
|
|
|
259
273
|
if (type !== openType)
|
|
260
274
|
throw new Error(
|
|
261
|
-
`Grammar close {type: ${type}} did not match open {type: ${
|
|
275
|
+
`Grammar close {type: ${printType(type)}} did not match open {type: ${printType(
|
|
276
|
+
openType,
|
|
277
|
+
)}}`,
|
|
262
278
|
);
|
|
263
279
|
|
|
264
280
|
if (!flags.escape && !flags.trivia) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const { properties } = this.parentNode;
|
|
268
|
-
|
|
269
|
-
if (!isArray) {
|
|
270
|
-
properties[refName] = this.node;
|
|
271
|
-
} else {
|
|
272
|
-
if (!hasOwn(properties, refName)) {
|
|
273
|
-
properties[refName] = [];
|
|
274
|
-
}
|
|
275
|
-
properties[refName].push(this.node);
|
|
276
|
-
}
|
|
281
|
+
add(this.parentNode, this.path.reference, this.node);
|
|
277
282
|
} else {
|
|
278
|
-
this.parentNode.children.push(
|
|
283
|
+
this.parentNode.children = btree.push(
|
|
284
|
+
this.parentNode.children,
|
|
285
|
+
buildEmbeddedNode(this.node),
|
|
286
|
+
);
|
|
279
287
|
}
|
|
280
288
|
}
|
|
281
289
|
|
|
@@ -289,26 +297,28 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
289
297
|
break;
|
|
290
298
|
}
|
|
291
299
|
|
|
292
|
-
case
|
|
293
|
-
|
|
294
|
-
nodeStates.get(this.node).resolver.consume(terminal);
|
|
295
|
-
}
|
|
300
|
+
case ReferenceTag: {
|
|
301
|
+
this.node.children = btree.push(this.node.children, tag);
|
|
296
302
|
|
|
297
|
-
|
|
303
|
+
const { isArray, name } = tag.value;
|
|
298
304
|
|
|
299
|
-
|
|
305
|
+
if (isArray && !hasOwn(this.node.properties, name)) {
|
|
306
|
+
this.node.properties[name] = [];
|
|
307
|
+
} else {
|
|
308
|
+
this.path = this.path.push(ctx, tag, btree.getSum(this.node.children));
|
|
309
|
+
}
|
|
300
310
|
|
|
301
|
-
this.tagPaths.set(
|
|
311
|
+
this.tagPaths.set(tag, this.path);
|
|
302
312
|
break;
|
|
303
313
|
}
|
|
304
314
|
|
|
305
|
-
case
|
|
306
|
-
this.tagPaths.set(
|
|
315
|
+
case GapTag: {
|
|
316
|
+
this.tagPaths.set(tag, this.path);
|
|
307
317
|
|
|
308
318
|
let target;
|
|
309
|
-
let ref =
|
|
319
|
+
let ref = btree.getAt(-1, this.node.children);
|
|
310
320
|
|
|
311
|
-
if (ref.type !==
|
|
321
|
+
if (ref.type !== ReferenceTag) throw new Error();
|
|
312
322
|
|
|
313
323
|
if (this.held) {
|
|
314
324
|
target = this.held.node;
|
|
@@ -316,10 +326,10 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
316
326
|
this.held = null;
|
|
317
327
|
} else if (this.expressions.size) {
|
|
318
328
|
const expression = this.expressions.value;
|
|
319
|
-
target = getRoot(expression);
|
|
329
|
+
target = expression != null ? getRoot(expression) : buildStubNode(tag);
|
|
320
330
|
this.expressions = this.expressions.pop();
|
|
321
331
|
} else {
|
|
322
|
-
target = buildStubNode(
|
|
332
|
+
target = buildStubNode(tag);
|
|
323
333
|
}
|
|
324
334
|
|
|
325
335
|
this.pathNodes.set(this.pathForTag(ref), target);
|
|
@@ -329,17 +339,16 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
329
339
|
break;
|
|
330
340
|
}
|
|
331
341
|
|
|
332
|
-
case
|
|
333
|
-
this.tagPaths.set(
|
|
342
|
+
case NullTag: {
|
|
343
|
+
this.tagPaths.set(tag, this.path);
|
|
334
344
|
|
|
335
345
|
const { properties } = this.node;
|
|
336
346
|
const { isArray, name } = this.result.value;
|
|
337
347
|
|
|
338
|
-
const newNode = buildStubNode(
|
|
348
|
+
const newNode = buildStubNode(tag);
|
|
339
349
|
|
|
340
350
|
if (!hasOwn(properties, name)) {
|
|
341
|
-
|
|
342
|
-
properties[name] = isArray ? [] : newNode;
|
|
351
|
+
properties[name] = newNode;
|
|
343
352
|
}
|
|
344
353
|
|
|
345
354
|
this.pathNodes.set(this.path, newNode);
|
|
@@ -348,9 +357,9 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
348
357
|
break;
|
|
349
358
|
}
|
|
350
359
|
|
|
351
|
-
case
|
|
360
|
+
case ShiftTag: {
|
|
352
361
|
const finishedNode = this.nodeForTag(this.result);
|
|
353
|
-
const ref = ctx.
|
|
362
|
+
const ref = ctx.getPreviousTag(getOpenTag(finishedNode));
|
|
354
363
|
const finishedPath = this.pathForTag(ref);
|
|
355
364
|
const { properties } = this.node;
|
|
356
365
|
|
|
@@ -361,8 +370,8 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
361
370
|
let node = properties[ref.value.name];
|
|
362
371
|
|
|
363
372
|
if (ref.value.isArray) {
|
|
364
|
-
node =
|
|
365
|
-
properties[ref.value.name].pop();
|
|
373
|
+
node = btree.getAt(-1, node);
|
|
374
|
+
properties[ref.value.name] = btree.pop(properties[ref.value.name]);
|
|
366
375
|
} else {
|
|
367
376
|
properties[ref.value.name] = null;
|
|
368
377
|
}
|
|
@@ -371,37 +380,41 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
371
380
|
break;
|
|
372
381
|
}
|
|
373
382
|
|
|
374
|
-
case
|
|
375
|
-
this.node.children.push(
|
|
383
|
+
case LiteralTag: {
|
|
384
|
+
this.node.children = btree.push(this.node.children, tag);
|
|
376
385
|
break;
|
|
377
386
|
}
|
|
378
387
|
|
|
388
|
+
case ArrayTag:
|
|
389
|
+
this.node.children = btree.push(this.node.children, tag);
|
|
390
|
+
break;
|
|
391
|
+
|
|
379
392
|
default:
|
|
380
393
|
throw new Error();
|
|
381
394
|
}
|
|
382
395
|
}
|
|
383
396
|
|
|
384
|
-
this.result =
|
|
397
|
+
this.result = tag;
|
|
385
398
|
|
|
386
|
-
return
|
|
399
|
+
return tag;
|
|
387
400
|
}
|
|
388
401
|
|
|
389
402
|
*emit() {
|
|
390
|
-
const {
|
|
403
|
+
const { nextTags } = this.context;
|
|
391
404
|
if (!this.depth) {
|
|
392
|
-
let emittable = this.emitted ?
|
|
405
|
+
let emittable = this.emitted ? nextTags.get(this.emitted) : this.result;
|
|
393
406
|
|
|
394
407
|
while (
|
|
395
408
|
emittable &&
|
|
396
409
|
!(
|
|
397
|
-
emittable.type ===
|
|
410
|
+
emittable.type === OpenNodeTag &&
|
|
398
411
|
emittable.value.type &&
|
|
399
412
|
nodeStates.get(this.nodeForTag(emittable)).unboundAttributes?.size
|
|
400
413
|
)
|
|
401
414
|
) {
|
|
402
415
|
yield emittable;
|
|
403
416
|
this.emitted = emittable;
|
|
404
|
-
emittable =
|
|
417
|
+
emittable = nextTags.get(this.emitted);
|
|
405
418
|
}
|
|
406
419
|
}
|
|
407
420
|
}
|
|
@@ -419,11 +432,12 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
419
432
|
}
|
|
420
433
|
|
|
421
434
|
get parentNode() {
|
|
422
|
-
return this.pathNodes.get(this.path.parent);
|
|
435
|
+
return this.pathNodes.has(this.path) ? this.pathNodes.get(this.path.parent) : this.node;
|
|
423
436
|
}
|
|
424
437
|
|
|
425
438
|
branch() {
|
|
426
|
-
const { context, expressions, path, node, result, emitted, held, internalContext } =
|
|
439
|
+
const { context, expressions, path, node, result, emitted, held, resolver, internalContext } =
|
|
440
|
+
this;
|
|
427
441
|
const { pathNodes } = internalContext;
|
|
428
442
|
|
|
429
443
|
const newNode = node && branchNode(node);
|
|
@@ -431,15 +445,26 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
431
445
|
const nodeState = nodeStates.get(node);
|
|
432
446
|
|
|
433
447
|
pathNodes.set(path, newNode);
|
|
448
|
+
pathNodes.set(newNode, path);
|
|
434
449
|
|
|
435
|
-
nodeStates.set(newNode, { ...nodeState
|
|
450
|
+
nodeStates.set(newNode, { ...nodeState });
|
|
436
451
|
|
|
437
452
|
const nodeOpen = getOpenTag(node);
|
|
438
453
|
const nodeClose = getCloseTag(node);
|
|
439
454
|
if (nodeOpen) this.tagNodes.set(nodeOpen, newNode);
|
|
440
455
|
if (nodeClose) this.tagNodes.set(nodeClose, newNode);
|
|
441
456
|
|
|
442
|
-
return this.push(
|
|
457
|
+
return this.push(
|
|
458
|
+
context,
|
|
459
|
+
expressions,
|
|
460
|
+
path,
|
|
461
|
+
newNode,
|
|
462
|
+
result,
|
|
463
|
+
emitted,
|
|
464
|
+
held,
|
|
465
|
+
resolver.branch(),
|
|
466
|
+
internalContext,
|
|
467
|
+
);
|
|
443
468
|
}
|
|
444
469
|
|
|
445
470
|
accept() {
|
|
@@ -463,6 +488,8 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
463
488
|
parent.result = this.result;
|
|
464
489
|
parent.held = this.held;
|
|
465
490
|
parent.path = this.path;
|
|
491
|
+
parent.node = this.node;
|
|
492
|
+
parent.resolver = this.resolver;
|
|
466
493
|
|
|
467
494
|
return parent;
|
|
468
495
|
}
|
|
@@ -472,7 +499,7 @@ export const State = class AgastState extends WeakStackFrame {
|
|
|
472
499
|
|
|
473
500
|
if (!parent) throw new Error('rejected root state');
|
|
474
501
|
|
|
475
|
-
context.
|
|
502
|
+
context.nextTags.delete(parent.result);
|
|
476
503
|
|
|
477
504
|
pathNodes.set(parent.path, parent.node);
|
|
478
505
|
|
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.5.0",
|
|
5
5
|
"author": "Conrad Buck<conartist6@gmail.com>",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -12,14 +12,13 @@
|
|
|
12
12
|
},
|
|
13
13
|
"sideEffects": false,
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@bablr/agast-helpers": "0.
|
|
16
|
-
"@bablr/agast-vm-helpers": "0.
|
|
15
|
+
"@bablr/agast-helpers": "0.4.0",
|
|
16
|
+
"@bablr/agast-vm-helpers": "0.4.0",
|
|
17
17
|
"@bablr/coroutine": "0.1.0",
|
|
18
18
|
"@bablr/weak-stack": "0.1.0",
|
|
19
19
|
"@iter-tools/imm-stack": "1.1.0"
|
|
20
20
|
},
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@bablr/agast-vm-strategy-passthrough": "github:bablr-lang/agast-vm-strategy-passthrough#2bd3a0c7311037af92c5b81941c79161499f6c9e",
|
|
23
22
|
"@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#49f5952efed27f94ee9b94340eb1563c440bf64e",
|
|
24
23
|
"enhanced-resolve": "^5.12.0",
|
|
25
24
|
"eslint": "^8.32.0",
|