@bablr/bablr-vm 0.13.2 → 0.13.3

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/README.md CHANGED
@@ -1,3 +1,33 @@
1
1
  # @bablr/vm
2
2
 
3
- The BABLR VM!
3
+ [![come chat on Discord](https://img.shields.io/discord/1151914613089251388)](https://discord.gg/NfMNyYN6cX)
4
+
5
+ Welcome to the home of the BABLR VM! The VM is at the core of the BABLR language definition ecosystem: its purpose is to provide a powerful, well-specified, universally-available execution environment for parsers.
6
+
7
+ By doing this BABLR makes it possible to create tools which integrate more deeply: it can create consensus around basic definitions that must be shared between well-integrated tools.
8
+
9
+ ## Usage
10
+
11
+ Unless you are a power user, you should be using either [the BABLR API](https://github.com/bablr-lang/bablr) or [the BABLR CLI](https://github.com/bablr-lang/bablr-cli). If you are a power user (or otherwise have unique reuquirements) looking at how those packages are implemented will be a good place to start.
12
+
13
+ ## Features
14
+
15
+ Parsers defined on the BABLR VM
16
+
17
+ - Execute in a streaming, LR fashion
18
+ - Hold output as necessary for expression building
19
+ - Do just-in-time tokenization
20
+ - Support backtracking to resolve ambiguity
21
+ - Support arbitrary-size textual lookahead
22
+ - Offer high-level tools for defining trivia and precedence
23
+ - Produce results as a CSTML stream or an agAST tree
24
+ - Are backwards compatible with new VMs
25
+ - Are freely extensible
26
+ - Allow incremental reparsing (for responsivene editing)
27
+ - Can be easily debugged
28
+ - Get a comment attachment engine for free
29
+ - Support parsing templates (programs with holes in them)
30
+
31
+ Parsers defined on the BABLR VM do not have error recovery, and this is on purpose. In general error recovery is not science, it's guesswork necessary to keep the semantic model of the program from continually blinking into and out of existence as the user types syntax, passing through invalid syntactic states.
32
+
33
+ BABLR instead aims to make the most obvious solution the most pleasant: help users edit syntax without passing through invalid states
package/lib/index.js CHANGED
@@ -1,4 +1,3 @@
1
- export { evaluate } from './evaluate.js';
1
+ export { createBablrStrategy } from './strategy.js';
2
2
 
3
3
  export { Source } from './source.js';
4
- export { Context } from './context.js';
package/lib/spans.js CHANGED
@@ -5,7 +5,7 @@ export function updateSpans(ctx, s, node, phase) {
5
5
  const { span: innerSpan, balanced, balancedSpan, balancer, openSpan } = attributes || {};
6
6
 
7
7
  if (!flags.intrinsic && (balancer || balanced)) {
8
- throw new Error('not implemented');
8
+ throw new Error('balanced tokens must be instrinsic');
9
9
  }
10
10
 
11
11
  if (flags.intrinsic) {
@@ -47,7 +47,7 @@ export function updateSpans(ctx, s, node, phase) {
47
47
  }
48
48
 
49
49
  if (balanced) {
50
- s.balanced = s.balanced.push(ctx.agast.nodeForTag(s.result));
50
+ s.balanced = s.balanced.push(ctx.nodeForTag(s.result));
51
51
  }
52
52
 
53
53
  if (innerSpan) {
package/lib/state.js CHANGED
@@ -9,8 +9,8 @@ export const StateFacade = class BABLRStateFacade {
9
9
  facades.set(state, this);
10
10
  }
11
11
 
12
- static from(context, source) {
13
- return State.from(actuals.get(context), actuals.get(source));
12
+ static from(source) {
13
+ return State.from(actuals.get(source));
14
14
  }
15
15
 
16
16
  get span() {
@@ -25,10 +25,6 @@ export const StateFacade = class BABLRStateFacade {
25
25
  return actuals.get(this).holding;
26
26
  }
27
27
 
28
- get context() {
29
- return facades.get(actuals.get(this).context);
30
- }
31
-
32
28
  get path() {
33
29
  return actuals.get(this).path;
34
30
  }
@@ -44,25 +40,14 @@ export const StateFacade = class BABLRStateFacade {
44
40
  get depth() {
45
41
  return actuals.get(this).depth;
46
42
  }
47
-
48
- get ctx() {
49
- return this.context;
50
- }
51
43
  };
52
44
 
53
45
  export const State = class BABLRState extends WeakStackFrame {
54
- constructor(
55
- context,
56
- source,
57
- agast,
58
- balanced = emptyStack,
59
- spans = emptyStack.push({ name: 'Bare' }),
60
- ) {
46
+ constructor(source, agast, balanced = emptyStack, spans = emptyStack.push({ name: 'Bare' })) {
61
47
  super();
62
48
 
63
- if (!context || !source || !agast) throw new Error('invalid args to State');
49
+ if (!source || !agast) throw new Error('invalid args to State');
64
50
 
65
- this.context = context;
66
51
  this.source = source;
67
52
  this.agast = agast;
68
53
  this.balanced = balanced;
@@ -71,8 +56,8 @@ export const State = class BABLRState extends WeakStackFrame {
71
56
  new StateFacade(this);
72
57
  }
73
58
 
74
- static from(context, source, agast) {
75
- return State.create(context, source, agast);
59
+ static from(source, agast) {
60
+ return State.create(source, agast);
76
61
  }
77
62
 
78
63
  get guardedSource() {
@@ -82,10 +67,6 @@ export const State = class BABLRState extends WeakStackFrame {
82
67
  return guard ? guardWithPattern(guard, source) : source;
83
68
  }
84
69
 
85
- get ctx() {
86
- return this.context;
87
- }
88
-
89
70
  get span() {
90
71
  return this.spans.value;
91
72
  }
@@ -118,7 +99,7 @@ export const State = class BABLRState extends WeakStackFrame {
118
99
  let { span, spans, source } = this;
119
100
  let { guard } = span;
120
101
 
121
- if (pattern?.flags?.intrinsic) {
102
+ if (pattern?.intrinsicValue) {
122
103
  if (pattern.type === 'OpenNodeTag') {
123
104
  // TODO differntiate better between self-closing tags and matchers
124
105
  pattern = pattern.value;
@@ -1,33 +1,24 @@
1
1
  import { Coroutine } from '@bablr/coroutine';
2
2
  import { buildCall, buildNull, buildReference, reifyExpression } from '@bablr/agast-vm-helpers';
3
3
  import { StreamGenerator } from '@bablr/agast-helpers/stream';
4
- import { resolveLanguage } from '@bablr/helpers/grammar';
5
4
  import { buildTokens } from './utils/token.js';
6
5
  import { formatType } from './utils/format.js';
7
6
  import { facades } from './facades.js';
8
7
  import { State } from './state.js';
9
8
  import { updateSpans } from './spans.js';
10
9
 
11
- const nodeTopType = Symbol.for('@bablr/node');
12
-
13
- export const evaluate = (ctx, rootLanguage, rootSource, strategy) => {
14
- return (agastCtx, agastState) => {
15
- if (ctx.agast !== agastCtx) throw new Error();
16
-
17
- if (rootLanguage !== ctx.languages.get(rootLanguage.canonicalURL)) {
18
- throw new Error();
19
- }
20
-
21
- return new StreamGenerator(__evaluate(ctx, rootLanguage, rootSource, agastState, strategy));
10
+ export const createBablrStrategy = (rootSource, strategy) => {
11
+ return (ctx, agastState) => {
12
+ return new StreamGenerator(__strategy(ctx, rootSource, agastState, strategy));
22
13
  };
23
14
  };
24
15
 
25
16
  const resolvedLanguages = new WeakMap();
26
17
 
27
- const __evaluate = function* bablrStrategy(ctx, rootLanguage, rootSource, agastState, strategy) {
28
- let s = State.from(ctx, rootSource, agastState);
18
+ const __strategy = function* bablrStrategy(ctx, rootSource, agastState, strategy) {
19
+ let s = State.from(rootSource, agastState);
29
20
 
30
- let co = new Coroutine(strategy(facades.get(s), facades.get(ctx)));
21
+ let co = new Coroutine(strategy(facades.get(s), ctx));
31
22
 
32
23
  co.advance();
33
24
 
@@ -69,42 +60,17 @@ const __evaluate = function* bablrStrategy(ctx, rootLanguage, rootSource, agastS
69
60
  }
70
61
 
71
62
  case 'OpenNodeTag': {
72
- const { flags, type, language: tagLanguage, intrinsicValue } = terminal.value;
73
-
74
- const reference = s.result;
75
-
76
- if (
77
- s.path.depth > 1 &&
78
- !flags.trivia &&
79
- !flags.escape &&
80
- reference?.type !== 'Reference' &&
81
- reference?.type !== 'OpenFragmentTag'
82
- ) {
83
- throw new Error('Invalid location for OpenNodeTag');
84
- }
85
-
86
- const oldNode = s.node;
63
+ const { intrinsicValue } = terminal.value;
87
64
 
88
65
  const openTag = yield sourceInstr;
89
66
 
90
- const resolvedLanguage =
91
- oldNode.depth > 1 ? resolvedLanguages.get(oldNode) : rootLanguage;
92
- const nextResolvedLanguage = resolveLanguage(resolvedLanguage, tagLanguage);
93
-
94
- if (!nextResolvedLanguage) {
95
- throw new Error(`Resolve failed { language: ${tagLanguage} }`);
96
- }
97
-
98
- const grammar = ctx.grammars.get(nextResolvedLanguage);
99
- const isNode = grammar.covers.get(nodeTopType).has(type);
100
-
101
67
  let intrinsicResult = intrinsicValue && s.guardedMatch(terminal.value);
102
68
 
103
69
  if (intrinsicResult instanceof Promise) {
104
70
  intrinsicResult = yield intrinsicResult;
105
71
  }
106
72
 
107
- updateSpans(ctx, s, intrinsicValue ? ctx.agast.nodeForTag(openTag) : s.node, 'open');
73
+ updateSpans(ctx, s, intrinsicValue ? ctx.nodeForTag(openTag) : s.node, 'open');
108
74
 
109
75
  if (intrinsicValue) {
110
76
  if (!intrinsicResult) {
@@ -117,13 +83,7 @@ const __evaluate = function* bablrStrategy(ctx, rootLanguage, rootSource, agastS
117
83
  yield sourceStep;
118
84
  }
119
85
 
120
- updateSpans(ctx, s, ctx.agast.nodeForTag(openTag), 'close');
121
- } else {
122
- if (isNode) {
123
- resolvedLanguages.set(s.node, nextResolvedLanguage);
124
- } else {
125
- resolvedLanguages.set(s.node, resolvedLanguage);
126
- }
86
+ updateSpans(ctx, s, ctx.nodeForTag(openTag), 'close');
127
87
  }
128
88
 
129
89
  returnValue = openTag;
@@ -133,8 +93,6 @@ const __evaluate = function* bablrStrategy(ctx, rootLanguage, rootSource, agastS
133
93
  case 'OpenFragmentTag': {
134
94
  const openTag = yield sourceInstr;
135
95
 
136
- resolvedLanguages.set(s.node, rootLanguage);
137
-
138
96
  returnValue = openTag;
139
97
  break;
140
98
  }
@@ -223,7 +181,7 @@ const __evaluate = function* bablrStrategy(ctx, rootLanguage, rootSource, agastS
223
181
  result = yield result;
224
182
  }
225
183
 
226
- returnValue = result && ctx.agast.buildRange(buildTokens(result));
184
+ returnValue = result && ctx.buildRange(buildTokens(result));
227
185
  break;
228
186
  }
229
187
 
@@ -245,11 +203,11 @@ const __evaluate = function* bablrStrategy(ctx, rootLanguage, rootSource, agastS
245
203
 
246
204
  case 'branch': {
247
205
  const baseState = s;
248
- let { context, source, agast, balanced, spans, node } = baseState;
206
+ let { source, agast, balanced, spans, node } = baseState;
249
207
 
250
208
  agast = yield sourceInstr;
251
209
 
252
- s = s.push(context, source.branch(), agast, balanced, spans);
210
+ s = s.push(source.branch(), agast, balanced, spans);
253
211
 
254
212
  resolvedLanguages.set(s.node, resolvedLanguages.get(node));
255
213
 
@@ -323,11 +281,6 @@ const __evaluate = function* bablrStrategy(ctx, rootLanguage, rootSource, agastS
323
281
  break;
324
282
  }
325
283
 
326
- case 'getContext': {
327
- returnValue = facades.get(ctx);
328
- break;
329
- }
330
-
331
284
  default: {
332
285
  throw new Error(`Unexpected call of {type: ${formatType(verb)}}`);
333
286
  }
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.13.2",
4
+ "version": "0.13.3",
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.1.1",
16
- "@bablr/agast-vm-helpers": "0.1.1",
15
+ "@bablr/agast-helpers": "0.1.5",
16
+ "@bablr/agast-vm-helpers": "0.1.3",
17
17
  "@bablr/coroutine": "0.1.0",
18
- "@bablr/helpers": "0.15.1",
19
- "@bablr/regex-vm": "0.5.0",
18
+ "@bablr/helpers": "0.15.4",
19
+ "@bablr/regex-vm": "0.5.1",
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/context.js DELETED
@@ -1,76 +0,0 @@
1
- import { facades, actuals } from './facades.js';
2
-
3
- export const ContextFacade = class BABLRContextFacade {
4
- get languages() {
5
- return actuals.get(this).languages;
6
- }
7
-
8
- get grammars() {
9
- return actuals.get(this).grammars;
10
- }
11
-
12
- get productionEnhancer() {
13
- return actuals.get(this).productionEnhancer;
14
- }
15
-
16
- get agast() {
17
- return actuals.get(this).agast;
18
- }
19
-
20
- getPreviousTerminal(token) {
21
- return actuals.get(this).agast.getPreviousTerminal(token);
22
- }
23
-
24
- ownTerminalsFor(range) {
25
- return actuals.get(this).agast.ownTerminalsFor(range);
26
- }
27
-
28
- allTerminalsFor(range) {
29
- return actuals.get(this).agast.allTerminalsFor(range);
30
- }
31
-
32
- getCooked(range) {
33
- return actuals.get(this).agast.getCooked(range);
34
- }
35
-
36
- reifyExpression(value) {
37
- return actuals.get(this).agast.reifyExpression(value);
38
- }
39
-
40
- getProperty(node, name) {
41
- return actuals.get(this).agast.getProperty(node, name);
42
- }
43
-
44
- sourceTextFor(range) {
45
- return actuals.get(this).agast.sourceTextFor(range);
46
- }
47
-
48
- nodeForTag(tag) {
49
- return actuals.get(this).agast.nodeForTag(tag);
50
- }
51
-
52
- unbox(value) {
53
- return actuals.get(this).agast.unbox(value);
54
- }
55
- };
56
-
57
- export const Context = class BABLRContext {
58
- static from(agastContext, languages, productionEnhancer) {
59
- return new Context(agastContext, languages, productionEnhancer);
60
- }
61
-
62
- constructor(agastContext, languages, productionEnhancer) {
63
- this.agast = agastContext;
64
- this.languages = languages;
65
- this.productionEnhancer = productionEnhancer;
66
-
67
- this.grammars = new WeakMap();
68
- this.facade = new ContextFacade();
69
-
70
- for (const { 1: language } of this.languages) {
71
- this.grammars.set(language, new language.grammar());
72
- }
73
-
74
- facades.set(this, this.facade);
75
- }
76
- };