@bablr/bablr-vm 0.19.2 → 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/context.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { buildDependentLanguages } from '@bablr/helpers/grammar';
2
2
  import { facades, actuals } from './facades.js';
3
3
  import { getPrototypeOf } from '@bablr/helpers/object';
4
- import { reifyExpressionShallow } from '@bablr/agast-vm-helpers';
5
4
  import { states as nodeStates } from './node.js';
6
5
 
7
- import { sourceTextFor } from '@bablr/agast-helpers/tree';
6
+ import { sourceTextFor } from '@bablr/agast-helpers/stream';
7
+ import { streamFromTree } from '@bablr/agast-helpers/tree';
8
+ import { allTagsFor, buildFullRange, Path, TagPath } from '@bablr/agast-helpers/path';
8
9
 
9
10
  export const ContextFacade = class BABLRContextFacade {
10
11
  constructor(actual) {
@@ -30,7 +31,31 @@ export const ContextFacade = class BABLRContextFacade {
30
31
  // }
31
32
 
32
33
  sourceTextFor(node) {
33
- return node && sourceTextFor(nodeStates.get(node).node);
34
+ let fragmentStream;
35
+ if (!node) return null;
36
+ if (getPrototypeOf(node) === Object.prototype) {
37
+ fragmentStream = streamFromTree(node);
38
+ } else if (nodeStates.has(node)) {
39
+ let state = nodeStates.get(node);
40
+ let { childrenIndexRange } = state;
41
+ let path = Path.from(state.fragmentNode);
42
+
43
+ if (!childrenIndexRange) {
44
+ childrenIndexRange = buildFullRange(state.node);
45
+ }
46
+
47
+ fragmentStream = allTagsFor(
48
+ [
49
+ TagPath.from(path, childrenIndexRange[0]),
50
+ TagPath.from(path, childrenIndexRange[1] ?? undefined),
51
+ ],
52
+ { unshift: true },
53
+ );
54
+ } else {
55
+ return null;
56
+ }
57
+
58
+ return fragmentStream && sourceTextFor(fragmentStream);
34
59
  }
35
60
  };
36
61
 
@@ -56,13 +81,4 @@ export const Context = class BABLRContext {
56
81
  this.grammars.set(language, new language.grammar());
57
82
  }
58
83
  }
59
-
60
- unbox(value) {
61
- const { unboxedValues } = this;
62
- if (!unboxedValues.has(value)) {
63
- unboxedValues.set(value, reifyExpressionShallow(value));
64
- }
65
-
66
- return unboxedValues.get(value);
67
- }
68
84
  };
package/lib/evaluate.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Coroutine } from '@bablr/coroutine';
2
- import { buildOpenNodeTag, buildShiftTag, buildWriteEffect } from '@bablr/agast-helpers/builders';
2
+ import { buildEmbeddedNode, buildOpenNodeTag, buildShiftTag } from '@bablr/agast-helpers/builders';
3
3
  import { getStreamIterator, printType, StreamIterable } from '@bablr/agast-helpers/stream';
4
4
  import { formatType } from './utils/format.js';
5
5
  import { facades } from './facades.js';
@@ -15,43 +15,21 @@ import {
15
15
  NullTag,
16
16
  ShiftTag,
17
17
  } from '@bablr/agast-helpers/symbols';
18
- import * as sym from '@bablr/agast-helpers/symbols';
19
- import { internalStates, FragmentFacade } from './node.js';
20
- import {
21
- buildArrayInitializerTag,
22
- buildNullTag,
23
- getOpenTag,
24
- treeFromStreamSync,
25
- } from '@bablr/agast-helpers/tree';
26
- import * as btree from '@bablr/agast-helpers/btree';
27
- import { getEmbeddedTag } from '@bablr/agast-vm-helpers/deembed';
18
+ import * as sym from '@bablr/agast-vm-helpers/symbols';
19
+ import { FragmentFacade, states } from './node.js';
20
+ import { defineAttribute as defineAttribute_, treeFromStreamSync } from '@bablr/agast-helpers/tree';
21
+ import * as sumtree from '@bablr/agast-helpers/sumtree';
22
+ import { getEmbeddedObject, getEmbeddedTag } from '@bablr/agast-vm-helpers/deembed';
28
23
  import { Match } from './match.js';
29
- import { reifyExpression, shouldBranch } from '@bablr/agast-vm-helpers';
24
+ import { effectsFor, reifyExpression } from '@bablr/agast-vm-helpers';
30
25
  import { TagPath } from '@bablr/agast-helpers/path';
