@bablr/bablr-vm 0.17.0 → 0.18.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 ADDED
@@ -0,0 +1,75 @@
1
+ import { buildDependentLanguages } from '@bablr/helpers/grammar';
2
+ import { facades, actuals } from '../../bablr-vm-strategy-parse/lib/facades.js';
3
+ import { getPrototypeOf } from '@bablr/helpers/object';
4
+ import { actuals as nodeActuals } from './node.js';
5
+
6
+ export const ContextFacade = class BABLRContextFacade {
7
+ get languages() {
8
+ return actuals.get(this).languages;
9
+ }
10
+
11
+ get grammars() {
12
+ return actuals.get(this).grammars;
13
+ }
14
+
15
+ get productionEnhancer() {
16
+ return actuals.get(this).productionEnhancer;
17
+ }
18
+
19
+ get agast() {
20
+ return actuals.get(this).agast;
21
+ }
22
+
23
+ getPreviousTag(token) {
24
+ return actuals.get(this).agast.getPreviousTag(token);
25
+ }
26
+
27
+ allTagsFor(range) {
28
+ return actuals.get(this).agast.allTagsFor(range);
29
+ }
30
+
31
+ ownTagsFor(range) {
32
+ return actuals.get(this).agast.ownTagsFor(range);
33
+ }
34
+
35
+ getCooked(node) {
36
+ return actuals.get(this).agast.getCooked(actuals.get(node));
37
+ }
38
+
39
+ reifyExpression(value) {
40
+ return actuals.get(this).agast.reifyExpression(value);
41
+ }
42
+
43
+ sourceTextFor(node) {
44
+ return actuals.get(this).agast.sourceTextFor(nodeActuals.get(node));
45
+ }
46
+
47
+ unbox(value) {
48
+ return actuals.get(this).agast.unbox(value);
49
+ }
50
+ };
51
+
52
+ export const Context = class BABLRContext {
53
+ static from(agastContext, language, productionEnhancer) {
54
+ return new Context(agastContext, buildDependentLanguages(language), productionEnhancer);
55
+ }
56
+
57
+ constructor(agastContext, languages, productionEnhancer) {
58
+ this.agast = agastContext;
59
+ this.languages = languages;
60
+ this.productionEnhancer = productionEnhancer;
61
+
62
+ this.grammars = new WeakMap();
63
+ this.facade = new ContextFacade();
64
+
65
+ for (const { 1: language } of this.languages) {
66
+ let { prototype } = language.grammar;
67
+ while (prototype && prototype !== Object.prototype) {
68
+ prototype = getPrototypeOf(prototype);
69
+ }
70
+ this.grammars.set(language, new language.grammar());
71
+ }
72
+
73
+ facades.set(this, this.facade);
74
+ }
75
+ };
package/lib/index.js CHANGED
@@ -1,3 +1,5 @@
1
1
  export { createBablrStrategy } from './strategy.js';
2
2
 
3
3
  export { Source } from './source.js';
