@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 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 { facades } from './facades.js';
17
- import { getFirstNode, TagPath } from '@bablr/agast-helpers/path';
18
- import * as sumtree from '@bablr/agast-helpers/children';
19
- import { NodeFacade } from './node.js';
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
- return Object.freeze({ options, state: facades.get(state), vm });
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 (s.resultPath && !s.path) {
43
- throw new Error('Cannot advance after completion');
44
- }
45
-
46
- if (
47
- s.held &&
48
- ![OpenNodeTag, ReferenceTag, InitializerTag, Property, BindingTag].includes(tag.type) &&
49
- !(
50
- (tag.type === Property &&
51
- (sumtree.getSize(tag.value.node.children) <= 1 ||
52
- getFirstNode(tag.value.node) === s.held.node)) ||
53
- tag.value.node === s.held.node
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 LiteralTag: {
70
- if (!s.node.flags.token) {
71
- throw new Error('literals must occur inside tokens');
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 ReferenceTag: {
83
- const { type } = tag.value;
84
-
85
- if (s.node?.flags.token && type !== '@') {
86
- throw new Error('A token node cannot contain a reference');
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
- s.advance(tag);
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 { languagePath } = tag.value;
103
+ const { segments } = tag.value;
95
104
 
96
- if (languagePath && !isArray(languagePath)) throw new Error();
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
- returnValue = new NodeFacade(s.resultPath.node);
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
- if (s.atReference) throw new Error();
111
-
112
- s.advance(tag);
121
+ s.path = s.path.advance(tag);
113
122
  break;
114
123
  }
115
124
 
116
- case InitializerTag: {
117
- if (!s.atReference) throw new Error();
118
-
119
- s.advance(tag);
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 ShiftTag: {
124
- const { index } = tag.value;
125
- if (s.resultPath.tag.type !== Property) throw new Error();
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
- let prevRef = s.resultPath.previousSibling.previousSibling;
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
- if (prevRef.tag.type === ShiftTag) {
130
- if (prevRef.tag.value.index + 1 !== index) throw new Error('bad shift index');
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
- let refPath = TagPath.from(
134
- s.resultPath.path,
135
- s.resultPath.childrenIndex - (index - 1) * 3 - 2,
136
- );
159
+ validNodes.add(s.node);
137
160
 
138
- if (refPath.tag.type !== ReferenceTag) throw new Error();
161
+ returnValue = s.node;
139
162
 
140
- if (!refPath.tag.value.flags.expression) {
141
- throw new Error();
142
- }
163
+ let donePath = s.path;
164
+
165
+ s.path = s.path.advance(tag);
143
166
 
144
- s.advance(tag);
167
+ s.resultPath = donePath.tagPathAt(-1, -1);
145
168
  break;
146
169
  }
147
170
 
148
- case OpenNodeTag: {
149
- s.advance(tag);
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 CloseNodeTag: {
156
- returnValue = new NodeFacade(s.node);
182
+ case NullNode:
183
+ case GapNode:
184
+ case TreeNode: {
185
+ let node = tag;
157
186
 
158
- s.advance(tag);
159
- break;
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
- case Property: {
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 = new NodeFacade(node);
198
+ returnValue = s.path.node;
168
199
  break;
169
200
  }
170
201
 
package/lib/index.js CHANGED
@@ -1,4 +1 @@
1
- export { agast } from './evaluate.js';
2
-
3
- export { PathFacade, TagPathFacade } from './path.js';
4
- export { NodeFacade } from './node.js';
1
+ export { agast, isKnownValid } from './evaluate.js';
package/lib/state.js CHANGED
@@ -1,164 +1,25 @@
1
- import {
2
- addProperty,
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
- static from(held) {
149
- return new State(held);
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 = targetPath && TagPath.from(targetPath, -1);
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.9.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
- "@bablr/agast-helpers": "0.8.0",
16
- "@bablr/agast-vm-helpers": "0.8.0"
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": "github:bablr-lang/agast-vm",
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
@@ -1,3 +0,0 @@
1
- import { buildFacadeLayer } from '@bablr/agast-vm-helpers/facades';
2
-
3
- export const { facades, actuals } = buildFacadeLayer();
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
- }