@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 +31 -1
- package/lib/index.js +1 -2
- package/lib/spans.js +2 -2
- package/lib/state.js +7 -26
- package/lib/{evaluate.js → strategy.js} +12 -59
- package/package.json +5 -5
- package/lib/context.js +0 -76
package/README.md
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
1
|
# @bablr/vm
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](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
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('
|
|
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.
|
|
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(
|
|
13
|
-
return State.from(actuals.get(
|
|
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 (!
|
|
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(
|
|
75
|
-
return State.create(
|
|
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?.
|
|
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
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
28
|
-
let s = State.from(
|
|
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),
|
|
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 {
|
|
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.
|
|
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.
|
|
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.
|
|
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 {
|
|
206
|
+
let { source, agast, balanced, spans, node } = baseState;
|
|
249
207
|
|
|
250
208
|
agast = yield sourceInstr;
|
|
251
209
|
|
|
252
|
-
s = s.push(
|
|
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.
|
|
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.
|
|
16
|
-
"@bablr/agast-vm-helpers": "0.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.
|
|
19
|
-
"@bablr/regex-vm": "0.5.
|
|
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
|
-
};
|