@bablr/bablr-vm 0.19.1 → 0.20.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/match.js CHANGED
@@ -1,32 +1,16 @@
1
1
  import { resolveLanguage } from '@bablr/helpers/grammar';
2
2
  import { WeakStackFrame } from '@bablr/weak-stack';
3
3
 
4
- import { allTagPathsFor, allTagsFor, get, isGapNode, TagPath } from '@bablr/agast-helpers/path';
5
- import { isEmpty } from '@bablr/agast-helpers/stream';
6
- import * as btree from '@bablr/agast-helpers/btree';
7
- import {
8
- buildCall,
9
- buildEmbeddedNode,
10
- buildEmbeddedTag,
11
- buildGapTag,
12
- buildReferenceTag,
13
- mergeReferences,
14
- } from '@bablr/agast-helpers/tree';
15
- import { effectsFor, shouldBranch } from '@bablr/agast-vm-helpers';
4
+ import { getChildPropertyIndex, Path, TagPath } from '@bablr/agast-helpers/path';
5
+ import * as sumtree from '@bablr/agast-helpers/sumtree';
6
+ import { buildGapTag, buildReferenceTag, mergeReferences } from '@bablr/agast-helpers/tree';
7
+ import { effectsFor } from '@bablr/agast-vm-helpers';
16
8
  import { buildInternalState, internalStates, FragmentFacade } from './node.js';
17
9
 
18
10
  import { facades, actuals } from './facades.js';
19
- import {
20
- CloseNodeTag,
21
- EmbeddedNode,
22
- GapTag,
23
- OpenNodeTag,
24
- ReferenceTag,
25
- ShiftTag,
26
- } from '@bablr/agast-helpers/symbols';
11
+ import { CloseNodeTag, OpenNodeTag } from '@bablr/agast-helpers/symbols';
27
12
  import { nodeStates } from './state.js';
28
-
29
- const nodeTopType = Symbol.for('@bablr/node');
13
+ import { buildCall, buildEmbeddedTag } from '@bablr/agast-vm-helpers/builders';
30
14
 