4
+
5
+ export { Context } from './context.js';
package/lib/node.js ADDED
@@ -0,0 +1,97 @@
1
+ import { parsePath } from '@bablr/agast-helpers/path';
2
+ import { ReferenceTag } from '@bablr/agast-helpers/symbols';
3
+ import {
4
+ buildGapTag,
5
+ buildStubNode,
6
+ get,
7
+ getCloseTag,
8
+ getOpenTag,
9
+ } from '@bablr/agast-helpers/tree';
10
+ import * as btree from '@bablr/agast-helpers/btree';
11
+
12
+ const { hasOwn } = Object;
13
+
14
+ export const contexts = new WeakMap();
15
+ export const actuals = new WeakMap();
16
+ // eslint-disable-next-line no-undef
17
+ export const transparentFacades = new WeakSet();
18
+
19
+ export const NodeFacade = class BABLRNodeFacade {
20
+ static wrap(node, context, transparent) {
21
+ if (!node || !context) throw new Error();
22
+ return node && new NodeFacade(node, context, transparent);
23
+ }
24
+
25
+ constructor(node, context, transparent) {
26
+ actuals.set(this, node);
27
+ contexts.set(this, context);
28
+ if (transparent) {
29
+ transparentFacades.add(this);
30
+ }
31
+ }
32
+
33
+ get isTransparent() {
34
+ return transparentFacades.has(this);
35
+ }
36
+
37
+ get children() {
38
+ const node = actuals.get(this);
39
+ const isTransparent = transparentFacades.has(this);
40
+ return {
41
+ *[Symbol.iterator]() {
42
+ if (isTransparent) {
43
+ yield* btree.traverse(node.children);
44
+ } else {
45
+ for (const child of btree.traverse(node.children)) {
46
+ const interpolated = false; // TODO
47
+ if (!interpolated) {
48
+ yield child;
49
+ }
50
+ }
51
+ }
52
+ },
53
+ };
54
+ }
55
+
56
+ get flags() {
57
+ return actuals.get(this).flags;
58
+ }
59
+
60
+ get language() {
61
+ return actuals.get(this).language;
62
+ }
63
+
64
+ get type() {
65
+ return actuals.get(this).type;
66
+ }
67
+
68
+ get attributes() {
69
+ return actuals.get(this).attributes;
70
+ }
71
+
72
+ get openTag() {
73
+ return getOpenTag(actuals.get(this));
74
+ }
75
+
76
+ get closeTag() {
77
+ return getCloseTag(actuals.get(this));
78
+ }
79
+
80
+ get(path) {
81
+ const context = contexts.get(this);
82
+ const node = get(actuals.get(this), path);
83
+ const isTransparent = transparentFacades.has(this);
84
+
85
+ const ref = node && context.agast.getPreviousTag(btree.getAt(0, node.children));
86
+ const node_ =
87
+ (ref && ref.type === ReferenceTag && !ref.value.hasGap) || isTransparent
88
+ ? node
89
+ : buildStubNode(buildGapTag());
90
+
91
+ return node && NodeFacade.wrap(node_, context, isTransparent);
92
+ }
93
+
94
+ has(path) {
95
+ return hasOwn(actuals.get(this).properties, parsePath(path).name);
96
+ }
97
+ };
package/lib/source.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { WeakStackFrame } from '@bablr/weak-stack';
2
2
  import { maybeWait, getStreamIterator, emptyStreamIterator } from '@bablr/agast-helpers/stream';
3
- import { facades, actuals } from './facades.js';
3
+ import { facades, actuals } from '../../bablr-vm-strategy-parse/lib/facades.js';
4
4
 
5
5
  // Queue item instances are shared between all forks.
