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