31
15
  const tagPathsAreEqual = (a, b) => {
32
16
  return !a || !b ? a == b : a.path.node === b.path.node && a.childrenIndex === b.childrenIndex;
@@ -45,6 +29,10 @@ export class MatchFacade {
45
29
  return actuals.get(this).matcher;
46
30
  }
47
31
 
32
+ get options() {
33
+ return actuals.get(this).options;
34
+ }
35
+
48
36
  get mergedReference() {
49
37
  return actuals.get(this).mergedReference;
50
38
  }
@@ -65,6 +53,37 @@ export class MatchFacade {
65
53
  return actuals.get(this).innerPath;
66
54
  }
67
55
 
56
+ get fragment() {
57
+ const {
58
+ ctx,
59
+ path,
60
+ isNode,
61
+ fragmentNode,
62
+ mergedReference,
63
+ rangePreviousIndex,
64
+ rangeFinalIndex,
65
+ } = actuals.get(this);
66
+
67
+ const { name, isArray } = mergedReference.value;
68
+
69
+ if (isNode && !['#', '@'].includes(name)) {
70
+ return new FragmentFacade(
71
+ fragmentNode,
72
+ ctx,
73
+ false,
74
+ true,
75
+ [rangePreviousIndex + 1, rangeFinalIndex],
76
+ mergedReference,
77
+ isArray ? getChildPropertyIndex(fragmentNode, rangePreviousIndex + 1) : null,
78
+ );
79
+ } else {
80
+ return new FragmentFacade(fragmentNode || path.node, ctx, false, true, [
81
+ rangePreviousIndex + 1,
82
+ rangeFinalIndex,
83
+ ]);
84
+ }
85
+ }
86
+
68
87
  get pathDepth() {
69
88
  return actuals.get(this).depths.path;
70
89
  }
@@ -82,14 +101,8 @@ export class MatchFacade {
82
101
  return FragmentFacade.wrap(node, ctx, false);
83
102
  }
84
103
 
85
- get fragmentNode() {
86
- const { fragmentNode, ctx } = actuals.get(this);
87
- return FragmentFacade.wrap(fragmentNode, ctx, false);
88
- }
89
-
90
104
  get props() {
91
- const { props, ctx } = actuals.get(this);
92
- return FragmentFacade.wrap(props, ctx, false);
105
+ return actuals.get(this).props;
93
106
  }
94
107
 
95
108
  get type() {
@@ -104,8 +117,24 @@ export class MatchFacade {
104
117
  return actuals.get(this).isCover;
105
118
  }
106
119
 
120
+ get allowEmpty() {
121
+ return actuals.get(this).allowEmpty;
122
+ }
123
+
124
+ get didShift() {
125
+ return actuals.get(this).didShift;
126
+ }
127
+
128
+ get isCoverBoundary() {
129
+ return actuals.get(this).isCoverBoundary;
130
+ }
131
+
107
132
  get cover() {
108
- return actuals.get(this).cover;
133
+ return facades.get(actuals.get(this).cover);
134
+ }
135
+
136
+ get shiftMatch() {
137
+ return facades.get(actuals.get(this).shiftMatch);
109
138
  }
110
139
 
111
140
  get captured() {
@@ -162,16 +191,7 @@ export class MatchFacade {
162
191
  }
163
192
 
164
193
  export class Match extends WeakStackFrame {
165
- constructor(
166
- parent,
167
- context,
168
- language,
169
- state,
170
- matcher,
171
- effects,
172
- didShift = false,
173
- suppressNode = false,
174
- ) {
194
+ constructor(parent, context, language, state, matcher, effects, shiftMatch = null, options = {}) {
175
195
  if (!context || !language || !state) {
176
196
  throw new Error('Invalid arguments to Match constructor');
177
197
  }
@@ -180,26 +200,28 @@ export class Match extends WeakStackFrame {
180
200
 
181
201
  this.context = context;
182
202
  this.language = language;
183
- this.state = shouldBranch(effects) ? state.branch() : state;
203
+ this.state = state;
184
204
  this.propertyMatcher = matcher;
185
205
  this.effects = effects;
186
- this.didShift = didShift;
206
+ this.shiftMatch = shiftMatch;
187
207
 
188
208
  this.rangePreviousIndex = null;
189
209
  this.rangeFinalIndex = null;
190
210
  this.node = null;
191
211
  this.fragmentNode = null; // why do it this way?
192
212
  this.cover = null;
193
- this.suppressNode = suppressNode;
213
+ this.options = options;
194
214
 
195
215
  let internalState;
196
- let { isNode, isCover } = this;
216
+ let { isNode } = this;
217
+ const { grammar, type } = this;
218
+ let isCover = grammar.covers?.has(type);
197
219
 
198
- this.cover = isCover ? this : isNode ? null : parent?.cover || null;
220
+ this.cover = isNode ? null : parent?.cover || (isCover ? this : null);
199
221
 
200
- let shouldConstructNode = (isNode && !parent.isCover) || isCover || !parent;
222
+ let isCoverBoundary = ((isNode || isCover) && !this.cover) || !parent;
201
223
 
202
- if (shouldConstructNode) {
224
+ if (isCoverBoundary) {
203
225
  internalState = buildInternalState();
204
226
  internalState.path.node;
205
227
  } else {
@@ -221,8 +243,12 @@ export class Match extends WeakStackFrame {
221
243
  new MatchFacade(this);
222
244
  }
223
245
 
224
- static from(context, language, state, matcher, props, suppressNode) {
225
- return Match.create(context, language, state, matcher, effectsFor('eat'), props, suppressNode);
246
+ static from(context, language, state, matcher, props, options) {
247
+ return Match.create(context, language, state, matcher, effectsFor('eat'), props, options);
248
+ }
249
+
250
+ get isCoverBoundary() {
251
+ return (this.isNode && !this.parent.cover) || this.cover === this;
226
252
  }
227
253
 
228
254
  get matcher() {
@@ -233,15 +259,21 @@ export class Match extends WeakStackFrame {
233
259
  let ref = buildReferenceTag('.');
234
260
 
235
261
  let first = true;
236
- for (const m of this.ancestors(true)) {
262
+ let m = this;
263
+ let lastName = null;
264
+ do {
237
265
  if (m.isNode && !first) break;
238
266
  if (m.propertyMatcher.refMatcher) {
239
267
  const { name, isArray, flags } = m.propertyMatcher.refMatcher;
240
268
  const parentRef = buildReferenceTag(name, isArray, flags);
269
+ if (lastName && lastName !== name) break;
241
270
  ref = ['#', '@'].includes(ref.value.name) ? ref : mergeReferences(ref, parentRef);
271
+ if (name !== '.') {
272
+ lastName = name;
273
+ }
242
274
  }
243
275
  first = false;
244
- }
276
+ } while ((m = m.shiftMatch || m.parent));
245
277
 
246
278
  return ref;
247
279
  }
@@ -264,6 +296,10 @@ export class Match extends WeakStackFrame {
264
296
  return m;
265
297
  }
266
298
 
299
+ get coveredBoundary() {
300
+ return this.isNode ? this.parent.cover || this : this.cover || this;
301
+ }
302
+
267
303
  get parentPath() {
268
304
  return this.pathParent?.path;
269
305
  }
@@ -293,11 +329,13 @@ export class Match extends WeakStackFrame {
293
329
  }
294
330
 
295
331
  get allowEmpty() {
296
- return !!this.grammar.emptyables?.has(this.type);
332
+ return !!this.grammar.emptyables?.has(this.type) || this.options.allowEmpty;
297
333
  }
298
334
 
299
335
  get rangePrevious() {
300
- return this.rangePreviousIndex == null ? null : new TagPath(this.path, this.rangePreviousIndex);
336
+ return this.rangePreviousIndex == null
337
+ ? null
338
+ : TagPath.from(this.path, this.rangePreviousIndex);
301
339
  }
302
340
 
303
341
  setRangePreviousIndex(value) {
@@ -311,7 +349,7 @@ export class Match extends WeakStackFrame {
311
349
  }
312
350
 
313
351
  get rangeFinal() {
314
- const path = !this.parent ? this.path : this.parent.path;
352
+ const path = Path.from(this.fragmentNode || this.node);
315
353
 
316
354
  return this.rangeFinalIndex == null ? null : new TagPath(path, this.rangeFinalIndex);
317
355
  }
@@ -321,13 +359,6 @@ export class Match extends WeakStackFrame {
321
359
 
322
360
  if (!rangePrevious) return rangePrevious;
323
361
 
324
- if (rangePrevious.tag.type === ShiftTag) {
325
- let tagPath = rangePrevious;
326
- while (tagPath.tag.type !== ReferenceTag) {
327
- tagPath = tagPath.previousSibling;
328
- }
329
- return tagPath;
330
- }
331
362
  return rangePrevious?.nextSibling;
332
363
  }
333
364
 
@@ -336,7 +367,11 @@ export class Match extends WeakStackFrame {
336
367
  return rangeInitial === null ? null : [rangeInitial, rangeFinal];
337
368
  }
338
369
 
339
- get referencePath() {
370
+ get didShift() {
371
+ return !!this.shiftMatch;
372
+ }
373
+
374
+ get unshiftedReferencePath() {
340
375
  const previous = this.rangePrevious;
341
376
 
342
377
  if (!this.isNode) {
@@ -344,28 +379,24 @@ export class Match extends WeakStackFrame {
344
379
  }
345
380
 
346
381
  if (this.parent.cover) {
347
- return previous.nextSibling;
382
+ return previous;
348
383
  }
349
384
 
350
385
  if (this.didShift) {
351
- let tagPath = previous;
352
- while ([GapTag, ShiftTag].includes(tagPath.tag.type)) {
353
- tagPath = tagPath.previousSibling;
354
- }
355
- return tagPath;
386
+ let refIndex = previous.childrenIndex - previous.tag.value.index * 2;
387
+ return previous.siblingPathAt(refIndex);
356
388
  }
357
389
 
358
- return previous.nextSibling;
390
+ return previous;
359
391
  }
360
392
 
361
393
  get isNode() {
362
- const { grammar, type } = this;
363
- return !this.suppressNode && grammar.covers?.get(nodeTopType).has(type);
394
+ let { flags, type } = this.matcher;
395
+ return !flags.fragment && type !== Symbol.for('@bablr/fragment');
364
396
  }
365
397
 
366
398
  get isCover() {
367
- const { grammar, type } = this;
368
- return grammar.covers?.has(type);
399
+ return this.cover === this;
369
400
  }
370
401
 
371
402
  get innerPath() {
@@ -373,9 +404,8 @@ export class Match extends WeakStackFrame {
373
404
  }
374
405
 
375
406
  add(node) {
376
- const { expressionsPump, instructionsPump, agast, agastState } = internalStates.get(
377
- this.fragmentNode,
378
- );
407
+ let internalState = internalStates.get(this.fragmentNode);
408
+ const { expressionsPump, instructionsPump, agast, agastState } = internalState;
379
409
 
380
410
  expressionsPump.queue(node);
381
411
  instructionsPump.queue(buildCall('advance', buildEmbeddedTag(buildGapTag())));
@@ -383,7 +413,7 @@ export class Match extends WeakStackFrame {
383
413
  agast.next();
384
414
  }
385
415
 
386
- startFrame(state, propertyMatcher, effects, didShift, suppressNode) {
416
+ startFrame(state, propertyMatcher, effects, didShift, options) {
387
417
  let { context } = this;
388
418
  const { nodeMatcher } = propertyMatcher;
389
419
 
@@ -393,37 +423,29 @@ export class Match extends WeakStackFrame {
393
423
  throw new Error(`Unknown language ${nodeMatcher.language.join('.')}`);
394
424
  }
395
425
 
396
- return this.push(context, language, state, propertyMatcher, effects, didShift, suppressNode);
426
+ return this.push(context, language, state, propertyMatcher, effects, didShift, options);
397
427
  }
398
428
 
399
429
  endFrame() {
400
430
  const finishedMatch = this;
401
431
  const m = finishedMatch.parent;
402
- let finishedState = finishedMatch.state;
403
432
 
404
- // if (
405
- // finishedMatch.isNode &&
406
- // finishedState.status !== 'rejected' &&
407
- // !getCloseTag(finishedMatch.node)
408
- // )
409
- // throw new Error();
433
+ if (!m) return m;
434
+
435
+ finishedMatch.setRangeFinalIndex(
436
+ sumtree.getSize((finishedMatch.fragmentNode || m.node).children) - 1,
437
+ );
438
+
439
+ return m;
440
+ }
441
+
442
+ throw_() {
443
+ const finishedMatch = this;
444
+ const m = finishedMatch.parent;
410
445
 
411
446
  if (!m) return m;
412
447
 
413
- if (finishedState !== m.state) {
414
- if (
415
- (!isEmpty(allTagsFor(finishedMatch.range)) || finishedMatch.allowEmpty) &&
416
- finishedMatch.state.status !== 'rejected'
417
- ) {
418
- finishedState.accept();
419
- finishedMatch.setRangeFinalIndex(btree.getSum(m.fragmentNode.children) - 1);
420
- } else {
421
- finishedState.reject();
422
- finishedMatch.setRangePreviousIndex(null);
423
- }
424
- } else {
425
- finishedMatch.setRangeFinalIndex(btree.getSum((m.fragmentNode || m.node).children) - 1);
426
- }
448
+ finishedMatch.setRangePreviousIndex(null);
427
449
 
428
450
  return m;
429
451
  }
@@ -434,9 +456,13 @@ export class Match extends WeakStackFrame {
434
456
  if (!state.depth) {
435
457
  let { node, emitted } = state;
436
458
 
459
+ if (!node) {
460
+ node = state.resultPath.node;
461
+ }
462
+
437
463
  let { path } = internalStates.get(node);
438
464
 
439
- let tagPath = emitted || (btree.getSum(path.node.children) ? new TagPath(path, 0) : null);
465
+ let tagPath = emitted || (sumtree.getSize(path.node.children) ? new TagPath(path, 0) : null);
440
466
 
441
467
  // two basic cases:
442
468
  // emitted can move
@@ -446,7 +472,7 @@ export class Match extends WeakStackFrame {
446
472
  if (
447
473
  tagPath.tag.type === OpenNodeTag &&
448
474
  tagPath.tag.value.type &&
449
- nodeStates.get(tagPath.path.node).unboundAttributes?.size
475
+ nodeStates.get(tagPath.path.node)?.undefinedAttributes?.size
450
476
  ) {
451
477
  break;
452
478
  }