@bablr/bablr-vm 0.20.1 → 0.22.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/state.js CHANGED
@@ -1,40 +1,46 @@
1
+ import { agast as createAgast, TagPathFacade as TagPath } from '@bablr/agast-vm';
1
2
  import emptyStack from '@iter-tools/imm-stack';
2
3
  import { WeakStackFrame } from '@bablr/weak-stack';
3
- import { getCooked } from '@bablr/agast-helpers/stream';
4
+ import { getCooked, maybeWait } from '@bablr/agast-helpers/stream';
5
+ import * as Tags from '@bablr/agast-helpers/tags';
4
6
  import { reifyExpression } from '@bablr/agast-vm-helpers';
5
7
  import {
6
- CloseNodeTag,
7
- EmbeddedMatcher,
8
- EmbeddedNode,
9
- EmbeddedRegex,
8
+ Matcher,
9
+ Node,
10
+ Regex,
10
11
  GapTag,
11
12
  InitializerTag,
12
13
  OpenNodeTag,
13
14
  ReferenceTag,
14
15
  ShiftTag,
16
+ Property,
17
+ BindingTag,
18
+ PropertyWrapper,
15
19
  } from '@bablr/agast-vm-helpers/symbols';
16
- import * as btree from '@bablr/agast-helpers/btree';
17
- import * as sumtree from '@bablr/agast-helpers/sumtree';
18
20
  import {
19
- buildGapTag,
21
+ buildBindingTag,
22
+ buildChild,
20
23
  buildInitializerTag,
21
24
  buildNullNode,
25
+ buildProperty,
26
+ buildPropertyWrapper,
22
27
  buildReferenceTag,
23
- buildStubNode,
28
+ getOr,
29
+ multiFragmentFlags,
24
30
  } from '@bablr/agast-helpers/tree';
25
- import { getShifted, TagPath } from '@bablr/agast-helpers/path';
26
31
  import { match, guardWithPattern } from './utils/pattern.js';
27
32
  import { facades, actuals } from './facades.js';
28
- import { buildInternalState, FragmentFacade, internalStates } from './node.js';
29
- import { buildCall, buildEmbeddedTag } from '@bablr/agast-vm-helpers/builders';
33
+ import { FragmentFacade } from './node.js';
34
+ import { wrapperIsFull } from '@bablr/agast-helpers/path';
30
35
 
31
- const { hasOwn } = Object;
36
+ const { freeze } = Object;
32
37
 
33
38
  export const nodeStates = new WeakMap();
34
39
 
