@bablr/agast-vm 0.9.0 → 0.11.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/evaluate.js +112 -81
- package/lib/index.js +1 -4
- package/lib/state.js +10 -449
- package/package.json +14 -4
- package/lib/facades.js +0 -3
- package/lib/node.js +0 -176
- package/lib/path.js +0 -162
package/lib/evaluate.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* global WeakSet */
|
|
1
2
|
import {
|
|
2
3
|
DoctypeTag,
|
|
3
4
|
OpenNodeTag,
|
|
@@ -6,23 +7,33 @@ import {
|
|
|
6
7
|
ShiftTag,
|
|
7
8
|
GapTag,
|
|
8
9
|
NullTag,
|
|
9
|
-
InitializerTag,
|
|
10
10
|
LiteralTag,
|
|
11
11
|
AttributeDefinition,
|
|
12
12
|
BindingTag,
|
|
13
13
|
Property,
|
|
14
|
+
TreeNode,
|
|
15
|
+
NullNode,
|
|
16
|
+
GapNode,
|
|
14
17
|
} from '@bablr/agast-helpers/symbols';
|
|
15
18
|
import { State } from './state.js';
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
19
|
+
import {
|
|
20
|
+
getAttributes,
|
|
21
|
+
getFirstNode,
|
|
22
|
+
isGapNode,
|
|
23
|
+
isNodeTag,
|
|
24
|
+
TagPath,
|
|
25
|
+
} from '@bablr/agast-helpers/path';
|
|
26
|
+
import * as BTree from '@bablr/agast-helpers/btree';
|
|
27
|
+
import * as Tags from '@bablr/agast-helpers/tags';
|
|
28
|
+
import { isObject } from '@bablr/agast-helpers/object';
|
|
29
|
+
|
|
30
|
+
const validNodes = new WeakSet();
|
|
31
|
+
|
|
32
|
+
export const isKnownValid = (node) => validNodes.has(node);
|
|
20
33
|
|
|
21
34
|
let { isArray } = Array;
|
|
22
35
|
|
|
23
36
|
export const agast = (options = {}) => {
|
|
24
|
-
if (options.held?.properties) throw new Error();
|
|
25
|
-
|
|
26
37
|
let state = State.from(options.held);
|
|
27
38
|
|
|
28
39
|
let vm = __agast(state, options);
|
|
@@ -31,7 +42,14 @@ export const agast = (options = {}) => {
|
|
|
31
42
|
|
|
32
43
|
Object.freeze(options);
|
|
33
44
|
|
|
34
|
-
|
|
45
|
+
let getState = () => ({
|
|
46
|
+
path: state.path,
|
|
47
|
+
node: state.path.node,
|
|
48
|
+
resultPath: state.resultPath,
|
|
49
|
+
held: state.held,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return Object.freeze({ options, getState, vm });
|
|
35
53
|
};
|
|
36
54
|
|
|
37
55
|
function* __agast(s) {
|
|
@@ -39,22 +57,19 @@ function* __agast(s) {
|
|
|
39
57
|
for (;;) {
|
|
40
58
|
let returnValue = null;
|
|
41
59
|
|
|
42
|
-
if (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
) {
|
|
56
|
-
throw new Error('Cannot advance while holding');
|
|
57
|
-
}
|
|
60
|
+
// if (
|
|
61
|
+
// s.held &&
|
|
62
|
+
// ![OpenNodeTag, ReferenceTag, TreeNode, GapNode, BindingTag, Property, GapTag].includes(
|
|
63
|
+
// tag.type,
|
|
64
|
+
// ) &&
|
|
65
|
+
// !(
|
|
66
|
+
// (isNodeTag(tag) &&
|
|
67
|
+
// (Tags.getSize(tag.value.tags) <= 1 || getFirstNode(tag.value) === s.held)) ||
|
|
68
|
+
// tag.value === s.held
|
|
69
|
+
// )
|
|
70
|
+
// ) {
|
|
71
|
+
// throw new Error('Cannot advance while holding');
|
|
72
|
+
// }
|
|
58
73
|
|
|
59
74
|
switch (tag.type) {
|
|
60
75
|
case DoctypeTag: {
|
|
@@ -62,109 +77,125 @@ function* __agast(s) {
|
|
|
62
77
|
throw new Error();
|
|
63
78
|
}
|
|
64
79
|
|
|
65
|
-
s.advance(tag);
|
|
80
|
+
s.path = s.path.advance(tag);
|
|
66
81
|
break;
|
|
67
82
|
}
|
|
68
83
|
|
|
69
|
-
case
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (s.held) {
|
|
75
|
-
throw new Error('Cannot consume input while hold register is full');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
s.advance(tag);
|
|
84
|
+
case ReferenceTag: {
|
|
85
|
+
s.path = s.path.advance(tag);
|
|
86
|
+
s.resultPath = s.path.tagPathAt(-1, -1);
|
|
79
87
|
break;
|
|
80
88
|
}
|
|
81
89
|
|
|
82
|
-
case
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
90
|
+
case LiteralTag: {
|
|
91
|
+
s.path = s.path.advance(tag);
|
|
92
|
+
s.resultPath = s.path.tagPathAt(-1, -1);
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
88
95
|
|
|
89
|
-
|
|
96
|
+
case AttributeDefinition: {
|
|
97
|
+
s.path = s.path.advance(tag);
|
|
98
|
+
s.resultPath = s.path.tagPathAt(-1, -1);
|
|
90
99
|
break;
|
|
91
100
|
}
|
|
92
101
|
|
|
93
102
|
case BindingTag: {
|
|
94
|
-
const {
|
|
103
|
+
const { segments } = tag.value;
|
|
95
104
|
|
|
96
|
-
if (
|
|
105
|
+
if (segments && !isArray(segments)) throw new Error();
|
|
97
106
|
|
|
98
|
-
s.advance(tag);
|
|
107
|
+
s.path = s.path.advance(tag);
|
|
108
|
+
s.resultPath = s.path.tagPathAt(-1, [-1, -1]);
|
|
99
109
|
break;
|
|
100
110
|
}
|
|
101
111
|
|
|
102
112
|
case NullTag:
|
|
103
113
|
case GapTag: {
|
|
104
|
-
s.advance(tag);
|
|
105
|
-
|
|
114
|
+
s.path = s.path.advance(tag);
|
|
115
|
+
s.resultPath = s.path.tagPathAt(-1, -1);
|
|
116
|
+
returnValue = s.resultPath.node;
|
|
106
117
|
break;
|
|
107
118
|
}
|
|
108
119
|
|
|
109
120
|
case AttributeDefinition: {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
s.advance(tag);
|
|
121
|
+
s.path = s.path.advance(tag);
|
|
113
122
|
break;
|
|
114
123
|
}
|
|
115
124
|
|
|
116
|
-
case
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
s.
|
|
125
|
+
case ShiftTag: {
|
|
126
|
+
let previousPropertyPath = s.path.tagPathAt(-1);
|
|
127
|
+
s.path = s.path.advance(tag);
|
|
128
|
+
s.resultPath = s.path.tagPathAt(-1, -1);
|
|
129
|
+
s.held = previousPropertyPath.inner.node;
|
|
120
130
|
break;
|
|
121
131
|
}
|
|
122
132
|
|
|
123
|
-
case
|
|
124
|
-
|
|
125
|
-
|
|
133
|
+
case OpenNodeTag: {
|
|
134
|
+
s.path = !s.path ? TagPath.fromTag(tag).path : s.path.advance(tag);
|
|
135
|
+
s.resultPath = s.path.tagPathAt(-1, -1);
|
|
126
136
|
|
|
127
|
-
|
|
137
|
+
if (s.path) {
|
|
138
|
+
let queue = [getAttributes(s.node)];
|
|
139
|
+
while (queue.length) {
|
|
140
|
+
for (let value of Object.values(queue[queue.length - 1])) {
|
|
141
|
+
if (value === undefined) {
|
|
142
|
+
} else if (isArray(value) || isObject(value)) {
|
|
143
|
+
queue.push(value);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
queue.pop();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
128
149
|
|
|
129
|
-
|
|
130
|
-
|
|
150
|
+
returnValue = s.node || s.resultPath.node;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
case CloseNodeTag: {
|
|
155
|
+
if (!s.path) {
|
|
156
|
+
s.done = true;
|
|
131
157
|
}
|
|
132
158
|
|
|
133
|
-
|
|
134
|
-
s.resultPath.path,
|
|
135
|
-
s.resultPath.childrenIndex - (index - 1) * 3 - 2,
|
|
136
|
-
);
|
|
159
|
+
validNodes.add(s.node);
|
|
137
160
|
|
|
138
|
-
|
|
161
|
+
returnValue = s.node;
|
|
139
162
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
163
|
+
let donePath = s.path;
|
|
164
|
+
|
|
165
|
+
s.path = s.path.advance(tag);
|
|
143
166
|
|
|
144
|
-
s.
|
|
167
|
+
s.resultPath = donePath.tagPathAt(-1, -1);
|
|
145
168
|
break;
|
|
146
169
|
}
|
|
147
170
|
|
|
148
|
-
case
|
|
149
|
-
|
|
171
|
+
case Property: {
|
|
172
|
+
let { tags, node } = tag.value;
|
|
173
|
+
|
|
174
|
+
for (tag of Tags.traverse(tags)) {
|
|
175
|
+
s.path = s.path.advance(tag);
|
|
176
|
+
}
|
|
177
|
+
returnValue = node;
|
|
150
178
|
|
|
151
|
-
returnValue = new NodeFacade(s.node);
|
|
152
179
|
break;
|
|
153
180
|
}
|
|
154
181
|
|
|
155
|
-
case
|
|
156
|
-
|
|
182
|
+
case NullNode:
|
|
183
|
+
case GapNode:
|
|
184
|
+
case TreeNode: {
|
|
185
|
+
let node = tag;
|
|
157
186
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
187
|
+
// let clearedHeld =
|
|
188
|
+
// s.held &&
|
|
189
|
+
// (node.type === GapNode ||
|
|
190
|
+
// BTree.getAt(-BTree.getSize(s.held.value.bounds[0]), node.value.bounds[0]));
|
|
161
191
|
|
|
162
|
-
|
|
163
|
-
let { node } = tag.value;
|
|
192
|
+
// if (s.held && !s.path.held) throw new Error();
|
|
164
193
|
|
|
165
|
-
s.advance(tag);
|
|
194
|
+
s.path = s.path.advance(s.held && isGapNode(node) ? s.held : tag);
|
|
195
|
+
s.resultPath = s.path.tagPathAt(-1, -1);
|
|
196
|
+
s.held = null;
|
|
166
197
|
|
|
167
|
-
returnValue =
|
|
198
|
+
returnValue = s.path.node;
|
|
168
199
|
break;
|
|
169
200
|
}
|
|
170
201
|
|
package/lib/index.js
CHANGED
package/lib/state.js
CHANGED
|
@@ -1,164 +1,25 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
finalizeNode,
|
|
4
|
-
buildStubNode,
|
|
5
|
-
getRange,
|
|
6
|
-
createNode,
|
|
7
|
-
buildNullNode,
|
|
8
|
-
buildOpenNodeTag,
|
|
9
|
-
shiftProperty,
|
|
10
|
-
buildProperty,
|
|
11
|
-
add,
|
|
12
|
-
buildBindingTag,
|
|
13
|
-
buildAttributeDefinition,
|
|
14
|
-
} from '@bablr/agast-helpers/tree';
|
|
15
|
-
import * as sumtree from '@bablr/agast-helpers/children';
|
|
16
|
-
import * as btree from '@bablr/agast-helpers/btree';
|
|
17
|
-
import {
|
|
18
|
-
getInitializerChildrenIndex,
|
|
19
|
-
getOpenTag,
|
|
20
|
-
getOriginalFirstNode,
|
|
21
|
-
isGapNode,
|
|
22
|
-
isStubNode,
|
|
23
|
-
referencesAreEqual,
|
|
24
|
-
TagPath,
|
|
25
|
-
} from '@bablr/agast-helpers/path';
|
|
26
|
-
import {
|
|
27
|
-
DoctypeTag,
|
|
28
|
-
OpenNodeTag,
|
|
29
|
-
CloseNodeTag,
|
|
30
|
-
ReferenceTag,
|
|
31
|
-
ShiftTag,
|
|
32
|
-
GapTag,
|
|
33
|
-
NullTag,
|
|
34
|
-
InitializerTag,
|
|
35
|
-
LiteralTag,
|
|
36
|
-
AttributeDefinition,
|
|
37
|
-
BindingTag,
|
|
38
|
-
Property,
|
|
39
|
-
} from '@bablr/agast-helpers/symbols';
|
|
40
|
-
import { facades, actuals } from './facades.js';
|
|
41
|
-
import { Path, PathFacade, TagPathFacade } from './path.js';
|
|
42
|
-
import { get, has, immSet, isObject } from '@bablr/agast-helpers/object';
|
|
43
|
-
import { NodeFacade } from './node.js';
|
|
44
|
-
|
|
45
|
-
const { isArray } = Array;
|
|
46
|
-
const { hasOwn } = Object;
|
|
47
|
-
|
|
48
|
-
const defineAttribute = (node, tag) => {
|
|
49
|
-
if (tag.type !== AttributeDefinition) throw new Error();
|
|
50
|
-
|
|
51
|
-
let { path, value } = tag.value;
|
|
52
|
-
let openTag = getOpenTag(node);
|
|
53
|
-
let { attributes } = node;
|
|
54
|
-
|
|
55
|
-
if (!has(attributes, path) && get(attributes, path) !== undefined)
|
|
56
|
-
throw new Error('Can only define undefined attributes');
|
|
57
|
-
|
|
58
|
-
if (value === undefined) throw new Error('cannot define attribute to undefined');
|
|
59
|
-
|
|
60
|
-
let { flags, type } = openTag.value;
|
|
61
|
-
attributes = immSet(attributes, path, value);
|
|
62
|
-
let newOpenTag = buildOpenNodeTag(flags, type, attributes);
|
|
63
|
-
|
|
64
|
-
node.attributes = attributes;
|
|
65
|
-
|
|
66
|
-
node.children = sumtree.replaceAt(0, node.children, newOpenTag);
|
|
67
|
-
node.children = sumtree.push(node.children, tag);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
export const StateFacade = class AgastStateFacade {
|
|
71
|
-
constructor(state) {
|
|
72
|
-
facades.set(state, this);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
static create() {
|
|
76
|
-
return State.create();
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
get resultPath() {
|
|
80
|
-
let { resultPath } = actuals.get(this);
|
|
81
|
-
return resultPath && new TagPathFacade(resultPath);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
get referenceTag() {
|
|
85
|
-
return actuals.get(this).referenceTag;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
get referenceTagPath() {
|
|
89
|
-
let { referenceTagPath } = actuals.get(this);
|
|
90
|
-
return referenceTagPath && new TagPathFacade(referenceTagPath);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
get path() {
|
|
94
|
-
let { path } = actuals.get(this);
|
|
95
|
-
return path && new PathFacade(path);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
get node() {
|
|
99
|
-
let { node } = actuals.get(this);
|
|
100
|
-
return node && new NodeFacade(node);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
get done() {
|
|
104
|
-
return actuals.get(this).done;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
get parentNode() {
|
|
108
|
-
let { parentNode } = actuals.get(this);
|
|
109
|
-
return parentNode && new NodeFacade(parentNode);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
get held() {
|
|
113
|
-
let { held } = actuals.get(this);
|
|
114
|
-
return held && new NodeFacade(held.node);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
get holding() {
|
|
118
|
-
return actuals.get(this).holding;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
get depth() {
|
|
122
|
-
return actuals.get(this).depth;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
get parent() {
|
|
126
|
-
return facades.get(actuals.get(this).parent);
|
|
127
|
-
}
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
export const nodeStates = new WeakMap();
|
|
1
|
+
import { offsetForTag, TagPath } from '@bablr/agast-helpers/path';
|
|
2
|
+
import { DoctypeTag, Property } from '@bablr/agast-helpers/symbols';
|
|
131
3
|
|
|
132
4
|
export const State = class AgastState {
|
|
133
5
|
static create() {
|
|
134
6
|
return new State();
|
|
135
7
|
}
|
|
136
8
|
|
|
9
|
+
static from(held) {
|
|
10
|
+
return new State(held);
|
|
11
|
+
}
|
|
12
|
+
|
|
137
13
|
constructor(held = null, path = null, resultPath = null) {
|
|
138
14
|
this.held = held;
|
|
139
15
|
this.path = path;
|
|
140
16
|
this.resultPath = resultPath;
|
|
141
17
|
this.done = false;
|
|
142
|
-
this.referenceTagPath = null;
|
|
143
18
|
this.doctype = null;
|
|
144
|
-
|
|
145
|
-
new StateFacade(this);
|
|
146
19
|
}
|
|
147
20
|
|
|
148
|
-
|
|
149
|
-
return
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
get holding() {
|
|
153
|
-
return !!this.held;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
get atReference() {
|
|
157
|
-
return [ReferenceTag, ShiftTag].includes(this.resultPath?.tag.type);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
get atBinding() {
|
|
161
|
-
return this.resultPath?.tag.type === BindingTag;
|
|
21
|
+
get node() {
|
|
22
|
+
return this.path.node;
|
|
162
23
|
}
|
|
163
24
|
|
|
164
25
|
advance(tag) {
|
|
@@ -180,313 +41,13 @@ export const State = class AgastState {
|
|
|
180
41
|
break;
|
|
181
42
|
}
|
|
182
43
|
|
|
183
|
-
case ReferenceTag: {
|
|
184
|
-
let { type, isArray, name, flags } = tag.value;
|
|
185
|
-
|
|
186
|
-
if (this.atReference) throw new Error('invalid location for reference');
|
|
187
|
-
if (!name && !type) throw new Error();
|
|
188
|
-
if (type && !['.', '#', '@'].includes(type)) throw new Error();
|
|
189
|
-
if (['#', '@'].includes(type) && isArray) throw new Error();
|
|
190
|
-
|
|
191
|
-
if (this.held && sumtree.getAt(2, this.node.children)?.type === InitializerTag) {
|
|
192
|
-
if (
|
|
193
|
-
!referencesAreEqual(sumtree.getAt(1, this.node.children), tag) ||
|
|
194
|
-
sumtree.getSize(this.node.children) > 3
|
|
195
|
-
) {
|
|
196
|
-
throw new Error();
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (this.node.properties[name] && this.node.properties[name]?.node === undefined) {
|
|
201
|
-
let existingReferenceIndex = getInitializerChildrenIndex(this.node, tag);
|
|
202
|
-
let existingReference = sumtree.getAt(existingReferenceIndex, this.node.children);
|
|
203
|
-
if (!referencesAreEqual(tag, existingReference)) {
|
|
204
|
-
throw new Error("reference didn't match initializer");
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
this.node.children = sumtree.push(this.node.children, tag);
|
|
209
|
-
|
|
210
|
-
if (flags.hasGap && !this.node.flags.hasGap) {
|
|
211
|
-
throw new Error('gap reference in gapless node');
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
this.referenceTagPath = TagPath.from(this.path, -1);
|
|
215
|
-
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
case BindingTag: {
|
|
220
|
-
if (!this.atReference && this.parent) {
|
|
221
|
-
throw new Error('Invalid location for BindingTag');
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
this.node.children = sumtree.push(this.node.children, tag);
|
|
225
|
-
break;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
case OpenNodeTag: {
|
|
229
|
-
let parentNode = this.node;
|
|
230
|
-
|
|
231
|
-
targetPath = this.path;
|
|
232
|
-
|
|
233
|
-
if (!this.atBinding && this.parent) {
|
|
234
|
-
throw new Error('Invalid location for OpenNodeTag');
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
let bindingTag = parentNode && sumtree.getAt(-1, parentNode.children);
|
|
238
|
-
|
|
239
|
-
if (bindingTag && !bindingTag.value.languagePath) throw new Error();
|
|
240
|
-
|
|
241
|
-
let node = createNode(tag);
|
|
242
|
-
|
|
243
|
-
if (!this.path) {
|
|
244
|
-
this.path = Path.create(node);
|
|
245
|
-
|
|
246
|
-
if (!tag.value.type && this.doctype) {
|
|
247
|
-
node.children = sumtree.push(node.children, this.doctype);
|
|
248
|
-
}
|
|
249
|
-
} else {
|
|
250
|
-
this.path = this.path.push(node, sumtree.getSize(parentNode.children) - 2);
|
|
251
|
-
add(parentNode, this.referenceTag, node, bindingTag);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
node.children = sumtree.push(node.children, tag);
|
|
255
|
-
|
|
256
|
-
let undefinedAttributes = 0;
|
|
257
|
-
|
|
258
|
-
let queue = [this.node.attributes];
|
|
259
|
-
while (queue.length) {
|
|
260
|
-
for (let value of Object.values(queue[queue.length - 1])) {
|
|
261
|
-
if (value === undefined) {
|
|
262
|
-
undefinedAttributes++;
|
|
263
|
-
} else if (isArray(value) || isObject(value)) {
|
|
264
|
-
queue.push(value);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
queue.pop();
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
nodeStates.set(this.node, { undefinedAttributes });
|
|
271
|
-
|
|
272
|
-
break;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
case CloseNodeTag: {
|
|
276
|
-
if (this.atReference) throw new Error('invalid location for close tag');
|
|
277
|
-
|
|
278
|
-
if (this.node.type) {
|
|
279
|
-
for (const value of Object.values(this.node.properties)) {
|
|
280
|
-
if (!Array.isArray(value) && value.node === undefined) {
|
|
281
|
-
addProperty(
|
|
282
|
-
this.node,
|
|
283
|
-
buildProperty(value.reference, buildBindingTag().value, buildNullNode()),
|
|
284
|
-
);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
let path = [];
|
|
290
|
-
let queue = [this.node.attributes];
|
|
291
|
-
while (queue.length) {
|
|
292
|
-
for (const { 0: key, 1: value } of Object.entries(queue[queue.length - 1])) {
|
|
293
|
-
if (value === undefined) {
|
|
294
|
-
path.push(key);
|
|
295
|
-
defineAttribute(this.node, buildAttributeDefinition(path, null));
|
|
296
|
-
path.pop(key);
|
|
297
|
-
} else if (isArray(value) || isObject(value)) {
|
|
298
|
-
queue.push(value);
|
|
299
|
-
path.push(key);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
queue.pop();
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
this.node.children = sumtree.push(this.node.children, tag);
|
|
306
|
-
|
|
307
|
-
if (!this.path) {
|
|
308
|
-
this.done = true;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
finalizeNode(this.node);
|
|
312
|
-
|
|
313
|
-
this.path = this.path.parent;
|
|
314
|
-
break;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
case Property: {
|
|
318
|
-
let target;
|
|
319
|
-
let property = tag.value;
|
|
320
|
-
let { node } = this.path;
|
|
321
|
-
let lastTagPath = TagPath.from(this.path, -1);
|
|
322
|
-
|
|
323
|
-
if (!this.atBinding && !isStubNode(property.node)) {
|
|
324
|
-
throw new Error('Invalid location for Property');
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if (this.held) {
|
|
328
|
-
if (isGapNode(property.node)) {
|
|
329
|
-
property = buildProperty(property.reference, this.held.binding, this.held.node);
|
|
330
|
-
} else {
|
|
331
|
-
let { height } = lastTagPath.previousSibling.tag.value;
|
|
332
|
-
let matchesHeld =
|
|
333
|
-
isGapNode(property.node) ||
|
|
334
|
-
this.held.node === property.node ||
|
|
335
|
-
this.held.node === getOriginalFirstNode(property.node, (height ?? 1) - 1);
|
|
336
|
-
if (!matchesHeld) {
|
|
337
|
-
throw new Error();
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
if (this.held && lastTagPath.previousSibling.tag.type !== ShiftTag) {
|
|
343
|
-
target = this.held.node;
|
|
344
|
-
} else {
|
|
345
|
-
// if (!this.node.flags.hasGap) throw new Error('Node must allow gaps');
|
|
346
|
-
|
|
347
|
-
target = buildStubNode(tag);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
const range = getRange(target);
|
|
351
|
-
|
|
352
|
-
this.resultPath = range ? range[1] : this.resultPath;
|
|
353
|
-
this.referenceTagPath = null;
|
|
354
|
-
|
|
355
|
-
let lastRefPath = [ShiftTag, ReferenceTag].includes(lastTagPath.tag.type)
|
|
356
|
-
? lastTagPath
|
|
357
|
-
: lastTagPath.previousSibling;
|
|
358
|
-
|
|
359
|
-
if (![ShiftTag, ReferenceTag].includes(lastRefPath.tag.type)) throw new Error();
|
|
360
|
-
|
|
361
|
-
if (lastRefPath.tag.type === ShiftTag) {
|
|
362
|
-
shiftProperty(node, property);
|
|
363
|
-
} else {
|
|
364
|
-
addProperty(node, property);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
this.held = null;
|
|
368
|
-
break;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
case GapTag:
|
|
372
|
-
case NullTag: {
|
|
373
|
-
const { node: parentNode, referenceTag } = this;
|
|
374
|
-
|
|
375
|
-
if (this.held) throw new Error();
|
|
376
|
-
|
|
377
|
-
if (!this.atReference && this.parent) {
|
|
378
|
-
throw new Error('Invalid location for NullTag');
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
let stubNode = buildStubNode(tag);
|
|
382
|
-
|
|
383
|
-
add(parentNode, referenceTag, stubNode);
|
|
384
|
-
|
|
385
|
-
targetPath = this.path.push(stubNode, sumtree.getSize(this.node.children) - 3);
|
|
386
|
-
break;
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
case AttributeDefinition: {
|
|
390
|
-
if (this.held) throw new Error('invalid place for an atrribute binding');
|
|
391
|
-
|
|
392
|
-
let nodeState = nodeStates.get(this.node);
|
|
393
|
-
|
|
394
|
-
nodeState.undefinedAttributes--;
|
|
395
|
-
|
|
396
|
-
// add undefined attributes from value
|
|
397
|
-
|
|
398
|
-
defineAttribute(this.node, tag);
|
|
399
|
-
|
|
400
|
-
break;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
case InitializerTag: {
|
|
404
|
-
const { isArray } = tag.value;
|
|
405
|
-
const { node, referenceTag } = this;
|
|
406
|
-
const { name } = referenceTag.value;
|
|
407
|
-
|
|
408
|
-
if (!this.atReference && this.parent) {
|
|
409
|
-
throw new Error('Invalid location for InitializerTag');
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
if (this.held && sumtree.getAt(2, node.children)?.type === InitializerTag) {
|
|
413
|
-
throw new Error();
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
if (
|
|
417
|
-
hasOwn(node.properties, name) &&
|
|
418
|
-
(isArray
|
|
419
|
-
? btree.getSize(node.properties[name])
|
|
420
|
-
: node.properties[name]?.node !== undefined)
|
|
421
|
-
) {
|
|
422
|
-
throw new Error();
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
add(node, referenceTag, isArray ? [] : undefined);
|
|
426
|
-
break;
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
case ShiftTag: {
|
|
430
|
-
if (this.resultPath.tag.type !== Property) throw new Error('invalid location for shift');
|
|
431
|
-
|
|
432
|
-
let heldProperty = this.resultPath.tag.value;
|
|
433
|
-
|
|
434
|
-
if (!this.held) {
|
|
435
|
-
this.held = heldProperty;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
if (!heldProperty.reference.flags.expression) throw new Error();
|
|
439
|
-
|
|
440
|
-
this.node.children = sumtree.push(this.node.children, tag);
|
|
441
|
-
|
|
442
|
-
this.referenceTagPath = TagPath.from(this.path, -1);
|
|
443
|
-
|
|
444
|
-
// this.path = finishedPath;
|
|
445
|
-
targetPath = this.path;
|
|
446
|
-
break;
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
case LiteralTag:
|
|
450
|
-
if (typeof tag.value !== 'string') throw new Error();
|
|
451
|
-
|
|
452
|
-
this.node.children = sumtree.push(this.node.children, tag);
|
|
453
|
-
break;
|
|
454
|
-
|
|
455
44
|
default:
|
|
456
45
|
throw new Error();
|
|
457
46
|
}
|
|
458
47
|
|
|
459
|
-
this.resultPath =
|
|
48
|
+
this.resultPath =
|
|
49
|
+
targetPath && TagPath.from(targetPath, -1, tag.type === Property ? -1 : offsetForTag(tag));
|
|
460
50
|
|
|
461
51
|
return tag;
|
|
462
52
|
}
|
|
463
|
-
|
|
464
|
-
get isGap() {
|
|
465
|
-
return this.tag.type === GapTag;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
get speculative() {
|
|
469
|
-
return !!this.parent;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
get parentNode() {
|
|
473
|
-
return this.path.parent.node;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
get referenceTag() {
|
|
477
|
-
return this.referenceTagPath?.tag;
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
get bindingPath() {
|
|
481
|
-
let path = this.referenceTagPath.nextSibling;
|
|
482
|
-
return path.tag.type === BindingTag ? path : null;
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
get binding() {
|
|
486
|
-
return this.bindingPath?.tag;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
get node() {
|
|
490
|
-
return this.path?.node;
|
|
491
|
-
}
|
|
492
53
|
};
|
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.11.0",
|
|
5
5
|
"author": "Conrad Buck<conartist6@gmail.com>",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"files": [
|
|
@@ -11,20 +11,30 @@
|
|
|
11
11
|
".": "./lib/index.js"
|
|
12
12
|
},
|
|
13
13
|
"sideEffects": false,
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "mocha test/*.test.js"
|
|
16
|
+
},
|
|
14
17
|
"dependencies": {
|
|
15
|
-
"@
|
|
16
|
-
"@bablr/agast-
|
|
18
|
+
"@iter-tools/imm-stack": "1.2.0",
|
|
19
|
+
"@bablr/agast-helpers": "0.10.0",
|
|
20
|
+
"@bablr/agast-vm-helpers": "0.10.0"
|
|
17
21
|
},
|
|
18
22
|
"devDependencies": {
|
|
19
23
|
"@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#49f5952efed27f94ee9b94340eb1563c440bf64e",
|
|
24
|
+
"@bablr/boot": "*",
|
|
20
25
|
"enhanced-resolve": "^5.12.0",
|
|
26
|
+
"expect": "30.1.2",
|
|
21
27
|
"eslint": "^8.32.0",
|
|
22
28
|
"eslint-import-resolver-enhanced-resolve": "^1.0.5",
|
|
23
29
|
"eslint-plugin-import": "^2.27.5",
|
|
24
30
|
"iter-tools-es": "^7.3.1",
|
|
31
|
+
"mocha": "11.7.2",
|
|
25
32
|
"prettier": "^2.6.2"
|
|
26
33
|
},
|
|
27
|
-
"repository":
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/bablr-lang/agast-vm.git"
|
|
37
|
+
},
|
|
28
38
|
"homepage": "https://github.com/bablr-lang/agast-vm",
|
|
29
39
|
"license": "MIT"
|
|
30
40
|
}
|
package/lib/facades.js
DELETED
package/lib/node.js
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildPathSegment,
|
|
3
|
-
get,
|
|
4
|
-
getChildPropertyIndex,
|
|
5
|
-
getCloseTag,
|
|
6
|
-
getProperties,
|
|
7
|
-
getPropertyChildrenIndex,
|
|
8
|
-
} from '@bablr/agast-helpers/path';
|
|
9
|
-
import * as sumtree from '@bablr/agast-helpers/children';
|
|
10
|
-
import * as btree from '@bablr/agast-helpers/btree';
|
|
11
|
-
import { DoctypeTag, GapTag, NullTag, OpenNodeTag } from '@bablr/agast-helpers/symbols';
|
|
12
|
-
import { nodeFlags } from '@bablr/agast-helpers/builders';
|
|
13
|
-
import { nodeStates } from './state.js';
|
|
14
|
-
|
|
15
|
-
let { freeze, hasOwn } = Object;
|
|
16
|
-
|
|
17
|
-
export const nodes = new WeakMap();
|
|
18
|
-
|
|
19
|
-
export class NodeChildrenFacade {
|
|
20
|
-
constructor(node) {
|
|
21
|
-
nodes.set(this, node);
|
|
22
|
-
let tree = node.children;
|
|
23
|
-
|
|
24
|
-
if (!Object.isFrozen(tree)) throw new Error(); // just a sanity check right now
|
|
25
|
-
|
|
26
|
-
this.size = sumtree.getSize(tree);
|
|
27
|
-
|
|
28
|
-
freeze(this);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
at(idx) {
|
|
32
|
-
return sumtree.getAt(idx, nodes.get(this).children);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
[Symbol.iterator]() {
|
|
36
|
-
return sumtree.traverse(nodes.get(this).children);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export class ListFacade {
|
|
41
|
-
constructor(tree) {
|
|
42
|
-
nodes.set(this, tree);
|
|
43
|
-
|
|
44
|
-
this.size = btree.getSize(tree);
|
|
45
|
-
|
|
46
|
-
freeze(this);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
at(idx) {
|
|
50
|
-
return btree.getAt(idx, nodes.get(this));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
[Symbol.iterator]() {
|
|
54
|
-
return btree.traverse(nodes.get(this));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export class NodePropertiesFacade {
|
|
59
|
-
constructor(node) {
|
|
60
|
-
nodes.set(this, node);
|
|
61
|
-
|
|
62
|
-
freeze(this);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
at(name, index = -1, shiftIndex = 0) {
|
|
66
|
-
let { properties } = nodes.get(this);
|
|
67
|
-
|
|
68
|
-
if (hasOwn(properties, name)) {
|
|
69
|
-
let prop = properties[name];
|
|
70
|
-
|
|
71
|
-
if (Array.isArray(prop.node) && index === -1) {
|
|
72
|
-
return new ListFacade(prop.node);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (Array.isArray(prop.node)) {
|
|
76
|
-
prop = btree.getAt(index ?? -1, prop.node);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (!prop) return index === -1 ? prop : null;
|
|
80
|
-
if (prop.reference.flags.expression) {
|
|
81
|
-
return NodeFacade.from(btree.getAt(shiftIndex + 1, prop.node)?.node);
|
|
82
|
-
} else {
|
|
83
|
-
return NodeFacade.from(prop.node);
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return null;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
has(name, index = -1) {
|
|
90
|
-
let { properties } = nodes.get(this);
|
|
91
|
-
if (!hasOwn(properties, name)) return false;
|
|
92
|
-
|
|
93
|
-
if (index >= 0 && !getProperties(name, index, properties)) {
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
referenceAt(name, index = -1) {
|
|
101
|
-
return getProperties(buildPathSegment(name, index), nodes.get(this).properties)?.reference;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
[Symbol.iterator]() {
|
|
105
|
-
return Object.entries(nodes.get(this))[Symbol.iterator]();
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export class NodeFacade {
|
|
110
|
-
static from(node) {
|
|
111
|
-
return node && new NodeFacade(node);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
constructor(node) {
|
|
115
|
-
let sigilTag = sumtree.getAt(0, node.children);
|
|
116
|
-
|
|
117
|
-
if (sigilTag.type === DoctypeTag) {
|
|
118
|
-
sigilTag = sumtree.getAt(1, node.children);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
this.sigilTag = sigilTag;
|
|
122
|
-
|
|
123
|
-
if ([NullTag, GapTag].includes(sigilTag.type)) {
|
|
124
|
-
this.flags = nodeFlags;
|
|
125
|
-
this.type = null;
|
|
126
|
-
} else if (sigilTag.type === OpenNodeTag) {
|
|
127
|
-
let { flags, type } = sigilTag.value;
|
|
128
|
-
|
|
129
|
-
this.flags = flags;
|
|
130
|
-
this.type = type;
|
|
131
|
-
} else {
|
|
132
|
-
throw new Error();
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
nodes.set(this, node);
|
|
136
|
-
|
|
137
|
-
freeze(this);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
get children() {
|
|
141
|
-
return new NodeChildrenFacade(nodes.get(this));
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
get properties() {
|
|
145
|
-
return new NodePropertiesFacade(nodes.get(this));
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
get attributes() {
|
|
149
|
-
return nodes.get(this).attributes;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
get undefinedAttributes() {
|
|
153
|
-
return nodeStates.get(nodes.get(this)).undefinedAttributes;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
get node() {
|
|
157
|
-
let node = nodes.get(this);
|
|
158
|
-
|
|
159
|
-
return getCloseTag(node) || [NullTag, GapTag].includes(sumtree.getAt(0, node.children).type)
|
|
160
|
-
? node
|
|
161
|
-
: undefined;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
get(path, shiftIndex = null) {
|
|
165
|
-
if (shiftIndex !== null) throw new Error('bad get');
|
|
166
|
-
return NodeFacade.from(get(path, nodes.get(this)));
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
getChildPropertyIndex(childrenIndex) {
|
|
170
|
-
return getChildPropertyIndex(nodes.get(this), childrenIndex);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
getPropertyChildrenIndex(type, name) {
|
|
174
|
-
return getPropertyChildrenIndex(nodes.get(this), type, name);
|
|
175
|
-
}
|
|
176
|
-
}
|
package/lib/path.js
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
import { Property } from '@bablr/agast-helpers/symbols';
|
|
2
|
-
import { NodeFacade, nodes } from './node.js';
|
|
3
|
-
import { Path, TagPath } from '@bablr/agast-helpers/path';
|
|
4
|
-
import { buildChild, buildFacadeProperty, buildProperty } from '@bablr/agast-helpers/builders';
|
|
5
|
-
|
|
6
|
-
export { Path, TagPath } from '@bablr/agast-helpers/path';
|
|
7
|
-
|
|
8
|
-
export { allTagPathsFor, ownTagPathsFor } from '@bablr/agast-helpers/path';
|
|
9
|
-
|
|
10
|
-
let { freeze } = Object;
|
|
11
|
-
|
|
12
|
-
let paths = new WeakMap();
|
|
13
|
-
|
|
14
|
-
export class TagPathFacade {
|
|
15
|
-
static from(path, childrenIndex) {
|
|
16
|
-
return path && new TagPathFacade(TagPath.from(paths.get(path), childrenIndex));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
static fromNode(node, childrenIndex) {
|
|
20
|
-
let path = node && TagPath.fromNode(nodes.get(node), childrenIndex);
|
|
21
|
-
return path && new TagPathFacade(path);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
static wrap(tagPath) {
|
|
25
|
-
return tagPath && new TagPathFacade(tagPath);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
constructor(tagPath) {
|
|
29
|
-
if (!(tagPath instanceof TagPath)) throw new Error();
|
|
30
|
-
paths.set(this, tagPath);
|
|
31
|
-
|
|
32
|
-
let { tag } = tagPath;
|
|
33
|
-
|
|
34
|
-
this.path = new PathFacade(tagPath.path);
|
|
35
|
-
this.node = new NodeFacade(tagPath.node);
|
|
36
|
-
this.tag =
|
|
37
|
-
tag.type === Property
|
|
38
|
-
? buildChild(
|
|
39
|
-
Property,
|
|
40
|
-
buildFacadeProperty(
|
|
41
|
-
tag.value.reference,
|
|
42
|
-
tag.value.binding,
|
|
43
|
-
new NodeFacade(tag.value.node),
|
|
44
|
-
),
|
|
45
|
-
)
|
|
46
|
-
: tag;
|
|
47
|
-
this.childrenIndex = tagPath.childrenIndex;
|
|
48
|
-
|
|
49
|
-
freeze(this);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
get child() {
|
|
53
|
-
return this.tag;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
siblingAt(index) {
|
|
57
|
-
return TagPathFacade.wrap(paths.get(this).siblingAt(index));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
get nextSibling() {
|
|
61
|
-
return TagPathFacade.wrap(paths.get(this).nextSibling);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
get next() {
|
|
65
|
-
return TagPathFacade.wrap(paths.get(this).next);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
get nextUnshifted() {
|
|
69
|
-
return TagPathFacade.wrap(paths.get(this).nextUnshifted);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
get previousSibling() {
|
|
73
|
-
return TagPathFacade.wrap(paths.get(this).previousSibling);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
get previous() {
|
|
77
|
-
return TagPathFacade.wrap(paths.get(this).previous);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
get previousUnshifted() {
|
|
81
|
-
return TagPathFacade.wrap(paths.get(this).previousUnshifted);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
get inner() {
|
|
85
|
-
return PathFacade.wrap(paths.get(this).inner);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
equalTo(tagPath) {
|
|
89
|
-
if (tagPath == null) return false;
|
|
90
|
-
|
|
91
|
-
let { path, childrenIndex } = paths.get(this);
|
|
92
|
-
let tagPath_ = paths.get(tagPath);
|
|
93
|
-
return path.node === tagPath_.path.node && childrenIndex === tagPath_.childrenIndex;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export class PathFacade {
|
|
98
|
-
static from(node) {
|
|
99
|
-
return node && new PathFacade(Path.from(nodes.get(node)));
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
static wrap(path) {
|
|
103
|
-
return path && new PathFacade(path);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
constructor(path) {
|
|
107
|
-
if (!(path instanceof Path)) throw new Error();
|
|
108
|
-
this.depth = path.depth;
|
|
109
|
-
|
|
110
|
-
this.node = new NodeFacade(path.node);
|
|
111
|
-
this.referenceIndex = path.referenceIndex;
|
|
112
|
-
|
|
113
|
-
paths.set(this, path);
|
|
114
|
-
|
|
115
|
-
freeze(this);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
get parent() {
|
|
119
|
-
// not a stable reference. can/should it be?
|
|
120
|
-
return PathFacade.wrap(paths.get(this).parent);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
get openTagPath() {
|
|
124
|
-
return TagPathFacade.wrap(paths.get(this).openTagPath);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
get openTag() {
|
|
128
|
-
return paths.get(this).openTag;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
get closeTagPath() {
|
|
132
|
-
return TagPathFacade.wrap(paths.get(this).closeTagPath);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
get closeTag() {
|
|
136
|
-
return paths.get(this).closeTag;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
get reference() {
|
|
140
|
-
return paths.get(this).reference;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
get referenceTagPath() {
|
|
144
|
-
return TagPathFacade.wrap(paths.get(this).referenceTagPath);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
get(path, shiftIndex = null) {
|
|
148
|
-
return new PathFacade(paths.get(this).get(path, shiftIndex));
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
tagPathAt(childrenIndex) {
|
|
152
|
-
return TagPathFacade.wrap(TagPath.from(paths.get(this), childrenIndex));
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
tagAt(childrenIndex) {
|
|
156
|
-
return this.tagPathAt(childrenIndex).tag;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
atDepth(depth) {
|
|
160
|
-
return new PathFacade(paths.get(this).atDepth(depth));
|
|
161
|
-
}
|
|
162
|
-
}
|