@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/match.js CHANGED
@@ -1,24 +1,36 @@
1
1
  import { resolveLanguage } from '@bablr/helpers/grammar';
2
2
  import { WeakStackFrame } from '@bablr/weak-stack';
3
3
 
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';
4
+ import { agast, TagPathFacade as TagPath, PathFacade as Path } from '@bablr/agast-vm';
5
+ import {
6
+ buildOpenNodeTag,
7
+ buildProperty,
8
+ buildReference,
9
+ fragmentFlags,
10
+ mergeReferences,
11
+ } from '@bablr/agast-helpers/tree';
7
12
  import { effectsFor } from '@bablr/agast-vm-helpers';
8
- import { buildInternalState, internalStates, FragmentFacade } from './node.js';
13
+ import { FragmentFacade } from './node.js';
9
14
 
10
15
  import { facades, actuals } from './facades.js';
11
- import { CloseNodeTag, OpenNodeTag } from '@bablr/agast-helpers/symbols';
12
- import { nodeStates } from './state.js';
13
- import { buildCall, buildEmbeddedTag } from '@bablr/agast-vm-helpers/builders';
14
-
15
- const tagPathsAreEqual = (a, b) => {
16
- return !a || !b ? a == b : a.path.node === b.path.node && a.childrenIndex === b.childrenIndex;
17
- };
16
+ import {
17
+ CloseNodeTag,
18
+ OpenNodeTag,
19
+ ReferenceTag,
20
+ ShiftTag,
21
+ AttributeDefinition,
22
+ Property,
23
+ PropertyWrapper,
24
+ InitializerTag,
25
+ BindingTag,
26
+ LiteralTag,
27
+ } from '@bablr/agast-helpers/symbols';
28
+ import { offsetForTag } from '@bablr/agast-helpers/path';
18
29
 
