@bablr/bablr-vm 0.22.1 → 0.23.1

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,31 +1,45 @@
1
- import { resolveLanguage } from '@bablr/helpers/grammar';
1
+ import { getProduction } from '@bablr/helpers/grammar';
2
2
  import { WeakStackFrame } from '@bablr/weak-stack';
3
3
 
4
- import { agast, TagPathFacade as TagPath, PathFacade as Path } from '@bablr/agast-vm';
4
+ import { agast } from '@bablr/agast-vm';
5
+ import * as Tags from '@bablr/agast-helpers/tags';
6
+ import * as BTree from '@bablr/agast-helpers/btree';
5
7
  import {
6
- buildOpenNodeTag,
7
- buildProperty,
8
8
  buildReference,
9
- fragmentFlags,
9
+ buildReferenceTag,
10
+ getFlagsWithGap,
10
11
  mergeReferences,
12
+ printBinding,
13
+ printType,
14
+ buildPropertyTag,
15
+ buildOpenFragmentTag,
16
+ nodeFlags,
17
+ streamFromTree,
11
18
  } from '@bablr/agast-helpers/tree';
12
- import { effectsFor } from '@bablr/agast-vm-helpers';
13
- import { FragmentFacade } from './node.js';
19
+ import { effectsFor, reifyExpression, shouldBranch } from '@bablr/agast-vm-helpers';
14
20
 
15
21
  import { facades, actuals } from './facades.js';
16
22
  import {
17
23
  CloseNodeTag,
18
24
  OpenNodeTag,
19
- ReferenceTag,
20
- ShiftTag,
21
25
  AttributeDefinition,
22
26
  Property,
23
- PropertyWrapper,
24
- InitializerTag,
27
+ ReferenceTag,
25
28
  BindingTag,
26
- LiteralTag,
29
+ ShiftTag,
30
+ TreeNode,
27
31
  } from '@bablr/agast-helpers/symbols';
28
- import { offsetForTag } from '@bablr/agast-helpers/path';
32
+ import {
33
+ buildNullNode,
34
+ endsNode,
35
+ getCloseTag,
36
+ getTags,
37
+ has,
38
+ isMultiFragment,
39
+ Path,
40
+ TagPath,
41
+ } from '@bablr/agast-helpers/path';
42
+ import { updateSpans } from './spans.js';
29
43
 