6
6
  class QueueItem {
package/lib/spans.js CHANGED
@@ -1,30 +1,24 @@
1
- import { getAt } from '@bablr/agast-helpers/btree';
1
+ import { ReferenceTag } from '@bablr/agast-helpers/symbols';
2
2
  import { getOpenTag } from '@bablr/agast-helpers/tree';
3
3
 
4
4
  export function updateSpans(ctx, s, node, phase) {
5
+ const { flags, attributes } = node;
6
+
7
+ const openTag = getOpenTag(node);
8
+
9
+ const ref = ctx.agast.getPreviousTag(openTag);
10
+
11
+ const intrinsic = ref.type === ReferenceTag && !ref.hasGap;
12
+
5
13
  switch (phase) {
6
14
  case 'open': {
7
- const { attributes, flags } = node;
8
- const { span: innerSpan, balanced, balancedSpan, balancer, openSpan } = attributes || {};
15
+ const { balancedSpan, span: innerSpan, balanced, balancer, openSpan } = attributes || {};
9
16
 
10
- if (!flags.intrinsic && (balancer || balanced)) {
17
+ if (!intrinsic && (balancer || balanced)) {
11
18
  throw new Error('balanced tokens must be instrinsic');
12
19
  }
13
20
 
14
- if (flags.intrinsic) {
15
- if (s.path && balanced) {
16
- s.spans = s.spans.push({
17
- type: 'Lexical',
18
- name: balancedSpan || s.span.name,
19
- path: s.path,
20
- guard: balanced,
21
- });
22
-
23
- if (innerSpan) {
24
- throw new Error();
25
- }
26
- }
27
- }
21
+ if (balancedSpan && !balanced) throw new Error();
28
22
 
29
23
  if (openSpan) {
30
24
  s.spans = s.spans.push({
@@ -40,7 +34,7 @@ export function updateSpans(ctx, s, node, phase) {
40
34
 
41
35
  if (!s.balanced.size) throw new Error();
42
36
 
43
- if (!getOpenTag(balancedNode).value.attributes.balanced) {
37
+ if (!balancedNode.attributes.balanced) {
44
38
  throw new Error();
45
39
  }
46
40
 
@@ -49,10 +43,6 @@ export function updateSpans(ctx, s, node, phase) {
49
43
  s.spans = s.spans.pop();
50
44
  }
51
45
 
52
- if (balanced) {
53
- s.balanced = s.balanced.push(s.nodeForTag(s.result));
54
- }
55
-
56
46
  if (innerSpan) {
57
47
  s.spans = s.spans.push({
58
48
  type: 'Inner',
@@ -66,15 +56,16 @@ export function updateSpans(ctx, s, node, phase) {
66
56
  }
67
57
 
68
58
  case 'close': {
69
- const { flags, attributes } = node;
70
59
  const { balancedSpan, span: innerSpan, closeSpan, balanced } = attributes || {};
71
60
 
72
- if (balanced && !flags.intrinsic) {
61
+ if (balanced) {
62
+ s.balanced = s.balanced.push(s.nodeForTag(s.result));
63
+
73
64
  s.spans = s.spans.push({
74
65
  type: 'Lexical',
75
66
  name: balancedSpan || s.span.name,
76
67
  path: s.path,
77
- guard: balanced,
68
+ guard: balanced === true ? null : balanced,
78
69
  });
79
70
  }
80
71
 
package/lib/state.js CHANGED
@@ -2,9 +2,10 @@ import emptyStack from '@iter-tools/imm-stack';
2
2
  import { WeakStackFrame } from '@bablr/weak-stack';
3
3
  import { getCooked } from '@bablr/agast-helpers/stream';
4
4
  import { match, guardWithPattern } from './utils/pattern.js';
5
- import { facades, actuals } from './facades.js';
5
+ import { facades, actuals } from '../../bablr-vm-strategy-parse/lib/facades.js';
6
6
  import { reifyExpression } from '@bablr/agast-vm-helpers';
7
- import { EmbeddedNode } from '@bablr/agast-helpers/symbols';
7
+ import { EmbeddedNode, GapTag } from '@bablr/agast-helpers/symbols';
8
+ import { NodeFacade } from './node.js';
8
9
 
9
10
  export const StateFacade = class BABLRStateFacade {
10
11
  constructor(state) {
@@ -15,6 +16,10 @@ export const StateFacade = class BABLRStateFacade {
15
16
  return State.from(actuals.get(source));
16
17
  }
17
18
 
19
+ get ctx() {
20
+ return actuals.get(this).context;
21
+ }
22
+
18
23
  get span() {
19
24
  return actuals.get(this).span.name;
20
25
  }
@@ -32,11 +37,11 @@ export const StateFacade = class BABLRStateFacade {
32
37
  }
33
38
 
34
39
  get node() {
35
- return actuals.get(this).node;
40
+ return NodeFacade.wrap(actuals.get(this).node, this.ctx);
36
41
  }
37
42
 
38
43
  get parentNode() {
39
- return actuals.get(this).parentNode;
44
+ return NodeFacade.wrap(actuals.get(this).parentNode, this.ctx);
40
45
  }
41
46
 
42
47
  get source() {
@@ -52,7 +57,7 @@ export const StateFacade = class BABLRStateFacade {
52
57
  }
53
58
 
54
59
  nodeForPath(path) {
55
- return actuals.get(this).nodeForPath(path);
60
+ return NodeFacade.wrap(actuals.get(this).nodeForPath(path), this.ctx);
56
61
  }
57
62
 
58
63
  pathForTag(tag) {
@@ -60,18 +65,25 @@ export const StateFacade = class BABLRStateFacade {
60
65
  }
61
66
 
62
67
  nodeForTag(tag) {
63
- return actuals.get(this).nodeForTag(tag);
68
+ return NodeFacade.wrap(actuals.get(this).nodeForTag(tag), this.ctx);
64
69
  }
65
70
  };
66
71
 
67
72
  export const State = class BABLRState extends WeakStackFrame {
68
- constructor(source, agast, balanced = emptyStack, spans = emptyStack.push({ name: 'Bare' })) {
73
+ constructor(
74
+ source,
75
+ agast,
76
+ context,
77
+ balanced = emptyStack,
78
+ spans = emptyStack.push({ name: 'Bare' }),
79
+ ) {
69
80
  super();
70
81
 
71
82
  if (!source || !agast) throw new Error('invalid args to State');
72
83
 
73
84
  this.source = source;
74
85
  this.agast = agast;
86
+ this.context = context;
75
87
  this.balanced = balanced;
76
88
  this.spans = spans;
77
89
 
@@ -80,8 +92,8 @@ export const State = class BABLRState extends WeakStackFrame {
80
92
  new StateFacade(this);
81
93
  }
82
94
 
83
- static from(source, agast) {
84
- return State.create(source, agast);
95
+ static from(source, agast, context) {
96
+ return State.create(source, agast, context);
85
97
  }
86
98
 
87
99
  get guardedSource() {
@@ -116,7 +128,7 @@ export const State = class BABLRState extends WeakStackFrame {
116
128
  }
117
129
 
118
130
  get isGap() {
119
- return this.tag.type === 'NodeGapTag';
131
+ return this.tag.type === GapTag;
120
132
  }
121
133
 
122
134
  get speculative() {
package/lib/strategy.js CHANGED
@@ -1,4 +1,3 @@
1
- import size from 'iter-tools-es/methods/size';
2
1
  import { Coroutine } from '@bablr/coroutine';
3
2
  import {
4
3
  buildCall,
@@ -8,9 +7,8 @@ import {
8
7
  buildArrayTag,
9
8
  } from '@bablr/agast-helpers/builders';
10
9
  import { StreamGenerator } from '@bablr/agast-helpers/stream';
11
- import { buildTokens } from './utils/token.js';
12
10
  import { formatType } from './utils/format.js';
13
- import { facades } from './facades.js';
11
+ import { facades } from '../../bablr-vm-strategy-parse/lib/facades.js';
14
12
  import { State } from './state.js';
15
13
  import { updateSpans } from './spans.js';
16
14
  import {
@@ -23,11 +21,26 @@ import {
23
21
  NullTag,
24
22
  LiteralTag,
25
23
  } from '@bablr/agast-helpers/symbols';
24
+ import { NodeFacade } from './node.js';
25
+ import { treeFromStreamSync } from '@bablr/agast-helpers/tree';
26
+
27
+ const getSourceLength = (tags) => {
28
+ let i = 0;
29
+ for (const tag of tags) {
30
+ if (tag.type === LiteralTag) {
31
+ i += tag.value.length;
32
+ } else if (tag.type === GapTag) {
33
+ i += 1;
34
+ }
35
+ }
36
+ return i;
37
+ };
26
38
 
27
39
  const { hasOwn } = Object;
28
40
 
29
- export const createBablrStrategy = (rootSource, strategy) => {
30
- return (ctx, agastState) => {
41
+ export const createBablrStrategy = (ctx, rootSource, strategy) => {
42
+ return (agastCtx, agastState) => {
43
+ if (agastCtx !== ctx.agast.facade) throw new Error();
31
44
  return new StreamGenerator(__strategy(ctx, rootSource, agastState, strategy));
32
45
  };
33
46
  };
@@ -35,9 +48,9 @@ export const createBablrStrategy = (rootSource, strategy) => {
35
48
  const resolvedLanguages = new WeakMap();
36
49
 
37
50
  const __strategy = function* bablrStrategy(ctx, rootSource, agastState, strategy) {
38
- let s = State.from(rootSource, agastState);
51
+ let s = State.from(rootSource, agastState, ctx);
39
52
 
40
- let co = new Coroutine(strategy(facades.get(s), ctx));
53
+ let co = new Coroutine(strategy(facades.get(s), facades.get(ctx)));
41
54
 
42
55
  co.advance();
43
56
 
@@ -93,7 +106,18 @@ const __strategy = function* bablrStrategy(ctx, rootSource, agastState, strategy
93
106
  case CloseNodeTag: {
94
107
  const { node } = s;
95
108
 
96
- const endTag = yield instr;
109
+ if (node.flags.escape) {
110
+ const cooked = node.flags.hasGap
111
+ ? null
112
+ : ctx.languages
113
+ .get(node.language)
114
+ .getCooked?.(NodeFacade.wrap(node, ctx, true), s.span.name, facades.get(ctx)) ||
115
+ null;
116
+
117
+ yield buildCall('bindAttribute', 'cooked', cooked);
118
+ }
119
+
120
+ const closeTag = yield instr;
97
121
 
98
122
  if (s.path) {
99
123
  updateSpans(ctx, s, node, 'close');
@@ -107,7 +131,7 @@ const __strategy = function* bablrStrategy(ctx, rootSource, agastState, strategy
107
131
  }
108
132
  }
109
133
 
110
- returnValue = endTag;
134
+ returnValue = closeTag;
111
135
  break;
112
136
  }
113
137
 
@@ -121,7 +145,7 @@ const __strategy = function* bablrStrategy(ctx, rootSource, agastState, strategy
121
145
  }
122
146
 
123
147
  if (result) {
124
- let sourceStep = s.source.advance(size(result));
148
+ let sourceStep = s.source.advance(getSourceLength(result));
125
149
 
126
150
  if (sourceStep instanceof Promise) {
127
151
  sourceStep = yield sourceStep;
@@ -178,19 +202,17 @@ const __strategy = function* bablrStrategy(ctx, rootSource, agastState, strategy
178
202
  result = yield result;
179
203
  }
180
204
 
181
- const tokens = result && ctx.buildRange(buildTokens(result));
182
-
183
- returnValue = tokens || null;
205
+ returnValue = result && NodeFacade.wrap(treeFromStreamSync(result), ctx, true);
184
206
  break;
185
207
  }
186
208
 
187
209
  case 'branch': {
188
210
  const baseState = s;
189
- let { source, agast, balanced, spans, node } = baseState;
211
+ let { source, agast, context, balanced, spans, node } = baseState;
190
212
 
191
213
  agast = yield instr;
192
214
 
193
- s = s.push(source.branch(), agast, balanced, spans);
215
+ s = s.push(source.branch(), agast, context, balanced, spans);
194
216
 
195
217
  if (node) {
196
218
  resolvedLanguages.set(s.node, resolvedLanguages.get(node));
@@ -232,7 +254,7 @@ const __strategy = function* bablrStrategy(ctx, rootSource, agastState, strategy
232
254
 
233
255
  s = s.parent;
234
256
 
235
- if (s.path.depth && rejectedState.path.depth > s.path.depth) {
257
+ if (s.path.depth && rejectedState.path.depth >= s.path.depth) {
236
258
  // const didShift = rejectedState.node.at(sNodeDepth) === s.node;
237
259
  const didShift =
238
260
  s.nodeForPath(s.path) && !s.nodeForPath(rejectedState.path.at(s.path.depth));
@@ -244,7 +266,7 @@ const __strategy = function* bablrStrategy(ctx, rootSource, agastState, strategy
244
266
  );
245
267
  const lowNode = s.node || s.parentNode;
246
268
 
247
- const { name, isArray } = lowPath.reference?.value || {};
269
+ const { name, isArray, hasGap } = lowPath.reference?.value || {};
248
270
 
249
271
  if (
250
272
  !didShift &&
@@ -252,10 +274,13 @@ const __strategy = function* bablrStrategy(ctx, rootSource, agastState, strategy
252
274
  !(s.result.type === ReferenceTag && s.result.value.name === name)
253
275
  ) {
254
276
  if (isArray) {
255
- yield buildCall('advance', buildEmbeddedTag(buildReferenceTag(name, true)));
277
+ yield buildCall('advance', buildEmbeddedTag(buildReferenceTag(name, true, hasGap)));
256
278
  yield buildCall('advance', buildEmbeddedTag(buildArrayTag()));
257
279
  } else {
258
- yield buildCall('advance', buildEmbeddedTag(buildReferenceTag(name)));
280
+ yield buildCall(
281
+ 'advance',
282
+ buildEmbeddedTag(buildReferenceTag(name, isArray, hasGap)),
283
+ );
259
284
  yield buildCall('advance', buildEmbeddedTag(buildNullTag()));
260
285
  }
261
286
  }
@@ -269,6 +294,18 @@ const __strategy = function* bablrStrategy(ctx, rootSource, agastState, strategy
269
294
  break;
270
295
  }
271
296
 
297
+ case 'openSpan': {
298
+ let { arguments: { 0: name } = [] } = instr;
299
+ s.spans = s.spans.push({ guard: null, name, path: s.path, type: 'Instruction' });
300
+ break;
301
+ }
302
+
303
+ case 'closeSpan': {
304
+ if (s.spans.value.type !== 'Instruction') throw new Error();
305
+ s.spans = s.spans.pop();
306
+ break;
307
+ }
308
+
272
309
  case 'write':
273
310
  case 'bindAttribute': {
274
311
  returnValue = yield instr;
@@ -57,7 +57,7 @@ class GuardedIterator {
57
57
  const guardMatch = match(pattern, fork.clone());
58
58
 
59
59
  return maybeWait(guardMatch, (guardMatch) => {
60
- if (guardMatch || this.done) {
60
+ if (guardMatch || fork.done) {
61
61
  this.done = true;
62
62
  return { value: undefined, done: true };
63
63
  } else {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bablr/bablr-vm",
3
3
  "description": "A VM for parsing using BABLR languages",
4
- "version": "0.17.0",
4
+ "version": "0.18.0",
5
5
  "author": "Conrad Buck<conartist6@gmail.com>",
6
6
  "type": "module",
7
7
  "files": [
@@ -12,11 +12,11 @@
12
12
  },
13
13
  "sideEffects": false,
14
14
  "dependencies": {
15
- "@bablr/agast-helpers": "0.4.0",
16
- "@bablr/agast-vm-helpers": "0.4.0",
15
+ "@bablr/agast-helpers": "^0.5.0",
16
+ "@bablr/agast-vm-helpers": "^0.5.0",
17
17
  "@bablr/coroutine": "0.1.0",
18
- "@bablr/helpers": "0.19.0",
19
- "@bablr/regex-vm": "0.8.1",
18
+ "@bablr/helpers": "^0.20.0",
19
+ "@bablr/regex-vm": "^0.9.0",
20
20
  "@bablr/weak-stack": "0.1.0",
21
21
  "@iter-tools/imm-stack": "1.1.0",
22
22
  "iter-tools-es": "^7.5.3"
package/lib/facades.js DELETED
@@ -1,3 +0,0 @@
1
- import { buildFacadeLayer } from '@bablr/agast-vm-helpers/facades';
2
-
3
- export const { facades, actuals } = buildFacadeLayer();