35
40
  export const StateFacade = class BABLRStateFacade {
36
41
  constructor(state) {
37
42
  facades.set(state, this);
43
+ freeze(this);
38
44
  }
39
45
 
40
46
  static from(source) {
@@ -57,38 +63,34 @@ export const StateFacade = class BABLRStateFacade {
57
63
  return actuals.get(this).result;
58
64
  }
59
65
 
60
- get reference() {
61
- return actuals.get(this).reference;
66
+ get referenceTag() {
67
+ return actuals.get(this).referenceTag;
62
68
  }
63
69
 
64
- get referencePath() {
65
- return actuals.get(this).referencePath;
70
+ get referenceTagPath() {
71
+ return actuals.get(this).referenceTagPath;
66
72
  }
67
73
 
68
- get unshiftedReferencePath() {
69
- return actuals.get(this).unshiftedReferencePath;
74
+ get referencePath() {
75
+ return actuals.get(this).referencePath;
70
76
  }
71
77
 
72
78
  get holding() {
73
79
  return actuals.get(this).holding;
74
80
  }
75
81
 
76
- get path() {
77
- return actuals.get(this).path;
78
- }
79
-
80
82
  get language() {
81
83
  return actuals.get(this).language;
82
84
  }
83
85
 
84
86
  get depths() {
85
- const { path, result } = actuals.get(this).depths;
86
- return { path, result };
87
+ const { path, result, shift, nodeShift } = actuals.get(this).depths;
88
+ return { path, result, shift, nodeShift };
87
89
  }
88
90
 
89
91
  get held() {
90
92
  const { held } = actuals.get(this);
91
- return held && FragmentFacade.wrapNode(held, this.ctx);
93
+ return held && FragmentFacade.wrapNode(held.node, this.ctx);
92
94
  }
93
95
 
94
96
  get node() {
@@ -129,11 +131,11 @@ export const State = class BABLRState extends WeakStackFrame {
129
131
  expressions = emptyStack,
130
132
  balanced = emptyStack,
131
133
  spans = emptyStack.push({ name: 'Bare' }),
132
- referencePath = null,
134
+ referenceTagPath = null,
133
135
  resultPath = null,
134
- depths = { path: -1, result: -1, emitted: -1 },
136
+ depths = { path: -1, result: -1, emitted: -1, shift: 0, nodeShift: 0 },
135
137
  held = null,
136
- node = null,
138
+ agast = null,
137
139
  ) {
138
140
  super(parent);
139
141
 
@@ -145,11 +147,11 @@ export const State = class BABLRState extends WeakStackFrame {
145
147
  this.expressions = expressions;
146
148
  this.balanced = balanced;
147
149
  this.spans = spans;
148
- this.referencePath = referencePath;
150
+ this.referenceTagPath = referenceTagPath;
149
151
  this.resultPath = resultPath;
150
152
  this.depths = depths;
151
153
  this.held = held;
152
- this.node = node;
154
+ this.agast = agast;
153
155
 
154
156
  this.status = 'active';
155
157
 
@@ -159,33 +161,38 @@ export const State = class BABLRState extends WeakStackFrame {
159
161
  }
160
162
 
161
163
  static from(source, context, language, expressions = []) {
162
- return State.create(source, context, language, emptyStack.push(...expressions));
164
+ return State.create(
165
+ source,
166
+ context,
167
+ language,
168
+ emptyStack.push(...emptyStack.push(...expressions).valuesReverse()),
169
+ );
170
+ }
171
+
172
+ get node() {
173
+ return this.agast?.state.node;
163
174
  }
164
175
 
165
- get unshiftedReferencePath() {
166
- let refPath = this.referencePath;
176
+ get referencePath() {
177
+ let refPath = this.referenceTagPath;
167
178
 
168
179
  if (!refPath) return null;
169
180
 
170
181
  let { previousSibling } = refPath;
171
182
  let isShift = previousSibling.tag.type === ShiftTag;
172
183
 
173
- let referencePath = previousSibling;
184
+ let referenceTagPath = previousSibling;
174
185
 
175
186
  if (isShift) {
176
- let refIndex = previousSibling.childrenIndex - 1 - previousSibling.tag.value.index * 2;
177
- referencePath = previousSibling.siblingAt(refIndex);
187
+ let refIndex = previousSibling.tagsIndex - 1 - previousSibling.tag.value.index * 3;
188
+ referenceTagPath = previousSibling.siblingAt(refIndex);
178
189
  }
179
- return referencePath;
180
- }
181
-
182
- get undefinedAttributes() {
183
- return nodeStates.get(this.node).undefinedAttributes;
190
+ return referenceTagPath;
184
191
  }
185
192
 
186
193
  get guardedSource() {
187
- const { source, span } = this;
188
- const { guard } = span;
194
+ let { source, span } = this;
195
+ let { guard } = span;
189
196
 
190
197
  return guard ? guardWithPattern(guard, source) : source;
191
198
  }
@@ -195,7 +202,7 @@ export const State = class BABLRState extends WeakStackFrame {
195
202
  }
196
203
 
197
204
  get path() {
198
- throw new Error('not implemented');
205
+ return this.agast?.state.path;
199
206
  }
200
207
 
201
208
  get result() {
@@ -210,8 +217,8 @@ export const State = class BABLRState extends WeakStackFrame {
210
217
  return !!this.held;
211
218
  }
212
219
 
213
- get reference() {
214
- return this.referencePath?.tag;
220
+ get referenceTag() {
221
+ return this.referenceTagPath?.tag;
215
222
  }
216
223
 
217
224
  get isGap() {
@@ -222,64 +229,42 @@ export const State = class BABLRState extends WeakStackFrame {
222
229
  return !!this.parent;
223
230
  }
224
231
 
225
- advance(tag) {
226
- const { path, instructionsPump, expressionsPump, agast } = internalStates.get(this.node);
227
-
228
- if (tag.type === GapTag) {
229
- expressionsPump.queue(
230
- this.expressions.size ? this.expressions.value : buildStubNode(buildGapTag()),
231
- );
232
- this.expressions = this.expressions.pop();
233
- }
234
- instructionsPump.queue(buildCall('advance', buildEmbeddedTag(tag)));
235
- agast.next();
236
-
237
- if (tag.type === OpenNodeTag) this.depths.result++;
238
- if (tag.type === CloseNodeTag) this.depths.result--;
239
-
240
- this.resultPath = TagPath.from(path, -1);
241
- }
242
-
243
- guardedMatch(pattern) {
232
+ guardedMatch(pattern, attributes = {}) {
244
233
  let { span, source } = this;
245
234
  let { guard } = span;
246
235
 
247
236
  let pattern_ = pattern;
248
- if (pattern.type === EmbeddedMatcher) {
237
+ if (pattern.type === Matcher) {
249
238
  pattern_ = reifyExpression(pattern.value).nodeMatcher;
250
- } else if (pattern.type === EmbeddedRegex || pattern.type === EmbeddedNode) {
239
+ } else if (pattern.type === Regex || pattern.type === Node) {
251
240
  pattern_ = pattern.value;
252
241
  } else if (typeof pattern !== 'string') {
253
242
  throw new Error();
254
243
  }
255
244
 
256
- let openNode =
257
- this.resultPath.tag.type === OpenNodeTag && this.resultPath.tag.value.flags.token
258
- ? this.resultPath.node
259
- : typeof pattern_ === 'string'
260
- ? null
261
- : pattern_;
262
-
263
- if (
264
- span.type === 'Lexical' &&
265
- openNode &&
266
- (openNode.flags.token
267
- ? openNode.attributes.balancer || openNode.attributes.balanced
268
- : openNode.attributes?.balancer)
269
- ) {
245
+ if (span.type === 'Lexical' && attributes.balancer) {
270
246
  // also check that the open node starts a lexical span?
271
247
  guard = null;
272
248
  }
273
249
 
274
- if (pattern_?.intrinsicValue) {
275
- pattern_ = pattern_.intrinsicValue || getCooked(pattern_.children);
250
+ if (pattern_?.literalValue) {
251
+ pattern_ = pattern_.literalValue || getCooked(pattern_.tags);
276
252
 
277
253
  if (pattern_.type === Symbol.for('String')) {
278
254
  pattern_ = reifyExpression(pattern_);
279
255
  }
280
256
  }
281
257
 
282
- return match(pattern_, guard ? guardWithPattern(guard, source) : source);
258
+ let guardedSource = guard && guardWithPattern(guard, source);
259
+
260
+ let result = match(pattern_, guardedSource || source);
261
+
262
+ return maybeWait(result, (result) => {
263
+ if (guardedSource && !guardedSource.done) {
264
+ guardedSource.return();
265
+ }
266
+ return result;
267
+ });
283
268
  }
284
269
 
285
270
  match(pattern) {
@@ -287,151 +272,119 @@ export const State = class BABLRState extends WeakStackFrame {
287
272
  }
288
273
 
289
274
  branch() {
290
- const baseState = this;
275
+ let baseState = this;
291
276
  let {
277
+ agast,
292
278
  source,
293
279
  context,
294
280
  balanced,
295
281
  spans,
296
282
  resultPath,
297
283
  depths,
298
- referencePath,
284
+ referenceTagPath,
299
285
  held,
300
286
  node,
301
287
  language,
302
288
  expressions,
303
289
  } = baseState;
304
290
 
305
- const internalState = buildInternalState();
306
-
307
- for (let tagPath = TagPath.fromNode(node, 0); tagPath; tagPath = tagPath.nextSibling) {
308
- let { tag } = tagPath;
309
-
310
- if (tag.type === GapTag) {
311
- let ref = tagPath.previousSibling.tag;
312
- let firstRef =
313
- ref.type === ShiftTag
314
- ? tagPath.siblingAt(tagPath.childrenIndex - ref.value.index * 2 - 1)
315
- : ref;
316
- let { name, isArray, flags } = firstRef.value;
317
- const resolvedPath = buildReferenceTag(
318
- name,
319
- isArray,
320
- flags,
321
- isArray ? btree.getSize(internalState.path.node.properties[name]) : null,
322
- );
323
- const expr = getShifted(ref.value.index || 0, resolvedPath, node);
324
- internalState.expressionsPump.queue(expr);
325
- } else if (tag.type === EmbeddedNode) {
326
- internalState.expressionsPump.queue(tag.value);
327
- tag = buildGapTag();
328
- }
329
-
330
- internalState.instructionsPump.queue(buildCall('advance', buildEmbeddedTag(tag)));
331
- internalState.agast.next();
291
+ let newAgast = createAgast(agast.options);
292
+
293
+ let fragRefTag = buildReferenceTag('_', null, false, multiFragmentFlags);
294
+
295
+ if (node.tags.openTag) newAgast.vm.next(node.tags.openTag);
296
+ if (node.tags.childrenNode) {
297
+ let property = buildProperty(fragRefTag.value, null, node.tags.childrenNode);
298
+ newAgast.vm.next(
299
+ buildChild(
300
+ PropertyWrapper,
301
+ buildPropertyWrapper(
302
+ [fragRefTag, buildBindingTag(), buildChild(Property, property)],
303
+ property,
304
+ ),
305
+ ),
306
+ );
332
307
  }
308
+ if (node.tags.closeTag) newAgast.vm.next(node.tags.closeTag);
333
309
 
334
- const newNode = internalState.agastState.node;
335
- const nodeState = nodeStates.get(node);
310
+ let newNode = newAgast.state.node;
311
+ let nodeState = nodeStates.get(node);
336
312
  let newResultPath;
337
313
 
338
314
  if (resultPath.path.node === node) {
339
- newResultPath = TagPath.fromNode(newNode, resultPath.childrenIndex);
315
+ newResultPath = TagPath.fromNode(newNode, resultPath.tagsIndex);
340
316
  } else {
341
317
  newResultPath = resultPath;
342
318
  }
343
319
 
344
320
  nodeStates.set(newNode, { ...nodeState });
345
- internalStates.set(newNode, internalState);
346
321
 
347
- const child = this.push(
322
+ let child = this.push(
348
323
  source.branch(),
349
324
  context,
350
325
  language,
351
326
  expressions,
352
327
  balanced,
353
328
  spans,
354
- referencePath,
329
+ referenceTagPath,
355
330
  newResultPath,
356
331
  { ...depths },
357
332
  held,
358
- newNode,
333
+ newAgast,
359
334
  );
360
335
 
361
336
  return child;
362
337
  }
363
338
 
364
339
  accept() {
365
- const accepted = this;
340
+ let accepted = this;
366
341
 
367
342
  this.status = 'accepted';
368
343
 
369
- const { parent } = this;
344
+ let { parent } = this;
370
345
 
371
346
  if (!parent) {
372
347
  throw new Error('accepted the root state');
373
348
  }
374
349
 
375
350
  if (parent.depths.path === accepted.depths.path) {
376
- const parentChildren = parent.node.children;
377
-
378
- const internalState = internalStates.get(parent.node);
379
- const { path: parentPath } = internalState;
351
+ let parentChildren = parent.node.tags;
380
352
 
381
353
  if (parent.node.type !== accepted.node.type) throw new Error();
382
354
 
383
- for (
384
- let i = sumtree.getSize(parentChildren);
385
- i < sumtree.getSize(accepted.node.children);
386
- i++
387
- ) {
388
- let tag = sumtree.getAt(i, accepted.node.children);
389
-
390
- if (tag.type === GapTag) {
391
- let previousSibling = sumtree.getAt(i - 1, accepted.node.children);
392
- let isShift = previousSibling.type === ShiftTag;
393
- let reference = previousSibling;
394
-
395
- if (isShift) {
396
- reference = sumtree.getAt(
397
- i - 1 - previousSibling.value.index * 2,
398
- accepted.node.children,
399
- );
400
- }
355
+ let lastParentProp = parentChildren.at(-1);
401
356
 
402
- let { name, isArray, flags } = reference.value;
403
- const resolvedPath = buildReferenceTag(
404
- name,
405
- isArray,
406
- flags,
407
- isArray ? btree.getSize(parentPath.node.properties[name]) - (isShift ? 1 : 0) : null,
408
- );
409
- const expr = getShifted(
410
- isShift ? previousSibling.value.index : null,
411
- resolvedPath,
412
- accepted.node,
413
- );
414
- internalState.expressionsPump.queue(expr);
415
- } else if (tag.type === EmbeddedNode) {
416
- internalState.expressionsPump.queue(tag.value);
417
- tag = buildGapTag();
418
- }
357
+ let partialOffset =
358
+ lastParentProp.type === PropertyWrapper && !wrapperIsFull(lastParentProp) ? -1 : 0;
359
+
360
+ for (let i = parentChildren.size + partialOffset; i < accepted.node.tags.size; i++) {
361
+ let acceptedTag = accepted.node.tags.at(i);
362
+ let tag = parentChildren.at(i);
419
363
 
420
- internalState.instructionsPump.queue(buildCall('advance', buildEmbeddedTag(tag)));
421
- internalState.agast.next();
364
+ // let wrapperTag =
365
+
366
+ for (
367
+ let i = tag ? Tags.getSize(tag.value.tags) : 0;
368
+ i < Tags.getSize(acceptedTag.value.tags);
369
+ i++
370
+ ) {
371
+ let tag = Tags.getAt(i, acceptedTag.value.tags);
372
+
373
+ parent.agast.vm.next(tag);
374
+ }
422
375
  }
423
376
  }
424
377
 
425
378
  parent.spans = accepted.spans;
426
379
  parent.balanced = accepted.balanced;
427
- parent.referencePath = accepted.referencePath;
380
+ parent.referenceTagPath = accepted.referenceTagPath;
428
381
  parent.held = accepted.held;
429
382
  parent.depths = accepted.depths;
430
383
  parent.language = accepted.language;
431
384
  parent.expressions = accepted.expressions;
432
385
 
433
386
  if (parent.depths.result + 1 === accepted.depths.result) {
434
- parent.resultPath = parent.resultPath.siblingAt(accepted.resultPath.childrenIndex);
387
+ parent.resultPath = parent.resultPath.siblingAt(accepted.resultPath.tagsIndex);
435
388
  } else {
436
389
  parent.resultPath = accepted.resultPath;
437
390
  }
@@ -453,75 +406,83 @@ export const State = class BABLRState extends WeakStackFrame {
453
406
  (!finishedMatch.isNode && !didBranch) ||
454
407
  finishedMatch.cover ||
455
408
  finishedMatch.effects.success === 'none';
456
- let shallower = finishedMatch.didShift ? finishedMatch.shiftMatch.state : this.parent;
457
-
458
- if (shallower) {
459
- let parentChildren = shallower.node.children;
460
- let ourChildren = finishedMatch.fragmentNode.children;
461
- let internalState = internalStates.get(shallower.node);
462
-
463
- if (!abandon && shallower.node.type) {
409
+ let shallower =
410
+ finishedMatch.coveredBoundary.didShift &&
411
+ finishedMatch.coveredBoundary.shiftMatch.state.depth === this.depth - 2
412
+ ? finishedMatch.coveredBoundary.shiftMatch.state
413
+ : this.parent;
414
+
415
+ if (!abandon && shallower) {
416
+ let parentChildren = shallower.node.tags;
417
+ let ourChildren = finishedMatch.fragmentNode.tags;
418
+ let refTag;
419
+
420
+ if (shallower.node.type) {
464
421
  if (shallower.node.type !== rejectedState.node.type) throw new Error();
465
422
 
466
- for (let i = sumtree.getSize(parentChildren); i < sumtree.getSize(ourChildren); i++) {
467
- let tag = sumtree.getAt(i, ourChildren);
468
-
469
- if ([InitializerTag, GapTag].includes(tag.type)) {
470
- let previousSibling = sumtree.getAt(i - 1, ourChildren);
471
- let isShift = previousSibling.type === ShiftTag;
423
+ for (let i = parentChildren.size; i < ourChildren.size; i++) {
424
+ let tag = ourChildren.at(i);
472
425
 
473
- let reference = previousSibling;
474
-
475
- if (isShift) {
476
- let refIndex = i - 1 - previousSibling.value.index * 2;
477
- reference = sumtree.getAt(refIndex, ourChildren);
478
- }
479
-
480
- let lastParentTag = TagPath.from(internalState.path, -1).tag.type;
481
-
482
- if (
483
- !['#', '@'].includes(reference.value.name) &&
484
- !reference.value.isArray &&
485
- !hasOwn(shallower.node.properties, reference.value.name) &&
486
- lastParentTag !== ShiftTag
487
- ) {
488
- if (lastParentTag !== ReferenceTag) {
489
- internalState.instructionsPump.queue(
490
- buildCall('advance', buildEmbeddedTag(reference)),
491
- );
492
- internalState.agast.next();
426
+ if (tag.type === PropertyWrapper) {
427
+ for (let wrappedTag of tag.value.tags) {
428
+ if (wrappedTag.type === ReferenceTag) {
429
+ refTag = wrappedTag;
493
430
  }
494
- if (bind || tag.type === GapTag) {
495
- internalState.expressionsPump.queue(buildNullNode());
496
- internalState.instructionsPump.queue(
497
- buildCall('advance', buildEmbeddedTag(buildGapTag())),
498
- );
499
- } else {
500
- internalState.instructionsPump.queue(buildCall('advance', buildEmbeddedTag(tag)));
431
+
432
+ if ([InitializerTag, GapTag].includes(wrappedTag.type)) {
433
+ let previousSibling = ourChildren.at(i, 0);
434
+ let isShift = previousSibling.type === ShiftTag;
435
+
436
+ let referenceTag = previousSibling;
437
+
438
+ if (isShift) {
439
+ let refIndex = i - 1 - previousSibling.value.index;
440
+ referenceTag = ourChildren.at(refIndex, 0);
441
+ }
442
+
443
+ if (
444
+ !['#', '@'].includes(referenceTag.value.type) &&
445
+ !referenceTag.value.isArray &&
446
+ getOr(0, referenceTag.value.name, shallower.node) === 0 &&
447
+ refTag !== ShiftTag
448
+ ) {
449
+ if (refTag !== ReferenceTag) {
450
+ shallower.agast.vm.next(refTag);
451
+ }
452
+
453
+ if (bind || wrappedTag.type === GapTag) {
454
+ shallower.agast.vm.next(
455
+ buildChild(Property, buildProperty(refTag.value, null, buildNullNode())),
456
+ );
457
+ } else {
458
+ shallower.agast.vm.next(wrappedTag);
459
+ }
460
+ }
461
+ refTag = null;
501
462
  }
502
- internalState.agast.next();
503
463
  }
504
464
  }
505
465
  }
506
466
 
507
- let lastParentTagPath = TagPath.fromNode(shallower.node, -1);
508
- if (lastParentTagPath?.tag.type === ReferenceTag) {
509
- let ref = lastParentTagPath?.tag;
510
- if (bind) {
511
- internalState.expressionsPump.queue(buildNullNode());
512
- internalState.instructionsPump.queue(
513
- buildCall('advance', buildEmbeddedTag(buildGapTag())),
514
- );
515
- internalState.agast.next();
516
- } else {
517
- if (!ref.value.flags.expression) {
518
- internalState.instructionsPump.queue(
519
- buildCall('advance', buildEmbeddedTag(buildInitializerTag(ref.value.isArray))),
467
+ if (refTag?.type === ReferenceTag) {
468
+ if (
469
+ refTag.value.name &&
470
+ !refTag.value.isArray &&
471
+ !shallower.node.properties.has(refTag.value.name)
472
+ ) {
473
+ if (bind) {
474
+ shallower.agast.vm.next(refTag);
475
+ shallower.agast.vm.next(
476
+ buildChild(Property, buildProperty(refTag.value, [], buildNullNode())),
520
477
  );
521
- internalState.agast.next();
478
+ } else {
479
+ if (!refTag.value.flags.expression) {
480
+ shallower.agast.vm.next(refTag);
481
+ shallower.agast.vm.next(buildInitializerTag(refTag.value.isArray));
482
+ }
522
483
  }
523
484
  }
524
- shallower.referencePath = null;
485
+ shallower.referenceTagPath = null;
525
486
  shallower.resultPath = TagPath.fromNode(shallower.node, -1);
526
487
  }
527
488
  }