@bablr/agast-vm 0.7.0 → 0.8.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
@@ -8,11 +8,11 @@ import {
8
8
  buildDoctypeTag,
9
9
  buildOpenNodeTag,
10
10
  buildCloseNodeTag,
11
+ buildInitializerTag,
11
12
  } from '@bablr/agast-vm-helpers/internal-builders';
12
13
  import { getEmbeddedTag } from '@bablr/agast-vm-helpers/deembed';
13
14
  import { StreamIterable, getStreamIterator } from '@bablr/agast-helpers/stream';
14
15
  import { printExpression } from '@bablr/agast-helpers/print';
15
- import { buildArrayInitializerTag } from '@bablr/agast-helpers/tree';
16
16
  import {
17
17
  DoctypeTag,
18
18
  OpenNodeTag,
@@ -21,12 +21,14 @@ import {
21
21
  ShiftTag,
22
22
  GapTag,
23
23
  NullTag,
24
- ArrayInitializerTag,
24
+ InitializerTag,
25
25
  LiteralTag,
26
26
  } from '@bablr/agast-helpers/symbols';
27
27
  import { State } from './state.js';
28
28
  import { Context } from './context.js';
29
29
  import { facades } from './facades.js';
30
+ import { add, TagPath } from '@bablr/agast-helpers/path';
31
+ import { buildNullNode, defineAttribute } from '@bablr/agast-helpers/tree';
30
32
 
31
33
  export const agast = (strategy, options = {}) => {
32
34
  const ctx = Context.create();
@@ -112,13 +114,13 @@ function* __agast(ctx, s, instructions) {
112
114
  break;
113
115
  }
114
116
 
115
- case ArrayInitializerTag: {
117
+ case InitializerTag: {
118
+ const { isArray } = tag.value;
116
119
  const { reference } = s;
117
120
 
118
121
  if (reference?.type !== ReferenceTag) throw new Error();
119
- if (!reference.value.isArray) throw new Error();
120
122
 
121
- returnValue = s.advance(buildArrayInitializerTag());
123
+ returnValue = s.advance(buildInitializerTag(isArray));
122
124
  break;
123
125
  }
124
126
 
@@ -126,7 +128,18 @@ function* __agast(ctx, s, instructions) {
126
128
  const { index } = tag.value;
127
129
  if (s.resultPath.tag.type !== GapTag) throw new Error();
128
130
 
129
- const refPath = s.resultPath.previousSibling;
131
+ let prevRef = s.resultPath.previousSibling;
132
+
133
+ if (prevRef.tag.type === ShiftTag) {
134
+ if (prevRef.tag.value.index + 1 !== index) throw new Error('bad shift index');
135
+ }
136
+
137
+ const refPath = TagPath.from(
138
+ s.resultPath.path,
139
+ s.resultPath.childrenIndex - (index - 1) * 2 - 1,
140
+ );
141
+
142
+ if (refPath.tag.type !== ReferenceTag) throw new Error();
130
143
 
131
144
  if (!refPath.tag.value.flags.expression) {
132
145
  throw new Error();
@@ -148,6 +161,20 @@ function* __agast(ctx, s, instructions) {
148
161
  }
149
162
 
150
163
  case CloseNodeTag: {
164
+ for (const { 0: key, 1: value } of Object.entries(s.node.properties)) {
165
+ if (!Array.isArray(value)) {
166
+ if (value.node === undefined) {
167
+ add(s.node, value.reference, buildNullNode());
168
+ }
169
+ }
170
+ }
171
+
172
+ for (const { 0: key, 1: value } of Object.entries(s.node.attributes)) {
173
+ if (value === undefined) {
174
+ defineAttribute(s.node, key, null);
175
+ }
176
+ }
177
+
151
178
  returnValue = s.advance(buildCloseNodeTag());
152
179
  break;
153
180
  }
package/lib/state.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import isArray from 'iter-tools-es/methods/is-array';
2
2
  import { WeakStackFrame } from '@bablr/weak-stack';
3
3
  import {
4
- Resolver,
5
4
  add,
6
5
  finalizeNode,
7
6
  getRoot,
@@ -10,9 +9,15 @@ import {
10
9
  buildNullTag,
11
10
  treeFromStream,
12
11
  createNode,
12
+ buildReferenceTag,
13
13
  } from '@bablr/agast-helpers/tree';
14
- import * as btree from '@bablr/agast-helpers/btree';
15
- import { TagPath, updatePath } from '@bablr/agast-helpers/path';
14
+ import * as sumtree from '@bablr/agast-helpers/sumtree';
15
+ import {
16
+ getInitializerChildrenIndex,
17
+ getPropertyChildrenIndex,
18
+ referencesAreEqual,
19
+ TagPath,
20
+ } from '@bablr/agast-helpers/path';
16
21
  import {
17
22
  DoctypeTag,
18
23
  OpenNodeTag,
@@ -21,13 +26,15 @@ import {
21
26
  ShiftTag,
22
27
  GapTag,
23
28
  NullTag,
24
- ArrayInitializerTag,
29
+ InitializerTag,
25
30
  LiteralTag,
26
31
  } from '@bablr/agast-helpers/symbols';
27
32
  import { facades, actuals } from './facades.js';
28
33
  import { Path } from './path.js';
29
34
  import { Coroutine } from '@bablr/coroutine';
30
35
 
36
+ const { hasOwn } = Object;
37
+
31
38
  export const StateFacade = class AgastStateFacade {
32
39
  constructor(state) {
33
40
  facades.set(state, this);
@@ -49,6 +56,10 @@ export const StateFacade = class AgastStateFacade {
49
56
  return actuals.get(this).referencePath;
50
57
  }
51
58
 
59
+ get unshiftedReferencePath() {
60
+ return actuals.get(this).unshiftedReferencePath;
61
+ }
62
+
52
63
  get context() {
53
64
  return facades.get(actuals.get(this).context);
54
65
  }
@@ -92,7 +103,6 @@ export const State = class AgastState extends WeakStackFrame {
92
103
  path = Path.from(createNode()),
93
104
  held = null,
94
105
  resultPath = null,
95
- resolver = new Resolver(),
96
106
  ) {
97
107
  super(parent);
98
108
 
@@ -103,7 +113,7 @@ export const State = class AgastState extends WeakStackFrame {
103
113
  this.path = path;
104
114
  this.held = held;
105
115
  this.resultPath = resultPath;
106
- this.resolver = resolver;
116
+ this.referencePath = null;
107
117
 
108
118
  if (!this.node) throw new Error();
109
119
 
@@ -118,71 +128,84 @@ export const State = class AgastState extends WeakStackFrame {
118
128
  return !!this.held;
119
129
  }
120
130
 
121
- advance(tag) {
122
- const ctx = this.context;
131
+ get atReference() {
132
+ return !![ReferenceTag, ShiftTag].includes(this.resultPath?.tag.type);
133
+ }
123
134
 
135
+ advance(tag) {
124
136
  if (!tag) throw new Error();
125
137
 
126
138
  let targetPath = this.path;
127
139
 
128
140
  switch (tag.type) {
129
141
  case DoctypeTag: {
130
- const { path, node } = this;
142
+ let { path, node } = this;
131
143
 
132
- node.children = btree.push(node.children, tag);
133
- node.attributes = tag.value.attributes;
144
+ if (this.resultPath) throw new Error('invalid location for doctype');
134
145
 
135
- this.resolver.advance(tag);
136
- updatePath(this.path, tag);
146
+ node.children = sumtree.push(node.children, tag);
147
+ node.attributes = tag.value.attributes;
137
148
 
138
149
  targetPath = path;
139
150
  break;
140
151
  }
141
152
 
142
153
  case ReferenceTag: {
143
- this.resolver.advance(tag);
154
+ let { name, isArray, flags } = tag.value;
155
+
156
+ if (this.atReference) throw new Error('invalid location for reference');
157
+ if (!name) throw new Error('missing reference name');
158
+ if (!/#|@|[.]|[a-zA-Z]+/.test(name)) throw new Error('invalid reference name');
144
159
 
145
- this.node.children = btree.push(this.node.children, tag);
146
- updatePath(this.path, tag);
160
+ if (this.node.properties[name] && this.node.properties[name]?.node === undefined) {
161
+ let existingReferenceIndex = getInitializerChildrenIndex(this.node, tag);
162
+ let existingReference = sumtree.getAt(existingReferenceIndex, this.node.children);
163
+ if (!referencesAreEqual(tag, existingReference)) {
164
+ throw new Error("reference didn't match initializer");
165
+ }
166
+ }
147
167
 
148
- const { hasGap } = tag.value;
168
+ this.node.children = sumtree.push(this.node.children, tag);
149
169
 
150
- if (hasGap && !this.node.flags.hasGap) {
170
+ if (flags.hasGap && !this.node.flags.hasGap) {
151
171
  throw new Error('gap reference in gapless node');
152
172
  }
153
173
 
174
+ this.referencePath = TagPath.from(this.path, -1);
175
+
154
176
  break;
155
177
  }
156
178
 
157
179
  case OpenNodeTag: {
158
- const parentNode = this.node;
180
+ let parentNode = this.node;
159
181
 
160
182
  targetPath = this.path;
161
183
 
184
+ if (!this.atReference && this.parent) {
185
+ throw new Error('Invalid location for OpenNodeTag');
186
+ }
187
+
162
188
  if (!this.path.depth) {
163
189
  this.node.flags = tag.value.flags;
164
- this.node.children = btree.push(this.node.children, tag);
190
+ this.node.children = sumtree.push(this.node.children, tag);
165
191
  this.node.type = tag.value.type;
166
192
  this.node.language = tag.value.language;
167
193
  this.node.attributes = tag.value.attributes || {};
168
194
  } else {
169
195
  let node = createNode(tag);
170
- this.path = this.path.push(node, btree.getSum(parentNode.children) - 2);
196
+ this.path = this.path.push(node, sumtree.getSize(parentNode.children) - 2);
171
197
  add(parentNode, this.reference, node);
172
198
  }
173
199
 
174
- this.resolver.advance(tag);
175
- updatePath(this.path, tag);
176
-
177
200
  break;
178
201
  }
179
202
 
180
203
  case CloseNodeTag: {
181
- this.resolver.advance(tag);
182
- this.node.children = btree.push(this.node.children, tag);
183
- updatePath(this.path, tag);
204
+ if (this.atReference) throw new Error('invalid location for close tag');
205
+
206
+ this.node.children = sumtree.push(this.node.children, tag);
184
207
 
185
- if (this.node.unboundAttributes?.size)
208
+ if (this.node.undefinedAttributes?.size)
186
209
  throw new Error('Grammar failed to bind all attributes');
187
210
 
188
211
  finalizeNode(this.node);
@@ -194,21 +217,16 @@ export const State = class AgastState extends WeakStackFrame {
194
217
  case GapTag: {
195
218
  let target;
196
219
  let lastTagPath = TagPath.from(this.path, -1);
197
- let refPath = lastTagPath;
198
220
 
199
- while (refPath && refPath.tag.type !== ReferenceTag) {
200
- refPath = refPath.previousSibling;
221
+ if (!this.atReference) {
222
+ throw new Error('Invalid location for GapTag');
201
223
  }
202
224
 
203
- this.resolver.advance(tag);
204
-
205
- let wasHeld = this.held;
206
-
207
225
  if (this.held && lastTagPath.tag.type !== ShiftTag) {
208
226
  target = this.held.node;
209
227
 
210
228
  this.held = null;
211
- } else if (refPath) {
229
+ } else {
212
230
  this.expressions.advance();
213
231
 
214
232
  if (!this.expressions.done) {
@@ -230,57 +248,83 @@ export const State = class AgastState extends WeakStackFrame {
230
248
  }
231
249
 
232
250
  this.held = null;
233
- } else {
234
- this.node.language = null;
235
- this.node.type = null;
236
- target = this.node;
237
251
  }
238
252
 
239
253
  const range = getRange(target);
240
254
 
241
255
  this.resultPath = range ? range[1] : this.resultPath;
256
+ this.referencePath = null;
257
+
258
+ let lastRefPath = [ShiftTag, ReferenceTag].includes(lastTagPath.tag.type)
259
+ ? lastTagPath
260
+ : lastTagPath.previousSibling;
261
+
262
+ if (![ShiftTag, ReferenceTag].includes(lastRefPath.tag.type)) throw new Error();
263
+
264
+ let refTag = lastRefPath.tag;
265
+
266
+ if (refTag.type === ShiftTag) {
267
+ let refIndex = lastRefPath.childrenIndex - lastRefPath.tag.value.index * 2;
268
+ refTag = sumtree.getAt(refIndex, this.node.children);
269
+ }
242
270
 
243
- if (refPath) {
244
- add(this.path.node, refPath.tag, target, wasHeld);
271
+ if (refTag) {
272
+ add(
273
+ this.path.node,
274
+ refTag,
275
+ target,
276
+ lastRefPath.tag.type === ShiftTag
277
+ ? lastRefPath.tag.value.index
278
+ : refTag.value.flags.expression
279
+ ? 0
280
+ : null,
281
+ );
245
282
  }
246
283
 
247
- updatePath(this.path, tag);
248
284
  break;
249
285
  }
250
286
 
251
287
  case NullTag: {
252
288
  const { node: parentNode, reference } = this;
253
- const { name } = reference.value;
254
-
255
- this.resolver.advance(tag);
256
- updatePath(this.path, tag);
257
289
 
258
- if (!name) throw new Error();
290
+ if (!this.atReference && this.parent) {
291
+ throw new Error('Invalid location for OpenNodeTag');
292
+ }
259
293
 
260
294
  let stubNode = buildStubNode(tag);
261
295
 
262
296
  add(parentNode, reference, stubNode);
263
297
 
264
- targetPath = this.path.push(stubNode, btree.getSum(this.node.children) - 2);
298
+ targetPath = this.path.push(stubNode, sumtree.getSize(this.node.children) - 2);
265
299
  break;
266
300
  }
267
301
 
268
- case ArrayInitializerTag: {
302
+ case InitializerTag: {
303
+ const { isArray } = tag.value;
269
304
  const { node, reference } = this;
270
- this.resolver.advance(tag);
305
+ const { name } = reference.value;
271
306
 
272
- add(node, reference, []);
273
- updatePath(this.path, tag);
307
+ if (!this.atReference && this.parent) {
308
+ throw new Error('Invalid location for OpenNodeTag');
309
+ }
310
+
311
+ if (node.properties[name] && node.properties[name]?.node === undefined) {
312
+ throw new Error();
313
+ }
314
+
315
+ add(node, reference, isArray ? [] : undefined);
274
316
  break;
275
317
  }
276
318
 
277
319
  case ShiftTag: {
278
- this.resolver.advance(tag);
279
- updatePath(this.path, tag);
320
+ if (this.atReference) throw new Error('invalid location for shift');
280
321
 
281
322
  const finishedPath = this.resultPath.innerPath;
282
323
  const finishedNode = finishedPath.node;
283
- const ref = this.resultPath.previousSibling.tag;
324
+ const ref = TagPath.from(
325
+ this.resultPath,
326
+ this.resultPath.childrenIndex - tag.value.index * 2 + 1,
327
+ ).tag;
284
328
 
285
329
  this.held = { node: finishedNode, path: finishedPath };
286
330
 
@@ -288,7 +332,9 @@ export const State = class AgastState extends WeakStackFrame {
288
332
 
289
333
  if (!ref.value.flags.expression) throw new Error();
290
334
 
291
- this.node.children = btree.push(this.node.children, tag);
335
+ this.node.children = sumtree.push(this.node.children, tag);
336
+
337
+ this.referencePath = TagPath.from(this.path, -1);
292
338
 
293
339
  // this.path = finishedPath;
294
340
  targetPath = this.path;
@@ -296,9 +342,9 @@ export const State = class AgastState extends WeakStackFrame {
296
342
  }
297
343
 
298
344
  case LiteralTag:
299
- this.resolver.advance(tag);
300
- updatePath(this.path, tag);
301
- this.node.children = btree.push(this.node.children, tag);
345
+ if (typeof tag.value !== 'string') throw new Error();
346
+
347
+ this.node.children = sumtree.push(this.node.children, tag);
302
348
  break;
303
349
 
304
350
  default:
@@ -327,11 +373,7 @@ export const State = class AgastState extends WeakStackFrame {
327
373
  }
328
374
 
329
375
  get reference() {
330
- return this.resolver.reference;
331
- }
332
-
333
- get referencePath() {
334
- return this.reference && this.path.referencePath;
376
+ return this.referencePath.tag;
335
377
  }
336
378
 
337
379
  get node() {
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.7.0",
4
+ "version": "0.8.0",
5
5
  "author": "Conrad Buck<conartist6@gmail.com>",
6
6
  "type": "module",
7
7
  "files": [
@@ -12,10 +12,10 @@
12
12
  },
13
13
  "sideEffects": false,
14
14
  "dependencies": {
15
- "@bablr/agast-helpers": "0.6.0",
16
- "@bablr/agast-vm-helpers": "0.6.0",
15
+ "@bablr/agast-helpers": "0.7.0",
16
+ "@bablr/agast-vm-helpers": "0.7.0",
17
17
  "@bablr/coroutine": "0.1.0",
18
- "@bablr/weak-stack": "0.1.0",
18
+ "@bablr/weak-stack": "1.0.0",
19
19
  "@iter-tools/imm-stack": "1.1.0"
20
20
  },
21
21
  "devDependencies": {