19
30
  export class MatchFacade {
20
31
  constructor(match) {
21
32
  facades.set(match, this);
33
+ Object.freeze(this);
22
34
  }
23
35
 
24
36
  get language() {
@@ -49,36 +61,41 @@ export class MatchFacade {
49
61
  return actuals.get(this).path;
50
62
  }
51
63
 
52
- get innerPath() {
53
- return actuals.get(this).innerPath;
54
- }
55
-
56
64
  get fragment() {
57
65
  const {
58
66
  ctx,
59
- path,
67
+ effects,
68
+ isCoverBoundary,
60
69
  isNode,
61
70
  fragmentNode,
71
+ node,
62
72
  mergedReference,
63
73
  rangePreviousIndex,
64
74
  rangeFinalIndex,
65
75
  } = actuals.get(this);
66
76
 
67
- const { name, isArray } = mergedReference.value;
77
+ const { name, isArray } = mergedReference;
68
78
 
69
- if (isNode && !['#', '@'].includes(name)) {
79
+ let prev =
80
+ rangePreviousIndex != null
81
+ ? (fragmentNode || node).tags.at(rangePreviousIndex)
82
+ : rangePreviousIndex;
83
+
84
+ let offset = [ReferenceTag, ShiftTag].includes(prev?.type) ? 0 : 1;
85
+
86
+ if ((isNode || isCoverBoundary) && name) {
70
87
  return new FragmentFacade(
71
- fragmentNode,
88
+ effects.success !== 'none' ? fragmentNode : null,
72
89
  ctx,
73
90
  false,
74
91
  true,
75
- [rangePreviousIndex + 1, rangeFinalIndex],
92
+ [rangePreviousIndex + offset, rangeFinalIndex],
76
93
  mergedReference,
77
- isArray ? getChildPropertyIndex(fragmentNode, rangePreviousIndex + 1) : null,
94
+ isArray ? fragmentNode.getChildPropertyIndex(rangePreviousIndex + offset) : null,
78
95
  );
79
96
  } else {
80
- return new FragmentFacade(fragmentNode || path.node, ctx, false, true, [
81
- rangePreviousIndex + 1,
97
+ return new FragmentFacade(fragmentNode || node, ctx, false, true, [
98
+ rangePreviousIndex + offset,
82
99
  rangeFinalIndex,
83
100
  ]);
84
101
  }
@@ -129,12 +146,16 @@ export class MatchFacade {
129
146
  return actuals.get(this).isCoverBoundary;
130
147
  }
131
148
 
149
+ get coveredBoundary() {
150
+ return facades.get(actuals.get(this).coveredBoundary);
151
+ }
152
+
132
153
  get cover() {
133
154
  return facades.get(actuals.get(this).cover);
134
155
  }
135
156
 
136
157
  get shiftMatch() {
137
- return facades.get(actuals.get(this).shiftMatch);
158
+ return facades.get(actuals.get(this).coveredBoundary.shiftMatch);
138
159
  }
139
160
 
140
161
  get captured() {
@@ -142,7 +163,8 @@ export class MatchFacade {
142
163
  }
143
164
 
144
165
  get range() {
145
- return actuals.get(this).range;
166
+ let { range } = actuals.get(this);
167
+ return range && [range[0], range[1]];
146
168
  }
147
169
 
148
170
  get effects() {
@@ -185,6 +207,10 @@ export class MatchFacade {
185
207
  return actuals.get(this).rangeFinalIndex;
186
208
  }
187
209
 
210
+ get mergedLanguagePath() {
211
+ return actuals.get(this).mergedLanguagePath;
212
+ }
213
+
188
214
  ancestors(...args) {
189
215
  return actuals.get(this).ancestors(...args);
190
216
  }
@@ -207,39 +233,43 @@ export class Match extends WeakStackFrame {
207
233
 
208
234
  this.rangePreviousIndex = null;
209
235
  this.rangeFinalIndex = null;
236
+ this.agast = null;
237
+ this.agastFragment = null; // why do it this way?
210
238
  this.node = null;
211
- this.fragmentNode = null; // why do it this way?
212
239
  this.cover = null;
240
+ this.running = null;
213
241
  this.options = options;
214
242
 
215
- let internalState;
216
- let { isNode } = this;
217
- const { grammar, type } = this;
218
- let isCover = grammar.covers?.has(type);
219
-
220
- this.cover = isNode ? null : parent?.cover || (isCover ? this : null);
221
-
222
- let isCoverBoundary = ((isNode || isCover) && !this.cover) || !parent;
223
-
224
- if (isCoverBoundary) {
225
- internalState = buildInternalState();
226
- internalState.path.node;
243
+ let isNode = !matcher.nodeMatcher.flags.fragment;
244
+ let isCover = matcher.nodeMatcher.flags.cover;
245
+
246
+ this.agast = isNode ? null : state.agast ?? null;
247
+ this.node = isNode ? null : parent?.node;
248
+ this.cover =
249
+ effects.success === 'none'
250
+ ? null
251
+ : !parent?.isNode && parent?.cover
252
+ ? parent.cover
253
+ : isCover && parent
254
+ ? this
255
+ : null;
256
+
257
+ if (isNode || !parent?.node) {
258
+ let held = null;
259
+ // TODO wat
260
+ if (this.coveredBoundary.shiftMatch) {
261
+ let { reference, binding, node } = state.held;
262
+ held = buildProperty(reference, binding, node.node);
263
+ }
264
+ this.agast = agast({ held });
227
265
  } else {
228
- const { instructionsPump, expressionsPump, agastState, agast, path } = internalStates.get(
229
- parent.node,
230
- );
231
- internalState = { instructionsPump, expressionsPump, agastState, agast, path };
232
- }
233
-
234
- this.node = internalState.path.node;
235
-
236
- if (this.isNode) {
237
- this.node.type = Symbol.for(this.type);
238
- this.node.language = this.language.canonicalURL;
266
+ this.agast = parent.agast;
267
+ if (effects.success === 'none') {
268
+ this.agast = agast();
269
+ this.agast.vm.next(buildOpenNodeTag(fragmentFlags));
270
+ }
239
271
  }
240
272
 
241
- internalStates.set(this.node, internalState);
242
-
243
273
  new MatchFacade(this);
244
274
  }
245
275
 
@@ -248,28 +278,36 @@ export class Match extends WeakStackFrame {
248
278
  }
249
279
 
250
280
  get isCoverBoundary() {
251
- return (this.isNode && !this.parent.cover) || this.cover === this;
281
+ let { cover } = this;
282
+ return cover === this;
252
283
  }
253
284
 
254
285
  get matcher() {
255
286
  return this.propertyMatcher?.nodeMatcher;
256
287
  }
257
288
 
289
+ get vm() {
290
+ return this.agast.vm;
291
+ }
292
+
258
293
  get mergedReference() {
259
- let ref = buildReferenceTag('.');
294
+ let ref = buildReference('.');
260
295
 
261
296
  let first = true;
262
297
  let m = this;
263
298
  let lastName = null;
299
+ let lastRefType = null;
264
300
  do {
265
301
  if (m.isNode && !first) break;
266
302
  if (m.propertyMatcher.refMatcher) {
267
- const { name, isArray, flags } = m.propertyMatcher.refMatcher;
268
- const parentRef = buildReferenceTag(name, isArray, flags);
269
- if (lastName && lastName !== name) break;
270
- ref = ['#', '@'].includes(ref.value.name) ? ref : mergeReferences(ref, parentRef);
271
- if (name !== '.') {
303
+ const parentRef = m.propertyMatcher.refMatcher;
304
+ const { type: refType, name } = parentRef;
305
+
306
+ if (lastName && (lastName !== name || lastRefType !== refType)) break;
307
+ ref = ['#', '@'].includes(ref.type) ? ref : mergeReferences(ref, parentRef);
308
+ if (refType !== '.') {
272
309
  lastName = name;
310
+ lastRefType = refType;
273
311
  }
274
312
  }
275
313
  first = false;
@@ -278,13 +316,30 @@ export class Match extends WeakStackFrame {
278
316
  return ref;
279
317
  }
280
318
 
319
+ get mergedLanguagePath() {
320
+ let languagePath = [];
321
+
322
+ let first = true;
323
+ let m = this;
324
+ do {
325
+ if (m.isNode && !first) break;
326
+ if (m.propertyMatcher.bindingMatcher) {
327
+ languagePath = m.propertyMatcher.bindingMatcher.languagePath.concat(languagePath);
328
+ }
329
+ first = false;
330
+ } while ((m = m.shiftMatch || m.parent));
331
+
332
+ return Object.freeze(languagePath);
333
+ }
334
+
281
335
  get pathName() {
282
- return this.mergedReference.value.name;
336
+ return this.mergedReference.name;
283
337
  }
284
338
 
285
339
  get path() {
286
- let { agastState } = internalStates.get(this.fragmentNode || this.node);
287
- return agastState.path || agastState.resultPath.path;
340
+ let { agast } = this;
341
+
342
+ return agast.state.path || agast.state.resultPath?.path;
288
343
  }
289
344
 
290
345
  get pathParent() {
@@ -297,7 +352,7 @@ export class Match extends WeakStackFrame {
297
352
  }
298
353
 
299
354
  get coveredBoundary() {
300
- return this.isNode ? this.parent.cover || this : this.cover || this;
355
+ return this.cover || this;
301
356
  }
302
357
 
303
358
  get parentPath() {
@@ -332,33 +387,63 @@ export class Match extends WeakStackFrame {
332
387
  return !!this.grammar.emptyables?.has(this.type) || this.options.allowEmpty;
333
388
  }
334
389
 
390
+ get fragmentNode() {
391
+ if (!this.agastFragment) return null;
392
+
393
+ let { node, resultPath } = this.agastFragment.state;
394
+ return resultPath?.tag.type === CloseNodeTag ? resultPath.node : node;
395
+ }
396
+
397
+ get fragmentPath() {
398
+ if (!this.agastFragment) return null;
399
+
400
+ let { path, resultPath } = this.agastFragment.state;
401
+ return resultPath?.tag.type === CloseNodeTag ? resultPath.path : path;
402
+ }
403
+
335
404
  get rangePrevious() {
336
- return this.rangePreviousIndex == null
405
+ let path = this.fragmentPath || this.path;
406
+
407
+ return this.rangePreviousIndex == null || path == null
337
408
  ? null
338
- : TagPath.from(this.path, this.rangePreviousIndex);
409
+ : TagPath.from(path, this.rangePreviousIndex, -1);
339
410
  }
340
411
 
341
412
  setRangePreviousIndex(value) {
413
+ if ((value != null && !Number.isFinite(value)) || value < 0) throw new Error();
342
414
  this.rangePreviousIndex = value;
343
- this.rangePrevious;
415
+ if (
416
+ value != null &&
417
+ (!this.rangePrevious ||
418
+ (this.isNode &&
419
+ ![OpenNodeTag, Property, InitializerTag, LiteralTag, AttributeDefinition].includes(
420
+ this.rangePrevious.tag.type,
421
+ )))
422
+ )
423
+ throw new Error();
344
424
  }
345
425
 
346
426
  setRangeFinalIndex(value) {
427
+ if (value != null && !Number.isFinite(value)) throw new Error();
347
428
  this.rangeFinalIndex = value;
348
429
  this.rangeFinal;
349
430
  }
350
431
 
351
432
  get rangeFinal() {
352
- const path = Path.from(this.fragmentNode || this.node);
433
+ let path = this.fragmentPath || this.path;
353
434
 
354
- return this.rangeFinalIndex == null ? null : new TagPath(path, this.rangeFinalIndex);
435
+ return this.rangeFinalIndex == null || path == null
436
+ ? null
437
+ : TagPath.from(path, this.rangeFinalIndex, -1);
355
438
  }
356
439
 
357
440
  get rangeInitial() {
358
- const { rangePrevious } = this;
441
+ const { rangePrevious, isNode, fragmentPath, path, rangePreviousIndex } = this;
359
442
 
360
443
  if (!rangePrevious) return rangePrevious;
361
444
 
445
+ if (isNode) return TagPath.from(fragmentPath || path, rangePreviousIndex + 1, 0);
446
+
362
447
  return rangePrevious?.nextSibling;
363
448
  }
364
449
 
@@ -371,59 +456,76 @@ export class Match extends WeakStackFrame {
371
456
  return !!this.shiftMatch;
372
457
  }
373
458
 
374
- get unshiftedReferencePath() {
375
- const previous = this.rangePrevious;
376
-
377
- if (!this.isNode) {
459
+ get referencePath() {
460
+ if (!(this.isNode || this.isCoverBoundary || this.cover) || this.state.depths.path < 0) {
378
461
  return null;
379
462
  }
380
463
 
381
- if (this.parent.cover) {
382
- return previous;
383
- }
464
+ let parentPath = this.fragmentPath || this.path;
384
465
 
385
- if (this.didShift) {
386
- let refIndex = previous.childrenIndex - previous.tag.value.index * 2;
387
- return previous.siblingPathAt(refIndex);
466
+ let ref = TagPath.from(parentPath, this.rangePreviousIndex + 1, 0);
467
+
468
+ if (!ref) return null;
469
+
470
+ if (ref.tag.type === ShiftTag) {
471
+ let refIndex = ref.tagsIndex - ref.tag.value.index;
472
+ ref = TagPath.from(ref.path, refIndex, 0);
388
473
  }
389
474
 
390
- return previous;
475
+ if (ref && ref.tag.type !== ReferenceTag) throw new Error();
476
+ return ref;
391
477
  }
392
478
 
393
479
  get isNode() {
394
- let { flags, type } = this.matcher;
395
- return !flags.fragment && type !== Symbol.for('@bablr/fragment');
480
+ let { flags } = this.matcher;
481
+ return !this.parent || !flags.fragment;
396
482
  }
397
483
 
398
484
  get isCover() {
399
485
  return this.cover === this;
400
486
  }
401
487
 
402
- get innerPath() {
403
- return internalStates.get(this.node).path;
404
- }
488
+ advance(tag, s = this.state) {
489
+ let { vm, state: agastState } = s.agast;
405
490
 
406
- add(node) {
407
- let internalState = internalStates.get(this.fragmentNode);
408
- const { expressionsPump, instructionsPump, agast, agastState } = internalState;
491
+ let result = vm.next(tag);
409
492
 
410
- expressionsPump.queue(node);
411
- instructionsPump.queue(buildCall('advance', buildEmbeddedTag(buildGapTag())));
493
+ if (tag.type === OpenNodeTag) {
494
+ s.depths.result++;
495
+ this.node = s.node || s.resultPath.node;
496
+ } else if (tag.type === CloseNodeTag) {
497
+ s.depths.result--;
498
+ }
412
499
 
413
- agast.next();
500
+ s.resultPath =
501
+ (agastState.path || agastState.resultPath?.path) &&
502
+ TagPath.from(
503
+ agastState.path || agastState.resultPath?.path,
504
+ -1,
505
+ tag.type === PropertyWrapper ? -1 : offsetForTag(tag),
506
+ );
507
+ return result.value;
414
508
  }
415
509
 
416
- startFrame(state, propertyMatcher, effects, didShift, options) {
510
+ startFrame(state, propertyMatcher, effects, shiftMatch, options) {
417
511
  let { context } = this;
418
- const { nodeMatcher } = propertyMatcher;
512
+ const { bindingMatcher } = propertyMatcher;
513
+
514
+ let language = shiftMatch?.language ?? this.language;
419
515
 
420
- const language = resolveLanguage(context, this.language, nodeMatcher.language);
516
+ if (bindingMatcher) {
517
+ language = resolveLanguage(context, language, bindingMatcher.languagePath);
421
518
 
422
- if (!language) {
423
- throw new Error(`Unknown language ${nodeMatcher.language.join('.')}`);
519
+ if (!language) {
520
+ throw new Error(`Unknown language ${bindingMatcher.languagePath.join('.')}`);
521
+ }
424
522
  }
425
523
 
426
- return this.push(context, language, state, propertyMatcher, effects, didShift, options);
524
+ let m = this.push(context, language, state, propertyMatcher, effects, shiftMatch, options);
525
+
526
+ this.running = m;
527
+
528
+ return m;
427
529
  }
428
530
 
429
531
  endFrame() {
@@ -432,9 +534,9 @@ export class Match extends WeakStackFrame {
432
534
 
433
535
  if (!m) return m;
434
536
 
435
- finishedMatch.setRangeFinalIndex(
436
- sumtree.getSize((finishedMatch.fragmentNode || m.node).children) - 1,
437
- );
537
+ finishedMatch.setRangeFinalIndex((finishedMatch.fragmentNode || m.node).tags.size - 1);
538
+
539
+ m.running = null;
438
540
 
439
541
  return m;
440
542
  }
@@ -445,40 +547,54 @@ export class Match extends WeakStackFrame {
445
547
 
446
548
  if (!m) return m;
447
549
 
550
+ m.running = null;
551
+
448
552
  finishedMatch.setRangePreviousIndex(null);
449
553
 
450
554
  return m;
451
555
  }
452
556
 
453
- *emit() {
557
+ *emit(options) {
454
558
  let { state } = this;
559
+ let { emitted } = state;
455
560
 
456
- if (!state.depth) {
457
- let { node, emitted } = state;
561
+ let m = emitted?.match ?? this;
458
562
 
459
- if (!node) {
460
- node = state.resultPath.node;
461
- }
563
+ if (!state.depth) {
564
+ let { node } = m;
462
565
 
463
- let { path } = internalStates.get(node);
566
+ // if (!node) {
567
+ // node = resultPath.node;
568
+ // }
464
569
 
465
- let tagPath = emitted || (sumtree.getSize(path.node.children) ? new TagPath(path, 0) : null);
570
+ let path = Path.from(node);
466
571
 
467
- // two basic cases:
468
- // emitted can move
469
- // emitted cannot move
572
+ let tagPath = emitted?.tagPath || (path.node.tags.size ? TagPath.from(path, 0) : null);
470
573
 
471
574
  while (tagPath) {
472
575
  if (
576
+ options.holdUndefinedAttributes &&
473
577
  tagPath.tag.type === OpenNodeTag &&
474
578
  tagPath.tag.value.type &&
475
- nodeStates.get(tagPath.path.node)?.undefinedAttributes?.size
579
+ (m.node.undefinedAttributes ?? 0) > 0
476
580
  ) {
477
581
  break;
478
582
  }
479
583
 
480
- if (!tagPathsAreEqual(tagPath, state.emitted)) {
481
- state.emitted = emitted = tagPath;
584
+ if (
585
+ tagPath.tag.type === OpenNodeTag &&
586
+ m.referencePath?.tag.value.type === '@' &&
587
+ tagPath.node.flags.hasGap
588
+ )
589
+ break;
590
+
591
+ let holdShifted =
592
+ options.holdShiftedNodes &&
593
+ tagPath.tag.type === ReferenceTag &&
594
+ tagPath.tag.value.flags.expression;
595
+
596
+ if (!state.emitted || !tagPath.equalTo(state.emitted.tagPath)) {
597
+ state.emitted = emitted = { match: m, tagPath };
482
598
 
483
599
  if (tagPath.tag.type === OpenNodeTag) {
484
600
  state.depths.emitted++;
@@ -486,10 +602,57 @@ export class Match extends WeakStackFrame {
486
602
  state.depths.emitted--;
487
603
  }
488
604
 
489
- yield tagPath.tag;
605
+ if (
606
+ !(
607
+ (tagPath.tag.type === BindingTag && !tagPath.tag.value.languagePath?.length) ||
608
+ (tagPath.tag.type === AttributeDefinition && options.holdUndefinedAttributes)
609
+ )
610
+ ) {
611
+ yield tagPath.tag;
612
+ }
613
+ }
614
+
615
+ if (
616
+ (tagPath.tag.type === CloseNodeTag ||
617
+ (tagPath.tag.type === OpenNodeTag && tagPath.tag.value.selfClosing)) &&
618
+ !tagPath.next
619
+ ) {
620
+ tagPath = TagPath.from(m.fragmentPath, m.rangeInitial.tagsIndex + 1, 0);
621
+
622
+ do {
623
+ m = m.parent;
624
+ } while (m && !m.isNode);
625
+
626
+ if (!m) break;
627
+
628
+ continue;
490
629
  }
491
630
 
492
- tagPath = emitted.next;
631
+ if (!holdShifted && tagPath.tag.type === ReferenceTag && !tagPath.nextSibling) {
632
+ let { running } = m;
633
+
634
+ while (running && !running.isNode) {
635
+ if (running.state.depth > 0) {
636
+ running = null;
637
+ break;
638
+ } else {
639
+ running = running.running;
640
+ }
641
+ }
642
+
643
+ if (running) {
644
+ m = running;
645
+ } else {
646
+ break;
647
+ }
648
+ tagPath = TagPath.from(m.path, 0);
649
+ } else {
650
+ if (holdShifted) {
651
+ tagPath = tagPath.nextUnshifted;
652
+ } else {
653
+ tagPath = tagPath.next;
654
+ }
655
+ }
493
656
  }
494
657
  }
495
658
  }