30
44
  export class MatchFacade {
31
45
  constructor(match) {
@@ -53,6 +67,10 @@ export class MatchFacade {
53
67
  return actuals.get(this).propertyMatcher;
54
68
  }
55
69
 
70
+ get rawPropertyMatcher() {
71
+ return actuals.get(this).rawPropertyMatcher;
72
+ }
73
+
56
74
  get depth() {
57
75
  return actuals.get(this).depth;
58
76
  }
@@ -62,43 +80,7 @@ export class MatchFacade {
62
80
  }
63
81
 
64
82
  get fragment() {
65
- const {
66
- ctx,
67
- effects,
68
- isCoverBoundary,
69
- isNode,
70
- fragmentNode,
71
- node,
72
- mergedReference,
73
- rangePreviousIndex,
74
- rangeFinalIndex,
75
- } = actuals.get(this);
76
-
77
- const { name, isArray } = mergedReference;
78
-
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) {
87
- return new FragmentFacade(
88
- effects.success !== 'none' ? fragmentNode : null,
89
- ctx,
90
- false,
91
- true,
92
- [rangePreviousIndex + offset, rangeFinalIndex],
93
- mergedReference,
94
- isArray ? fragmentNode.getChildPropertyIndex(rangePreviousIndex + offset) : null,
95
- );
96
- } else {
97
- return new FragmentFacade(fragmentNode || node, ctx, false, true, [
98
- rangePreviousIndex + offset,
99
- rangeFinalIndex,
100
- ]);
101
- }
83
+ return actuals.get(this).fragment;
102
84
  }
103
85
 
104
86
  get pathDepth() {
@@ -109,13 +91,8 @@ export class MatchFacade {
109
91
  return actuals.get(this).pathName;
110
92
  }
111
93
 
112
- get pathParent() {
113
- return actuals.get(this).pathParent;
114
- }
115
-
116
94
  get node() {
117
- const { node, ctx } = actuals.get(this);
118
- return FragmentFacade.wrap(node, ctx, false);
95
+ return actuals.get(this).node;
119
96
  }
120
97
 
121
98
  get props() {
@@ -150,6 +127,10 @@ export class MatchFacade {
150
127
  return facades.get(actuals.get(this).coveredBoundary);
151
128
  }
152
129
 
130
+ get nodeMatch() {
131
+ return facades.get(actuals.get(this).nodeMatch);
132
+ }
133
+
153
134
  get cover() {
154
135
  return facades.get(actuals.get(this).cover);
155
136
  }
@@ -158,15 +139,6 @@ export class MatchFacade {
158
139
  return facades.get(actuals.get(this).coveredBoundary.shiftMatch);
159
140
  }
160
141
 
161
- get captured() {
162
- return actuals.get(this).captured;
163
- }
164
-
165
- get range() {
166
- let { range } = actuals.get(this);
167
- return range && [range[0], range[1]];
168
- }
169
-
170
142
  get effects() {
171
143
  return actuals.get(this).effects;
172
144
  }
@@ -180,118 +152,186 @@ export class MatchFacade {
180
152
  }
181
153
 
182
154
  get state() {
183
- return facades.get(actuals.get(this).state);
155
+ return actuals.get(this).s.getPublic();
184
156
  }
185
157
 
186
158
  get s() {
187
- return facades.get(actuals.get(this).s);
159
+ return actuals.get(this).s.getPublic();
188
160
  }
189
161
 
190
- get rangePrevious() {
191
- return actuals.get(this).rangePrevious;
162
+ ancestors(...args) {
163
+ return actuals.get(this).ancestors(...args);
192
164
  }
165
+ }
193
166
 
194
- get rangePreviousIndex() {
195
- return actuals.get(this).rangePreviousIndex;
196
- }
167
+ const normalizeBindingPath = (path) => {
168
+ if (path.length <= 1) return path;
169
+ };
197
170
 
198
- get rangeInitial() {
199
- return actuals.get(this).rangeInitial;
200
- }
171
+ export class Match extends WeakStackFrame {
172
+ static startFrame(ctx, s, m, finishedMatch, verb, matcher, options) {
173
+ let effects = effectsFor(verb.description);
174
+ let isShift = verb.description.startsWith('shift'); // should this be didShift?
201
175
 
202
- get rangeFinal() {
203
- return actuals.get(this).rangeFinal;
204
- }
176
+ let parentMatch = m;
205
177
 
206
- get rangeFinalIndex() {
207
- return actuals.get(this).rangeFinalIndex;
208
- }
178
+ if (!s) throw new Error('not initialized');
209
179
 
210
- get mergedLanguagePath() {
211
- return actuals.get(this).mergedLanguagePath;
212
- }
180
+ let matcher_ = reifyExpression(matcher);
213
181
 
214
- ancestors(...args) {
215
- return actuals.get(this).ancestors(...args);
182
+ if (parentMatch && parentMatch.cover && !parentMatch.isNode) {
183
+ if (matcher_.refMatcher) {
184
+ let m = matcher_.refMatcher;
185
+ if (!['_', '#'].includes(m.type) || m.flags.expression || m.flags.hasGap || m.flags.array) {
186
+ throw new Error('no references inside covers');
187
+ }
188
+ }
189
+ }
190
+
191
+ if (isShift && matcher_.nodeMatcher.type === '__') {
192
+ throw new Error();
193
+ }
194
+ if (isShift && !parentMatch) throw new Error();
195
+
196
+ if (shouldBranch(effects)) {
197
+ s = s.branch();
198
+ }
199
+
200
+ s.languages = isShift
201
+ ? finishedMatch.languages
202
+ : parentMatch
203
+ ? parentMatch.languages
204
+ : s.languages;
205
+
206
+ m = parentMatch
207
+ ? parentMatch.startFrame(s, matcher, effects, isShift ? finishedMatch : null, options)
208
+ : Match.from(ctx, s, matcher, null, options);
209
+
210
+ updateSpans(m, 'open');
211
+
212
+ if (m.name && !getProduction(m.grammar, m.name))
213
+ throw new Error(`Production {type: ${printType(m.name)}} does not exist`);
214
+
215
+ if (m.flags.token && !m.isNode) {
216
+ throw new Error('tokens must be nodes');
217
+ }
218
+
219
+ finishedMatch = null;
220
+
221
+ if (!m.parent && m.type === '__') {
222
+ s.node = m.rootNode;
223
+ }
224
+ return m;
216
225
  }
217
- }
218
226
 
219
- export class Match extends WeakStackFrame {
220
- constructor(parent, context, language, state, matcher, effects, shiftMatch = null, options = {}) {
221
- if (!context || !language || !state) {
227
+ constructor(parent, context, state, rawMatcher, effects, shiftMatch = null, options = {}) {
228
+ if (!context || !state) {
222
229
  throw new Error('Invalid arguments to Match constructor');
223
230
  }
231
+ let matcher = reifyExpression(rawMatcher);
224
232
 
225
233
  super(parent);
226
234
 
227
235
  this.context = context;
228
- this.language = language;
229
236
  this.state = state;
237
+ this.rawPropertyMatcher = rawMatcher;
230
238
  this.propertyMatcher = matcher;
231
239
  this.effects = effects;
232
240
  this.shiftMatch = shiftMatch;
241
+ this.emittedMatch = parent?.emittedMatch || this;
233
242
 
234
- this.rangePreviousIndex = null;
235
- this.rangeFinalIndex = null;
236
243
  this.agast = null;
237
- this.agastFragment = null; // why do it this way?
238
- this.node = null;
239
244
  this.cover = null;
240
245
  this.running = null;
241
246
  this.options = options;
242
247
 
243
- let isNode = !matcher.nodeMatcher.flags.fragment;
244
- let isCover = matcher.nodeMatcher.flags.cover;
248
+ let isNode = !matcher.nodeMatcher.type;
249
+ let isCover = matcher.nodeMatcher.type === '_';
245
250
 
246
- this.agast = isNode ? null : state.agast ?? null;
247
- this.node = isNode ? null : parent?.node;
251
+ this.languages = state.languages;
252
+ this.nodeMatch = isNode || !parent ? this : parent.nodeMatch;
253
+ this.expressionMatch = shiftMatch
254
+ ? shiftMatch.expressionMatch
255
+ : parent?.expressionMatch || (matcher.refMatcher?.flags.expression ? this : null);
256
+ this.languageMatch =
257
+ normalizeBindingPath(matcher.bindingMatchers).length || !parent ? this : parent.languageMatch;
258
+ this.agast = null;
248
259
  this.cover =
249
- effects.success === 'none'
250
- ? null
251
- : !parent?.isNode && parent?.cover
260
+ !parent?.isNode && parent?.cover && matcher.refMatcher?.type !== '#'
252
261
  ? parent.cover
253
262
  : isCover && parent
254
263
  ? this
255
264
  : null;
265
+ this.rootNode = null;
266
+ this.nodeDepth = (parent?.nodeDepth ?? 0) + (isNode ? 1 : 0);
267
+ this.parentPreviousTagPath = this.parent && TagPath.from(this.parent.path, -1, -1);
268
+ this.emitted = this.parent?.emitted || null;
256
269
 
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 });
265
- } else {
266
- this.agast = parent.agast;
267
- if (effects.success === 'none') {
268
- this.agast = agast();
269
- this.agast.vm.next(buildOpenNodeTag(fragmentFlags));
270
+ if (!this.language) {
271
+ throw new Error(`Unknown language ${printBinding(matcher.bindingMatcher)}`);
272
+ }
273
+
274
+ let held = null;
275
+ if (this.coveredBoundary.shiftMatch) {
276
+ held = state.held;
277
+ }
278
+ this.agast = agast({ held });
279
+
280
+ let parentHasGap = !this.parent
281
+ ? this.matcher.flags.hasGap
282
+ : this.parent.nodeMatch.node.value.flags.hasGap;
283
+
284
+ this.agast.vm.next(buildOpenFragmentTag(parentHasGap ? getFlagsWithGap(nodeFlags) : nodeFlags));
285
+ if (!this.parent) {
286
+ if (isNode || isCover) {
287
+ this.agast.vm.next(buildReferenceTag());
270
288
  }
289
+ } else if (shiftMatch) {
290
+ this.agast.vm.next(shiftMatch.rootNode);
271
291
  }
292
+ this.rootNode = this.agast.getState().node;
272
293
 
273
294
  new MatchFacade(this);
274
295
  }
275
296
 
276
- static from(context, language, state, matcher, props, options) {
277
- return Match.create(context, language, state, matcher, effectsFor('eat'), props, options);
297
+ static from(context, state, matcher, props, options) {
298
+ return Match.create(context, state, matcher, effectsFor('eat'), props, options);
299
+ }
300
+
301
+ get language() {
302
+ return BTree.getAt(-1, this.languages);
303
+ }
304
+
305
+ get emitting() {
306
+ return this.emittedMatch === this;
278
307
  }
279
308
 
280
309
  get isCoverBoundary() {
281
- let { cover } = this;
282
- return cover === this;
310
+ let { cover, isNode } = this;
311
+ return cover === this || (!cover && isNode);
283
312
  }
284
313
 
285
314
  get matcher() {
286
315
  return this.propertyMatcher?.nodeMatcher;
287
316
  }
288
317
 
318
+ get node() {
319
+ let { rootNode } = this;
320
+
321
+ if (this.matcher.type === '__') {
322
+ return rootNode;
323
+ }
324
+ if (Tags.getSize(getTags(rootNode)) <= 1) return null;
325
+
326
+ return Tags.getAt(-1, rootNode.value.children).value.node;
327
+ }
328
+
289
329
  get vm() {
290
330
  return this.agast.vm;
291
331
  }
292
332
 
293
333
  get mergedReference() {
294
- let ref = buildReference('.');
334
+ let ref = buildReference();
295
335
 
296
336
  let first = true;
297
337
  let m = this;
@@ -305,7 +345,7 @@ export class Match extends WeakStackFrame {
305
345
 
306
346
  if (lastName && (lastName !== name || lastRefType !== refType)) break;
307
347
  ref = ['#', '@'].includes(ref.type) ? ref : mergeReferences(ref, parentRef);
308
- if (refType !== '.') {
348
+ if (refType !== '_') {
309
349
  lastName = name;
310
350
  lastRefType = refType;
311
351
  }
@@ -316,22 +356,6 @@ export class Match extends WeakStackFrame {
316
356
  return ref;
317
357
  }
318
358
 
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
-
335
359
  get pathName() {
336
360
  return this.mergedReference.name;
337
361
  }
@@ -339,26 +363,13 @@ export class Match extends WeakStackFrame {
339
363
  get path() {
340
364
  let { agast } = this;
341
365
 
342
- return agast.state.path || agast.state.resultPath?.path;
343
- }
344
-
345
- get pathParent() {
346
- let m = this;
347
-
348
- do {
349
- m = m.parent;
350
- } while (m && !m.isNode);
351
- return m;
366
+ return agast.getState().path || agast.getState().resultPath?.path;
352
367
  }
353
368
 
354
369
  get coveredBoundary() {
355
370
  return this.cover || this;
356
371
  }
357
372
 
358
- get parentPath() {
359
- return this.pathParent?.path;
360
- }
361
-
362
373
  get ctx() {
363
374
  return this.context;
364
375
  }
@@ -375,153 +386,90 @@ export class Match extends WeakStackFrame {
375
386
  return this.matcher?.type || null;
376
387
  }
377
388
 
378
- get flags() {
379
- return this.matcher?.flags;
389
+ get name() {
390
+ return this.matcher?.name || null;
380
391
  }
381
392
 
382
- get captured() {
383
- return !this.rangePrevious || !!this.rangeFinal;
393
+ get flags() {
394
+ return this.matcher?.flags;
384
395
  }
385
396
 
386
397
  get allowEmpty() {
387
- return !!this.grammar.emptyables?.has(this.type) || this.options.allowEmpty;
388
- }
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;
398
+ return !!this.grammar.emptyables?.has(this.name) || this.options.allowEmpty;
395
399
  }
396
400
 
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
-
404
- get rangePrevious() {
405
- let path = this.fragmentPath || this.path;
406
-
407
- return this.rangePreviousIndex == null || path == null
408
- ? null
409
- : TagPath.from(path, this.rangePreviousIndex, -1);
410
- }
411
-
412
- setRangePreviousIndex(value) {
413
- if ((value != null && !Number.isFinite(value)) || value < 0) throw new Error();
414
- this.rangePreviousIndex = value;
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();
401
+ get didShift() {
402
+ return !!this.shiftMatch;
424
403
  }
425
404
 
426
- setRangeFinalIndex(value) {
427
- if (value != null && !Number.isFinite(value)) throw new Error();
428
- this.rangeFinalIndex = value;
429
- this.rangeFinal;
405
+ get isNode() {
406
+ return !this.matcher.type;
430
407
  }
431
408
 
432
- get rangeFinal() {
433
- let path = this.fragmentPath || this.path;
434
-
435
- return this.rangeFinalIndex == null || path == null
436
- ? null
437
- : TagPath.from(path, this.rangeFinalIndex, -1);
409
+ get isCover() {
410
+ return this.propertyMatcher.nodeMatcher.type === '_';
438
411
  }
439
412
 
440
- get rangeInitial() {
441
- const { rangePrevious, isNode, fragmentPath, path, rangePreviousIndex } = this;
442
-
443
- if (!rangePrevious) return rangePrevious;
444
-
445
- if (isNode) return TagPath.from(fragmentPath || path, rangePreviousIndex + 1, 0);
413
+ advance(tag) {
414
+ let { vm, getState } = this.agast;
415
+ let s = this.state;
446
416
 
447
- return rangePrevious?.nextSibling;
448
- }
417
+ let result = vm.next(tag);
449
418
 
450
- get range() {
451
- const { rangeInitial, rangeFinal } = this;
452
- return rangeInitial === null ? null : [rangeInitial, rangeFinal];
453
- }
419
+ if (!result || result.done) throw new Error();
454
420
 
455
- get didShift() {
456
- return !!this.shiftMatch;
457
- }
421
+ let agastState = getState();
458
422
 
459
- get referencePath() {
460
- if (!(this.isNode || this.isCoverBoundary || this.cover) || this.state.depths.path < 0) {
461
- return null;
423
+ if (tag.type === OpenNodeTag) {
424
+ s.depths.result++;
425
+ } else if (tag.type === CloseNodeTag) {
426
+ s.depths.result--;
462
427
  }
463
428
 
464
- let parentPath = this.fragmentPath || this.path;
465
-
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);
473
- }
429
+ this.rootNode = agastState.path.atDepth(0).node;
474
430
 
475
- if (ref && ref.tag.type !== ReferenceTag) throw new Error();
476
- return ref;
477
- }
431
+ s.resultPath = agastState.resultPath;
478
432
 
479
- get isNode() {
480
- let { flags } = this.matcher;
481
- return !this.parent || !flags.fragment;
482
- }
433
+ // if (this.emitted?.depth > 1) throw new Error();
483
434
 
484
- get isCover() {
485
- return this.cover === this;
486
- }
435
+ if (this.emittedMatch === this) {
436
+ let emittedPath = this.emitted || null;
487
437
 
488
- advance(tag, s = this.state) {
489
- let { vm, state: agastState } = s.agast;
438
+ let newEmitted = emittedPath && emittedPath.mapOnto(this.rootNode);
490
439
 
491
- let result = vm.next(tag);
440
+ if (emittedPath) {
441
+ if (!newEmitted) throw new Error();
442
+ // if (newEmitted.tag.depth !== this.emitted.tag.depth) throw new Error();
443
+ }
492
444
 
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--;
445
+ this.emitted = newEmitted;
498
446
  }
499
447
 
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
448
  return result.value;
508
449
  }
509
450
 
510
- startFrame(state, propertyMatcher, effects, shiftMatch, options) {
451
+ startFrame(state, rawMatcher, effects, shiftMatch, options) {
511
452
  let { context } = this;
512
- const { bindingMatcher } = propertyMatcher;
513
453
 
514
- let language = shiftMatch?.language ?? this.language;
454
+ let language = this.language;
455
+
456
+ // TODO stop wasting CPU cycles doing this twice or more!
457
+ let matcher = reifyExpression(rawMatcher);
458
+ let { bindingMatchers } = matcher;
515
459
 
516
- if (bindingMatcher) {
517
- language = resolveLanguage(context, language, bindingMatcher.languagePath);
460
+ for (let bindingMatcher of bindingMatchers) {
461
+ let { segments } = bindingMatcher || { segments: [] };
518
462
 
519
- if (!language) {
520
- throw new Error(`Unknown language ${bindingMatcher.languagePath.join('.')}`);
463
+ for (let segment of segments) {
464
+ language = segment.type
465
+ ? BTree.getAt(-2, state.languages)
466
+ : language.dependencies[segment.name];
521
467
  }
468
+
469
+ state.languages = BTree.push(state.languages, language);
522
470
  }
523
471
 
524
- let m = this.push(context, language, state, propertyMatcher, effects, shiftMatch, options);
472
+ let m = this.push(context, state, rawMatcher, effects, shiftMatch, options);
525
473
 
526
474
  this.running = m;
527
475
 
@@ -531,55 +479,129 @@ export class Match extends WeakStackFrame {
531
479
  endFrame() {
532
480
  const finishedMatch = this;
533
481
  const m = finishedMatch.parent;
482
+ let s = finishedMatch.state;
483
+
484
+ if (finishedMatch.shiftMatch) {
485
+ s.held = null;
486
+ }
487
+
488
+ let matchers = finishedMatch.propertyMatcher.bindingMatchers || [];
489
+
490
+ for (let matcher of matchers) {
491
+ let { segments } = matcher;
492
+ let normalizedSegments = normalizeBindingPath(segments);
493
+ if (normalizedSegments.length) {
494
+ finishedMatch.state.languages = BTree.pop(finishedMatch.state.languages);
495
+ }
496
+ }
534
497
 
535
498
  if (!m) return m;
536
499
 
537
- finishedMatch.setRangeFinalIndex((finishedMatch.fragmentNode || m.node).tags.size - 1);
500
+ updateSpans(finishedMatch, 'close');
538
501
 
539
- m.running = null;
502
+ if (shouldBranch(finishedMatch.effects)) {
503
+ if (finishedMatch.effects.success !== 'none') {
504
+ s = s.accept();
505
+ } else {
506
+ s = s.reject(finishedMatch);
507
+ }
508
+ }
509
+
510
+ m.emitted = finishedMatch.emitted;
511
+ m.emittedMatch = finishedMatch.emittedMatch;
512
+
513
+ if (finishedMatch.effects.success !== 'none') {
514
+ // for a shift, only copy the parts of the stack that aren't already present from the last successful shift frame
515
+
516
+ if (finishedMatch.shiftMatch) {
517
+ let property = Tags.getAt(-2, finishedMatch.rootNode.value.tags);
518
+ s.node = property.value.node;
519
+ m.advance(property.value.tags[0]);
520
+ for (let bindingTag of property.value.tags[1]) {
521
+ m.advance(bindingTag);
522
+ }
523
+ m.advance(property.value.node);
524
+ } else {
525
+ s.node = finishedMatch.rootNode;
526
+ m.advance(s.node);
527
+ }
528
+ }
540
529
 
541
530
  return m;
542
531
  }
543
532
 
544
533
  throw_() {
545
- const finishedMatch = this;
546
- const m = finishedMatch.parent;
534
+ let { bind } = this.options;
535
+ let finishedMatch = this;
536
+ let m = finishedMatch.parent;
537
+ let s = finishedMatch.state;
547
538
 
548
539
  if (!m) return m;
549
540
 
550
541
  m.running = null;
551
542
 
552
- finishedMatch.setRangePreviousIndex(null);
543
+ // TODO don't do this if we're falling back to a successful shift either
544
+ if (bind) {
545
+ for (let tag of Tags.traverse(this.rootNode.value.children)) {
546
+ if (tag.type === Property) {
547
+ let { reference, shift, tags } = tag.value;
548
+
549
+ if (
550
+ !['#', '@'].includes(reference.type) &&
551
+ !reference.flags.array &&
552
+ !has(reference.name, m.node) &&
553
+ !shift
554
+ ) {
555
+ m.advance(buildPropertyTag([tags[0], [], buildNullNode()]));
556
+ }
557
+ }
558
+ }
559
+ }
560
+
561
+ if (s.status === 'active') {
562
+ s.reject(finishedMatch);
563
+ }
564
+
565
+ // restore successful parts of shift stack here
566
+
567
+ // if (
568
+ // finishedMatch.isCoverBoundary &&
569
+ // finishedMatch.shiftMatch &&
570
+ // finishedMatch.effects.failure === 'none'
571
+ // ) {
572
+ // m.advance(finishedMatch.shiftMatch.rootNode);
573
+
574
+ // s.node = finishedMatch.shiftMatch.node;
575
+ // }
576
+
577
+ m.state.resultPath = TagPath.fromNode(m.state.node, -1);
553
578
 
554
579
  return m;
555
580
  }
556
581
 
557
582
  *emit(options) {
558
- let { state } = this;
559
- let { emitted } = state;
583
+ let { state, emitted, emittedMatch } = this;
560
584
 
561
- let m = emitted?.match ?? this;
585
+ let m = emittedMatch;
562
586
 
563
587
  if (!state.depth) {
564
- let { node } = m;
588
+ let node = m.node || m.rootNode;
565
589
 
566
- // if (!node) {
567
- // node = resultPath.node;
568
- // }
569
-
570
- let path = Path.from(node);
571
-
572
- let tagPath = emitted?.tagPath || (path.node.tags.size ? TagPath.from(path, 0) : null);
590
+ let tagPath = emitted
591
+ ? emitted
592
+ : Tags.getSize(getTags(node))
593
+ ? TagPath.fromNode(m.rootNode, 0)
594
+ : null;
573
595
 
574
596
  while (tagPath) {
575
- if (
576
- options.holdUndefinedAttributes &&
577
- tagPath.tag.type === OpenNodeTag &&
578
- tagPath.tag.value.type &&
579
- (m.node.undefinedAttributes ?? 0) > 0
580
- ) {
581
- break;
582
- }
597
+ // if (
598
+ // options.holdUndefinedAttributes &&
599
+ // tagPath.tag.type === OpenNodeTag &&
600
+ // tagPath.tag.value.name
601
+ // && (countUndefinedAttributes(m.node) ?? 0) > 0
602
+ // ) {
603
+ // break;
604
+ // }
583
605
 
584
606
  if (
585
607
  tagPath.tag.type === OpenNodeTag &&
@@ -588,70 +610,191 @@ export class Match extends WeakStackFrame {
588
610
  )
589
611
  break;
590
612
 
591
- let holdShifted =
613
+ let holdingShifted =
592
614
  options.holdShiftedNodes &&
593
615
  tagPath.tag.type === ReferenceTag &&
594
- tagPath.tag.value.flags.expression;
616
+ tagPath.tag.value.flags.expression &&
617
+ tagPath.depth <= 1;
595
618
 
596
- if (!state.emitted || !tagPath.equalTo(state.emitted.tagPath)) {
597
- state.emitted = emitted = { match: m, tagPath };
619
+ if (holdingShifted) {
620
+ if (!tagPath.equalTo(m.emitted)) {
621
+ m.emitted = emitted = tagPath;
622
+ this.emittedMatch = m;
598
623
 
599
- if (tagPath.tag.type === OpenNodeTag) {
600
- state.depths.emitted++;
601
- } else if (tagPath.tag.type === CloseNodeTag) {
602
- state.depths.emitted--;
624
+ yield tagPath.tag;
603
625
  }
604
626
 
627
+ if (
628
+ m.expressionMatch &&
629
+ (m.expressionMatch.running || !getCloseTag(m.expressionMatch.rootNode))
630
+ ) {
631
+ break;
632
+ } else {
633
+ let { parentPreviousTagPath } = m;
634
+ let nextTag =
635
+ parentPreviousTagPath &&
636
+ Tags.getAt(parentPreviousTagPath.tagsIndex + 2, m.parent.node.value.tags);
637
+
638
+ let doneShifting = !(nextTag?.type === Property && nextTag.value.shift);
639
+
640
+ // todo monomorph
641
+ let shiftIndex = nextTag?.type === Property ? nextTag?.value.shift?.index ?? 0 : 0;
642
+ while (!doneShifting && nextTag) {
643
+ nextTag =
644
+ Tags.getAt(
645
+ [parentPreviousTagPath.tagsIndex + 2 + shiftIndex, 1],
646
+ m.parent.node.value.tags,
647
+ ) ||
648
+ Tags.getAt(
649
+ parentPreviousTagPath.tagsIndex + 2 + shiftIndex,
650
+ m.parent.node.value.tags,
651
+ );
652
+ doneShifting = nextTag && !(nextTag.type === Property && nextTag.value.shift);
653
+ if (!doneShifting) {
654
+ shiftIndex++;
655
+ }
656
+ }
657
+
658
+ if ((parentPreviousTagPath && !nextTag) || !doneShifting) {
659
+ break;
660
+ } else {
661
+ if (m.parent.rootNode) {
662
+ if (shiftIndex) {
663
+ m = m.parent;
664
+ tagPath = Path.from(m.rootNode)
665
+ .tagPathAt(1)
666
+ .inner.tagPathAt(1 + shiftIndex, 0).nextSibling;
667
+ if (tagPath?.tag.type === TreeNode) {
668
+ tagPath = tagPath.inner.tagPathAt(0);
669
+ }
670
+ }
671
+ }
672
+
673
+ if (!tagPath) break;
674
+ }
675
+ }
676
+ }
677
+
678
+ if (tagPath.depth === 0 && endsNode(tagPath.tag)) {
679
+ let finishedMatch = m;
680
+ m = m.parent;
681
+
682
+ let parentRoot = Path.from(m.rootNode);
683
+
684
+ if (m.type !== '__') {
685
+ parentRoot = parentRoot.tagPathAt(getCloseTag(m.rootNode) ? -2 : -1).inner;
686
+ }
687
+
688
+ let finishedChildren =
689
+ finishedMatch.type !== '__' ? 1 : Tags.getSize(finishedMatch.rootNode.value.children);
690
+
691
+ let parentNextTagPath = parentRoot?.tagPathAt(
692
+ finishedMatch.parentPreviousTagPath.tagsIndex + finishedChildren + 1,
693
+ );
694
+
695
+ tagPath = parentNextTagPath;
696
+
697
+ if (!tagPath) {
698
+ if (m.running) {
699
+ m = m.running;
700
+
701
+ let shiftIndex =
702
+ finishedMatch.parentPreviousTagPath.tag.type === Property &&
703
+ finishedMatch.parentPreviousTagPath.tag.value.shift
704
+ ? finishedMatch.parentPreviousTagPath.tag.value.shift.index
705
+ : m.didShift
706
+ ? 1
707
+ : 0;
708
+
709
+ tagPath = TagPath.fromNode(m.rootNode, 1 + shiftIndex);
710
+ }
711
+ }
712
+
713
+ if (tagPath?.tag.type === Property) {
714
+ tagPath = tagPath.next;
715
+ }
716
+ continue;
717
+ }
718
+
719
+ if (!m.emitted || !tagPath.equalTo(m.emitted)) {
605
720
  if (
606
721
  !(
607
- (tagPath.tag.type === BindingTag && !tagPath.tag.value.languagePath?.length) ||
608
- (tagPath.tag.type === AttributeDefinition && options.holdUndefinedAttributes)
722
+ [OpenNodeTag, CloseNodeTag].includes(tagPath.tag.type) &&
723
+ tagPath.depth === 0 &&
724
+ isMultiFragment(tagPath.node) &&
725
+ (m.depth || !(m.matcher.type === '__'))
609
726
  )
610
727
  ) {
611
- yield tagPath.tag;
728
+ m.emitted = emitted = tagPath;
729
+ this.emittedMatch = m;
730
+
731
+ if (tagPath.tag.type === OpenNodeTag && !tagPath.tag.value.selfClosing) {
732
+ state.depths.emitted++;
733
+ } else if (tagPath.tag.type === CloseNodeTag) {
734
+ state.depths.emitted--;
735
+ }
736
+
737
+ if (
738
+ tagPath.tag.type !== Property &&
739
+ !(tagPath.tag.type === BindingTag && !tagPath.tag.value.segments?.length) &&
740
+ !(tagPath.tag.type === ShiftTag && options.holdShiftedNodes) &&
741
+ !(tagPath.tag.type === AttributeDefinition && options.holdUndefinedAttributes) &&
742
+ !(m.depth === 0 && !tagPath.depth && tagPath.tag.type === ReferenceTag)
743
+ ) {
744
+ yield tagPath.tag;
745
+ }
612
746
  }
613
747
  }
614
748
 
615
749
  if (
616
- (tagPath.tag.type === CloseNodeTag ||
617
- (tagPath.tag.type === OpenNodeTag && tagPath.tag.value.selfClosing)) &&
618
- !tagPath.next
750
+ endsNode(tagPath.tag) ||
751
+ (!holdingShifted && !tagPath.nextSibling && !tagPath.tag.type === Property)
619
752
  ) {
620
- tagPath = TagPath.from(m.fragmentPath, m.rangeInitial.tagsIndex + 1, 0);
753
+ let nextPath = tagPath.next;
754
+ if (nextPath) {
755
+ tagPath = nextPath;
756
+ continue;
757
+ }
621
758
 
622
759
  do {
623
- m = m.parent;
624
- } while (m && !m.isNode);
760
+ if (m.running) {
761
+ m = m.running;
762
+ // TODO is -1 ok
763
+ let shiftIndex = m.didShift ? -1 : 0;
764
+ tagPath = TagPath.fromNode(m.rootNode, shiftIndex);
765
+ } else if (m.parent && (!m.parent.running || m.parent.running !== m)) {
766
+ tagPath = m.parentPreviousTagPath;
767
+
768
+ tagPath = TagPath.from(
769
+ tagPath.path,
770
+ tagPath.tagsIndex +
771
+ 1 +
772
+ (m.matcher.type === '__' ? Tags.getSize(m.rootNode.value.children) : 1),
773
+ 0,
774
+ );
775
+
776
+ m = m.parent;
777
+ } else {
778
+ m = null;
779
+ break;
780
+ }
781
+ } while (m && !tagPath);
625
782
 
626
783
  if (!m) break;
627
784
 
628
785
  continue;
629
786
  }
630
787
 
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
- }
788
+ tagPath =
789
+ tagPath.tag.type === Property
790
+ ? tagPath.nextProperty?.next || tagPath.nextSibling
791
+ : options.holdShiftedNodes
792
+ ? tagPath.nextUnshifted
793
+ : tagPath.next;
642
794
 
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
- }
795
+ if (!tagPath && m.running) {
796
+ m = m.running;
797
+ tagPath = TagPath.fromNode(m.rootNode, 0);
655
798
  }
656
799
  }
657
800
  }