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