@bablr/bablr-vm 0.18.1 → 0.19.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 ADDED
@@ -0,0 +1,470 @@
1
+ import { resolveLanguage } from '@bablr/helpers/grammar';
2
+ import { WeakStackFrame } from '@bablr/weak-stack';
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';
16
+ import { buildInternalState, internalStates, FragmentFacade } from './node.js';
17
+
18
+ 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';
27
+ import { nodeStates } from './state.js';
28
+
29
+ const nodeTopType = Symbol.for('@bablr/node');
30
+
31
+ const tagPathsAreEqual = (a, b) => {
32
+ return !a || !b ? a == b : a.path.node === b.path.node && a.childrenIndex === b.childrenIndex;
33
+ };
34
+
35
+ export class MatchFacade {
36
+ constructor(match) {
37
+ facades.set(match, this);
38
+ }
39
+
40
+ get language() {
41
+ return actuals.get(this).language;
42
+ }
43
+
44
+ get matcher() {
45
+ return actuals.get(this).matcher;
46
+ }
47
+
48
+ get mergedReference() {
49
+ return actuals.get(this).mergedReference;
50
+ }
51
+
52
+ get propertyMatcher() {
53
+ return actuals.get(this).propertyMatcher;
54
+ }
55
+
56
+ get depth() {
57
+ return actuals.get(this).depth;
58
+ }
59
+
60
+ get path() {
61
+ return actuals.get(this).path;
62
+ }
63
+
64
+ get innerPath() {
65
+ return actuals.get(this).innerPath;
66
+ }
67
+
68
+ get pathDepth() {
69
+ return actuals.get(this).depths.path;
70
+ }
71
+
72
+ get pathName() {
73
+ return actuals.get(this).pathName;
74
+ }
75
+
76
+ get pathParent() {
77
+ return actuals.get(this).pathParent;
78
+ }
79
+
80
+ get node() {
81
+ const { node, ctx } = actuals.get(this);
82
+ return FragmentFacade.wrap(node, ctx, false);
83
+ }
84
+
85
+ get fragmentNode() {
86
+ const { fragmentNode, ctx } = actuals.get(this);
87
+ return FragmentFacade.wrap(fragmentNode, ctx, false);
88
+ }
89
+
90
+ get props() {
91
+ const { props, ctx } = actuals.get(this);
92
+ return FragmentFacade.wrap(props, ctx, false);
93
+ }
94
+
95
+ get type() {
96
+ return actuals.get(this).type;
97
+ }
98
+
99
+ get isNode() {
100
+ return actuals.get(this).isNode;
101
+ }
102
+
103
+ get isCover() {
104
+ return actuals.get(this).isCover;
105
+ }
106
+
107
+ get cover() {
108
+ return actuals.get(this).cover;
109
+ }
110
+
111
+ get captured() {
112
+ return actuals.get(this).captured;
113
+ }
114
+
115
+ get range() {
116
+ return actuals.get(this).range;
117
+ }
118
+
119
+ get effects() {
120
+ return actuals.get(this).effects;
121
+ }
122
+
123
+ get parent() {
124
+ return facades.get(actuals.get(this).parent);
125
+ }
126
+
127
+ get grammar() {
128
+ return actuals.get(this).grammar;
129
+ }
130
+
131
+ get state() {
132
+ return facades.get(actuals.get(this).state);
133
+ }
134
+
135
+ get s() {
136
+ return facades.get(actuals.get(this).s);
137
+ }
138
+
139
+ get rangePrevious() {
140
+ return actuals.get(this).rangePrevious;
141
+ }
142
+
143
+ get rangePreviousIndex() {
144
+ return actuals.get(this).rangePreviousIndex;
145
+ }
146
+
147
+ get rangeInitial() {
148
+ return actuals.get(this).rangeInitial;
149
+ }
150
+
151
+ get rangeFinal() {
152
+ return actuals.get(this).rangeFinal;
153
+ }
154
+
155
+ get rangeFinalIndex() {
156
+ return actuals.get(this).rangeFinalIndex;
157
+ }
158
+
159
+ ancestors(...args) {
160
+ return actuals.get(this).ancestors(...args);
161
+ }
162
+ }
163
+
164
+ 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
+ ) {
175
+ if (!context || !language || !state) {
176
+ throw new Error('Invalid arguments to Match constructor');
177
+ }
178
+
179
+ super(parent);
180
+
181
+ this.context = context;
182
+ this.language = language;
183
+ this.state = shouldBranch(effects) ? state.branch() : state;
184
+ this.propertyMatcher = matcher;
185
+ this.effects = effects;
186
+ this.didShift = didShift;
187
+
188
+ this.rangePreviousIndex = null;
189
+ this.rangeFinalIndex = null;
190
+ this.node = null;
191
+ this.fragmentNode = null; // why do it this way?
192
+ this.cover = null;
193
+ this.suppressNode = suppressNode;
194
+
195
+ let internalState;
196
+ let { isNode, isCover } = this;
197
+
198
+ this.cover = isCover ? this : isNode ? null : parent?.cover || null;
199
+
200
+ let shouldConstructNode = (isNode && !parent.isCover) || isCover || !parent;
201
+
202
+ if (shouldConstructNode) {
203
+ internalState = buildInternalState();
204
+ internalState.path.node;
205
+ } else {
206
+ const { instructionsPump, expressionsPump, agastState, agast, path } = internalStates.get(
207
+ parent.node,
208
+ );
209
+ internalState = { instructionsPump, expressionsPump, agastState, agast, path };
210
+ }
211
+
212
+ this.node = internalState.path.node;
213
+
214
+ if (this.isNode) {
215
+ this.node.type = Symbol.for(this.type);
216
+ this.node.language = this.language.canonicalURL;
217
+ }
218
+
219
+ internalStates.set(this.node, internalState);
220
+
221
+ new MatchFacade(this);
222
+ }
223
+
224
+ static from(context, language, state, matcher, props, suppressNode) {
225
+ return Match.create(context, language, state, matcher, effectsFor('eat'), props, suppressNode);
226
+ }
227
+
228
+ get matcher() {
229
+ return this.propertyMatcher?.nodeMatcher;
230
+ }
231
+
232
+ get mergedReference() {
233
+ let ref = buildReferenceTag('.');
234
+
235
+ let first = true;
236
+ for (const m of this.ancestors(true)) {
237
+ if (m.isNode && !first) break;
238
+ if (m.propertyMatcher.refMatcher) {
239
+ const { name, isArray, flags } = m.propertyMatcher.refMatcher;
240
+ const parentRef = buildReferenceTag(name, isArray, flags);
241
+ ref = ['#', '@'].includes(ref.value.name) ? ref : mergeReferences(ref, parentRef);
242
+ }
243
+ first = false;
244
+ }
245
+
246
+ return ref;
247
+ }
248
+
249
+ get pathName() {
250
+ return this.mergedReference.value.name;
251
+ }
252
+
253
+ get path() {
254
+ let { agastState } = internalStates.get(this.fragmentNode || this.node);
255
+ return agastState.path || agastState.resultPath.path;
256
+ }
257
+
258
+ get pathParent() {
259
+ let m = this;
260
+
261
+ do {
262
+ m = m.parent;
263
+ } while (m && !m.isNode);
264
+ return m;
265
+ }
266
+
267
+ get parentPath() {
268
+ return this.pathParent?.path;
269
+ }
270
+
271
+ get ctx() {
272
+ return this.context;
273
+ }
274
+
275
+ get grammar() {
276
+ return this.context.grammars.get(this.language);
277
+ }
278
+
279
+ get s() {
280
+ return this.state;
281
+ }
282
+
283
+ get type() {
284
+ return this.matcher?.type || null;
285
+ }
286
+
287
+ get flags() {
288
+ return this.matcher?.flags;
289
+ }
290
+
291
+ get captured() {
292
+ return !this.rangePrevious || !!this.rangeFinal;
293
+ }
294
+
295
+ get allowEmpty() {
296
+ return !!this.grammar.emptyables?.has(this.type);
297
+ }
298
+
299
+ get rangePrevious() {
300
+ return this.rangePreviousIndex == null ? null : new TagPath(this.path, this.rangePreviousIndex);
301
+ }
302
+
303
+ setRangePreviousIndex(value) {
304
+ this.rangePreviousIndex = value;
305
+ this.rangePrevious;
306
+ }
307
+
308
+ setRangeFinalIndex(value) {
309
+ this.rangeFinalIndex = value;
310
+ this.rangeFinal;
311
+ }
312
+
313
+ get rangeFinal() {
314
+ const path = !this.parent ? this.path : this.parent.path;
315
+
316
+ return this.rangeFinalIndex == null ? null : new TagPath(path, this.rangeFinalIndex);
317
+ }
318
+
319
+ get rangeInitial() {
320
+ const { rangePrevious } = this;
321
+
322
+ if (!rangePrevious) return rangePrevious;
323
+
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
+ return rangePrevious?.nextSibling;
332
+ }
333
+
334
+ get range() {
335
+ const { rangeInitial, rangeFinal } = this;
336
+ return rangeInitial === null ? null : [rangeInitial, rangeFinal];
337
+ }
338
+
339
+ get referencePath() {
340
+ const previous = this.rangePrevious;
341
+
342
+ if (!this.isNode) {
343
+ return null;
344
+ }
345
+
346
+ if (this.parent.cover) {
347
+ return previous.nextSibling;
348
+ }
349
+
350
+ if (this.didShift) {
351
+ let tagPath = previous;
352
+ while ([GapTag, ShiftTag].includes(tagPath.tag.type)) {
353
+ tagPath = tagPath.previousSibling;
354
+ }
355
+ return tagPath;
356
+ }
357
+
358
+ return previous.nextSibling;
359
+ }
360
+
361
+ get isNode() {
362
+ const { grammar, type } = this;
363
+ return !this.suppressNode && grammar.covers?.get(nodeTopType).has(type);
364
+ }
365
+
366
+ get isCover() {
367
+ const { grammar, type } = this;
368
+ return grammar.covers?.has(type);
369
+ }
370
+
371
+ get innerPath() {
372
+ return internalStates.get(this.node).path;
373
+ }
374
+
375
+ add(node) {
376
+ const { expressionsPump, instructionsPump, agast, agastState } = internalStates.get(
377
+ this.fragmentNode,
378
+ );
379
+
380
+ expressionsPump.queue(node);
381
+ instructionsPump.queue(buildCall('advance', buildEmbeddedTag(buildGapTag())));
382
+
383
+ agast.next();
384
+ }
385
+
386
+ startFrame(state, propertyMatcher, effects, didShift, suppressNode) {
387
+ let { context } = this;
388
+ const { nodeMatcher } = propertyMatcher;
389
+
390
+ const language = resolveLanguage(context, this.language, nodeMatcher.language);
391
+
392
+ if (!language) {
393
+ throw new Error(`Unknown language ${nodeMatcher.language.join('.')}`);
394
+ }
395
+
396
+ return this.push(context, language, state, propertyMatcher, effects, didShift, suppressNode);
397
+ }
398
+
399
+ endFrame() {
400
+ const finishedMatch = this;
401
+ const m = finishedMatch.parent;
402
+ let finishedState = finishedMatch.state;
403
+
404
+ // if (
405
+ // finishedMatch.isNode &&
406
+ // finishedState.status !== 'rejected' &&
407
+ // !getCloseTag(finishedMatch.node)
408
+ // )
409
+ // throw new Error();
410
+
411
+ if (!m) return m;
412
+
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
+ }
427
+
428
+ return m;
429
+ }
430
+
431
+ *emit() {
432
+ let { state } = this;
433
+
434
+ if (!state.depth) {
435
+ let { node, emitted } = state;
436
+
437
+ let { path } = internalStates.get(node);
438
+
439
+ let tagPath = emitted || (btree.getSum(path.node.children) ? new TagPath(path, 0) : null);
440
+
441
+ // two basic cases:
442
+ // emitted can move
443
+ // emitted cannot move
444
+
445
+ while (tagPath) {
446
+ if (
447
+ tagPath.tag.type === OpenNodeTag &&
448
+ tagPath.tag.value.type &&
449
+ nodeStates.get(tagPath.path.node).unboundAttributes?.size
450
+ ) {
451
+ break;
452
+ }
453
+
454
+ if (!tagPathsAreEqual(tagPath, state.emitted)) {
455
+ state.emitted = emitted = tagPath;
456
+
457
+ if (tagPath.tag.type === OpenNodeTag) {
458
+ state.depths.emitted++;
459
+ } else if (tagPath.tag.type === CloseNodeTag) {
460
+ state.depths.emitted--;
461
+ }
462
+
463
+ yield tagPath.tag;
464
+ }
465
+
466
+ tagPath = emitted.next;
467
+ }
468
+ }
469
+ }
470
+ }