31
26
  import { getProduction } from '@bablr/helpers/grammar';
27
+ import { buildWriteEffect } from '@bablr/agast-vm-helpers/builders';
32
28
 
33
- const { hasOwn } = Object;
34
-
35
- const bindAttribute = (m, s, key, value) => {
36
- const openTag = getOpenTag(m.node);
37
-
38
- if (value != null) {
39
- const { flags, language, type } = openTag.value;
40
- const attributes = { ...openTag.value.attributes, [key]: value };
41
- const newOpenTag = buildOpenNodeTag(flags, language, type, attributes);
29
+ const defineAttribute = (m, s, key, value) => {
30
+ defineAttribute_(m.node, key, value);
42
31
 
43
- m.node.attributes = attributes;
44
-
45
- // if (openNext) {
46
- // } else {
47
- // // could this tag be stored anywhere else?
48
- // s.resultPath = newOpenTag;
49
- // }
50
-
51
- m.node.children = btree.replaceAt(0, m.node.children, newOpenTag);
52
- }
53
-
54
- nodeStates.get(m.node).unboundAttributes.delete(key);
32
+ nodeStates.get(m.node).undefinedAttributes.delete(key);
55
33
  };
56
34
 
57
35
  const getSourceLength = (tags) => {
@@ -71,30 +49,23 @@ export const bablr = (ctx, rootSource, strategy, options = {}) => {
71
49
  };
72
50
 
73
51
  function* __bablr(ctx, rootSource, strategy, options) {
74
- let s = State.from(rootSource, ctx, options.expressions);
52
+ let s = null;
75
53
  let m = null;
76
54
  let language = null;
55
+ let finishedMatch = null;
77
56
 
78
- let co = new Coroutine(getStreamIterator(strategy(facades.get(s), facades.get(ctx))));
57
+ let co = new Coroutine(getStreamIterator(strategy(facades.get(ctx))));
79
58
 
80
59
  co.advance();
81
60
 
82
- {
83
- s.source.advance();
84
-
85
- const sourceStep = s.source.fork.head.step;
86
-
87
- if (sourceStep instanceof Promise) {
88
- yield sourceStep;
89
- }
90
- }
91
-
92
61
  for (;;) {
93
62
  if (co.current instanceof Promise) {
94
63
  co.current = yield co.current;
95
64
  }
96
65
 
97
- if (co.done) break;
66
+ if (co.done) {
67
+ return co.value && states.get(co.value).node;
68
+ }
98
69
 
99
70
  const instr = co.value;
100
71
  let returnValue = undefined;
@@ -107,7 +78,19 @@ function* __bablr(ctx, rootSource, strategy, options) {
107
78
 
108
79
  if (language !== null) throw new Error();
109
80
 
81
+ s = State.from(rootSource, ctx, canonicalURL, options.expressions);
82
+
83
+ s.source.advance();
84
+
85
+ const sourceStep = s.source.fork.head.step;
86
+
87
+ if (sourceStep instanceof Promise) {
88
+ yield sourceStep;
89
+ }
90
+
110
91
  language = ctx.languages.get(canonicalURL);
92
+
93
+ returnValue = facades.get(s);
111
94
  break;
112
95
  }
113
96
 
@@ -120,6 +103,9 @@ function* __bablr(ctx, rootSource, strategy, options) {
120
103
  s.referencePath = null;
121
104
  }
122
105
 
106
+ if (m.cover && !m.isNode && !(s.holding && tag.type === GapTag))
107
+ throw new Error('cannot advance inside a cover');
108
+
123
109
  switch (tag.type) {
124
110
  case DoctypeTag: {
125
111
  s.node = m.node;
@@ -134,7 +120,8 @@ function* __bablr(ctx, rootSource, strategy, options) {
134
120
  case ReferenceTag: {
135
121
  s.advance(tag);
136
122
 
137
- s.referencePath = TagPath.from(m.path, -1);
123
+ s.referencePath = TagPath.fromNode(s.node, -1);
124
+ if (s.referencePath.tag.type === GapTag) throw new Error();
138
125
  break;
139
126
  }
140
127
 
@@ -142,13 +129,17 @@ function* __bablr(ctx, rootSource, strategy, options) {
142
129
  s.depths.path++;
143
130
 
144
131
  if (tag.value.type) {
145
- if (s.depth === 0 && !m.mergedReference.value.flags.expression) {
146
- m.add(m.node);
147
- }
132
+ m.add(m.node);
148
133
 
149
134
  s.node = m.node;
150
135
  }
151
136
 
137
+ for (const { 0: key, 1: value } of Object.entries(tag.value.attributes)) {
138
+ if (value === undefined) {
139
+ nodeStates.get(m.node).undefinedAttributes.add(key);
140
+ }
141
+ }
142
+
152
143
  s.advance(tag);
153
144
 
154
145
  if (tag.value.type) {
@@ -161,10 +152,12 @@ function* __bablr(ctx, rootSource, strategy, options) {
161
152
  case CloseNodeTag: {
162
153
  const { node } = s;
163
154
 
164
- if (btree.getAt(0, node.children).value.type) {
165
- const refPath = m.referencePath;
155
+ s.depths.path--;
166
156
 
167
- if (refPath.tag.type === ReferenceTag && refPath?.tag.value.name === '@') {
157
+ if (sumtree.getAt(0, node.children).value.type) {
158
+ const refPath = m.unshiftedReferencePath;
159
+
160
+ if (refPath?.tag.type === ReferenceTag && refPath?.tag.value.name === '@') {
168
161
  const cooked = node.flags.hasGap
169
162
  ? null
170
163
  : ctx.languages
@@ -174,29 +167,28 @@ function* __bablr(ctx, rootSource, strategy, options) {
174
167
  node,
175
168
  ctx,
176
169
  true,
177
- [refPath.childrenIndex, refPath.childrenIndex],
170
+ [refPath.childrenIndex, refPath.childrenIndex + 1],
178
171
  null,
179
172
  ),
180
173
  s.span.name,
181
174
  facades.get(ctx),
182
175
  ) || null;
183
176
 
184
- bindAttribute(m, s, 'cooked', cooked);
177
+ defineAttribute(m, s, 'cooked', cooked);
185
178
 
186
- nodeStates.get(m.node).unboundAttributes.delete('cooked');
179
+ nodeStates.get(m.node).undefinedAttributes.delete('cooked');
187
180
  }
188
181
 
189
182
  s.advance(tag);
190
183
 
184
+ // if (s.depth > 0) {
185
+ // m.add(m.node);
186
+ // }
187
+
191
188
  s.node = m.fragmentNode;
192
- s.depths.path--;
193
189
 
194
190
  updateSpans(m, s.resultPath.path.node, 'close');
195
191
 
196
- if (s.depth > 0 || m.mergedReference.value.flags.expression) {
197
- m.add(m.node);
198
- }
199
-
200
192
  if (!m.parent) {
201
193
  if (!s.source.done) {
202
194
  throw new Error('Parser failed to consume input');
@@ -208,7 +200,7 @@ function* __bablr(ctx, rootSource, strategy, options) {
208
200
  }
209
201
  } else {
210
202
  s.advance(tag);
211
- s.node = m.node;
203
+ s.node = m.fragmentNode;
212
204
  }
213
205
 
214
206
  break;
@@ -221,7 +213,7 @@ function* __bablr(ctx, rootSource, strategy, options) {
221
213
  if (
222
214
  s.resultPath.tag.type === OpenNodeTag &&
223
215
  s.resultPath.tag.value.attributes.balancer &&
224
- s.balanced.value.attributes.balanced === pattern
216
+ s.balanced.value?.attributes.balanced === pattern
225
217
  ) {
226
218
  result = s.match(pattern);
227
219
  } else {
@@ -247,7 +239,7 @@ function* __bablr(ctx, rootSource, strategy, options) {
247
239
  }
248
240
 
249
241
  case GapTag: {
250
- if (s.source.value == null && !s.source.done) {
242
+ if (s.source.value == null && (!s.source.done || s.source.holding)) {
251
243
  if (s.source.holding) {
252
244
  s.source.unshift();
253
245
  } else {
@@ -261,7 +253,8 @@ function* __bablr(ctx, rootSource, strategy, options) {
261
253
  if (s.held) {
262
254
  m.add(s.held);
263
255
 
264
- s.resultPath = s.resultPath.nextSibling;
256
+ s.resultPath = TagPath.fromNode(s.resultPath.node, -1);
257
+
265
258
  s.held = null;
266
259
  break;
267
260
  }
@@ -275,7 +268,7 @@ function* __bablr(ctx, rootSource, strategy, options) {
275
268
  break;
276
269
  }
277
270
 
278
- if (btree.getSum(s.node.children)) {
271
+ if (sumtree.getSize(s.node.children)) {
279
272
  s.advance(tag);
280
273
  } else {
281
274
  m.add(m.node);
@@ -283,6 +276,8 @@ function* __bablr(ctx, rootSource, strategy, options) {
283
276
  s.advance(tag);
284
277
  }
285
278
 
279
+ s.referencePath = null;
280
+
286
281
  s.node = m.fragmentNode;
287
282
  } else {
288
283
  throw new Error('Failed to advance gap');
@@ -313,7 +308,7 @@ function* __bablr(ctx, rootSource, strategy, options) {
313
308
 
314
309
  let node = result && treeFromStreamSync(result);
315
310
 
316
- returnValue = result && FragmentFacade.wrap(node, ctx, true);
311
+ returnValue = node && buildEmbeddedNode(node);
317
312
  break;
318
313
  }
319
314
 
@@ -329,18 +324,48 @@ function* __bablr(ctx, rootSource, strategy, options) {
329
324
  break;
330
325
  }
331
326
 
327
+ case 'branch': {
328
+ s = s.branch();
329
+
330
+ returnValue = facades.get(s);
331
+ break;
332
+ }
333
+
334
+ case 'accept': {
335
+ s = s.accept(finishedMatch.fragmentNode);
336
+
337
+ returnValue = facades.get(s);
338
+ break;
339
+ }
340
+
341
+ case 'reject': {
342
+ let finishedState = s;
343
+ const { arguments: { 0: embeddedOptions } = [] } = instr;
344
+ let options = getEmbeddedObject(embeddedOptions);
345
+
346
+ s = finishedState.reject(
347
+ finishedMatch,
348
+ // finishedMatch.effects.success === 'none',
349
+ options,
350
+ );
351
+
352
+ s = finishedState.parent;
353
+
354
+ returnValue = facades.get(s);
355
+ break;
356
+ }
357
+
332
358
  case 'startFrame': {
333
- const {
334
- arguments: {
335
- 0: { value: matcher } = {},
336
- 1: { value: effects } = {},
337
- 2: { value: options = {} } = {},
338
- } = [],
359
+ let {
360
+ arguments: { 0: verb, 1: { value: matcher } = {}, 2: { value: options = {} } = {} } = [],
339
361
  } = instr;
340
362
 
341
- const { unboundAttributes, didShift, suppressNode } = options;
363
+ let effects = effectsFor(verb.description);
364
+ let didShift = verb.description.startsWith('holdFor');
365
+
366
+ let { undefinedAttributes } = options;
342
367
 
343
- const parentMatch = m;
368
+ let parentMatch = m;
344
369
 
345
370
  if (!language) throw new Error('not initialized');
346
371
 
@@ -349,8 +374,10 @@ function* __bablr(ctx, rootSource, strategy, options) {
349
374
  if (didShift && !parentMatch) throw new Error();
350
375
 
351
376
  m = parentMatch
352
- ? parentMatch.startFrame(s, matcher_, effects, didShift, suppressNode)
353
- : Match.from(ctx, language, s, matcher_, null, suppressNode);
377
+ ? parentMatch.startFrame(s, matcher_, effects, didShift ? finishedMatch : null, options)
378
+ : Match.from(ctx, language, s, matcher_, null, options);
379
+
380
+ finishedMatch = null;
354
381
 
355
382
  if (m.isNode && m.isCover) throw new Error();
356
383
 
@@ -365,43 +392,46 @@ function* __bablr(ctx, rootSource, strategy, options) {
365
392
  if (matcher_.refMatcher) {
366
393
  let m = matcher_.refMatcher;
367
394
  if (!(m.name === '.' && !m.flags.expression && !m.flags.hasGap && !m.isArray)) {
368
- throw new Error();
395
+ throw new Error('no references inside covers');
369
396
  }
370
397
  }
371
398
  }
372
399
 
373
- s = m.state;
374
-
375
400
  if (didShift) {
376
401
  s.source.shift();
377
402
  s.held = s.resultPath.node;
378
403
 
379
- let refTag = btree.getAt(-2, parentMatch.node.children);
404
+ let refTag = sumtree.getAt(-2, s.node.children);
380
405
 
381
406
  s.advance(buildShiftTag(refTag.type === ShiftTag ? refTag.value.index + 1 : 1));
407
+ s.referencePath = TagPath.fromNode(s.node, -1);
382
408
  }
383
409
 
384
- if (!m.isNode && options.unboundAttributes) throw new Error();
410
+ if (!m.isNode && options.undefinedAttributes) throw new Error();
385
411
 
386
412
  m.fragmentNode = s.node;
387
413
 
388
414
  nodeStates.set(m.node, {
389
- unboundAttributes: m.isNode
390
- ? new Set(unboundAttributes)
391
- : new Set(parentMatch ? nodeStates.get(parentMatch.node).unboundAttributes || [] : []),
415
+ undefinedAttributes: m.isNode
416
+ ? new Set(undefinedAttributes)
417
+ : new Set(
418
+ parentMatch ? nodeStates.get(parentMatch.node).undefinedAttributes || [] : [],
419
+ ),
392
420
  });
393
421
 
394
422
  ({ language } = m);
395
423
 
396
424
  if (parentMatch) {
397
425
  let previousIndex = [CloseNodeTag, NullTag, GapTag].includes(s.resultPath.tag.type)
398
- ? btree.getSum(m.fragmentNode.children) - 1
426
+ ? sumtree.getSize(m.fragmentNode.children) - 1
399
427
  : s.resultPath.childrenIndex;
400
428
 
401
429
  m.setRangePreviousIndex(previousIndex);
402
430
  }
403
431
 
404
432
  returnValue = facades.get(m);
433
+
434
+ m.range;
405
435
  break;
406
436
  }
407
437
 
@@ -409,71 +439,48 @@ function* __bablr(ctx, rootSource, strategy, options) {
409
439
  const {
410
440
  arguments: { 0: hasContinuation },
411
441
  } = instr;
412
- const finishedMatch = m;
442
+ finishedMatch = m;
413
443
 
414
444
  m = m.endFrame();
415
445
 
416
- if (m && internalStates.get(m.s.node).path.node !== m.s.node) {
417
- throw new Error('waaat');
418
- }
419
-
420
446
  if (!m) {
421
447
  returnValue = m;
422
448
  break;
423
449
  }
424
450
 
425
- s = m.state;
426
-
427
451
  if (finishedMatch.state.status !== 'rejected') {
428
452
  yield* m.emit();
429
453
  }
430
454
 
431
- returnValue = facades.get(m.node);
455
+ returnValue = m && facades.get(m);
432
456
  break;
433
457
  }
434
458
 
435
- case 'bindAttribute': {
436
- const { arguments: { 0: key, 1: value } = [] } = instr;
459
+ case 'throw': {
460
+ finishedMatch = m;
437
461
 
438
- bindAttribute(m, s, key, value);
462
+ m = m.throw_();
439
463
 
440
- yield* m.emit();
464
+ s.status = 'rejected';
465
+ if (m && finishedMatch.s === s) {
466
+ s.node = m.node;
467
+ }
441
468
 
469
+ returnValue = m && facades.get(m);
442
470
  break;
443
471
  }
444
472
 
445
- case 'throw': {
446
- s.reject();
447
-
448
- let rejectedMatch = m;
449
-
450
- m = m.endFrame();
451
- s = m.state;
452
-
453
- let ref = null;
454
-
455
- if (rejectedMatch.isNode) {
456
- ref = rejectedMatch.mergedReference;
457
- }
473
+ case 'defineAttribute': {
474
+ const { arguments: { 0: key, 1: value } = [] } = instr;
458
475
 
459
- if (ref && ref.value.name !== '#' && !rejectedMatch.didShift) {
460
- if (shouldBranch(rejectedMatch.effects) && !hasOwn(s.node.properties, ref.value.name)) {
461
- s.advance(ref);
476
+ if (!nodeStates.get(m.node).undefinedAttributes.has(key))
477
+ throw new Error('undefined attributes must be declared');
478
+ if (value && typeof value === 'object') throw new Error('unimplemented');
462
479
 
463
- if (ref.value.isArray) {
464
- s.advance(buildArrayInitializerTag());
465
- } else {
466
- s.advance(buildNullTag());
467
- }
468
- }
469
- }
480
+ defineAttribute(m, s, key, value);
470
481
 
471
- if (!m) {
472
- returnValue = m;
473
- break;
474
- }
482
+ yield* m.emit();
475
483
 
476
- returnValue = facades.get(m);
477
484
  break;
478
485
  }
479
486