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