@bablr/bablr-vm 0.13.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/LICENSE +21 -0
- package/README.md +3 -0
- package/lib/context.js +76 -0
- package/lib/evaluate.js +414 -0
- package/lib/facades.js +3 -0
- package/lib/index.js +4 -0
- package/lib/source.js +256 -0
- package/lib/state.js +140 -0
- package/lib/symbols.js +1 -0
- package/lib/utils/array.js +13 -0
- package/lib/utils/format.js +9 -0
- package/lib/utils/object.js +97 -0
- package/lib/utils/pattern.js +77 -0
- package/lib/utils/token.js +30 -0
- package/package.json +36 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Conrad Buck
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
package/lib/context.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
};
|
package/lib/evaluate.js
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
import { Coroutine } from '@bablr/coroutine';
|
|
2
|
+
import { reifyExpression } from '@bablr/agast-vm-helpers';
|
|
3
|
+
import { StreamGenerator } from '@bablr/agast-helpers/stream';
|
|
4
|
+
import { resolveLanguage } from '@bablr/helpers/grammar';
|
|
5
|
+
import { buildTokens } from './utils/token.js';
|
|
6
|
+
import { formatType } from './utils/format.js';
|
|
7
|
+
import { facades } from './facades.js';
|
|
8
|
+
import { State } from './state.js';
|
|
9
|
+
|
|
10
|
+
const nodeTopType = Symbol.for('@bablr/node');
|
|
11
|
+
|
|
12
|
+
export const evaluate = (ctx, rootLanguage, rootSource, strategy) => {
|
|
13
|
+
return (agastCtx, agastState) => {
|
|
14
|
+
if (ctx.agast !== agastCtx) throw new Error();
|
|
15
|
+
|
|
16
|
+
if (rootLanguage !== ctx.languages.get(rootLanguage.canonicalURL)) {
|
|
17
|
+
throw new Error();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return new StreamGenerator(__evaluate(ctx, rootLanguage, rootSource, agastState, strategy));
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const resolvedLanguages = new WeakMap();
|
|
25
|
+
|
|
26
|
+
function updateSpans(ctx, s, node, phase) {
|
|
27
|
+
switch (phase) {
|
|
28
|
+
case 'open': {
|
|
29
|
+
const { attributes, flags } = node;
|
|
30
|
+
const { span: innerSpan, balanced, balancedSpan, balancer, openSpan } = attributes || {};
|
|
31
|
+
|
|
32
|
+
if (!flags.intrinsic && (balancer || balanced)) {
|
|
33
|
+
throw new Error('not implemented');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (flags.intrinsic) {
|
|
37
|
+
if (s.path) {
|
|
38
|
+
if (balancedSpan) {
|
|
39
|
+
if (!balanced) throw new Error();
|
|
40
|
+
|
|
41
|
+
s.spans = s.spans.push({
|
|
42
|
+
type: 'Lexical',
|
|
43
|
+
name: balancedSpan,
|
|
44
|
+
path: s.path,
|
|
45
|
+
guard: balanced,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (innerSpan) {
|
|
50
|
+
throw new Error();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (openSpan) {
|
|
56
|
+
s.spans = s.spans.push({
|
|
57
|
+
type: 'Explicit',
|
|
58
|
+
name: openSpan,
|
|
59
|
+
path: s.path,
|
|
60
|
+
guard: null,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (balancer) {
|
|
65
|
+
const balancedNode = s.balanced.value;
|
|
66
|
+
|
|
67
|
+
if (!s.balanced.size) throw new Error();
|
|
68
|
+
|
|
69
|
+
s.balanced = s.balanced.pop();
|
|
70
|
+
|
|
71
|
+
if (balancer && balancedNode.openTag.value.attributes.balancedSpan) {
|
|
72
|
+
s.spans = s.spans.pop();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (balanced) {
|
|
77
|
+
s.balanced = s.balanced.push(ctx.agast.nodeForTag(s.result));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (innerSpan) {
|
|
81
|
+
s.spans = s.spans.push({
|
|
82
|
+
type: 'Inner',
|
|
83
|
+
name: innerSpan,
|
|
84
|
+
path: s.path,
|
|
85
|
+
guard: null,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
case 'close': {
|
|
93
|
+
const { openTag, flags } = node;
|
|
94
|
+
const { attributes } = openTag.value;
|
|
95
|
+
const { balancedSpan, span: innerSpan, closeSpan, balanced } = attributes || {};
|
|
96
|
+
|
|
97
|
+
if (balancedSpan && !flags.intrinsic) {
|
|
98
|
+
if (!balanced) throw new Error();
|
|
99
|
+
|
|
100
|
+
s.spans = s.spans.push({
|
|
101
|
+
type: 'Lexical',
|
|
102
|
+
name: balancedSpan,
|
|
103
|
+
path: s.path,
|
|
104
|
+
guard: balanced,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (closeSpan) {
|
|
109
|
+
if (s.spans.value.type !== 'Explicit') throw new Error();
|
|
110
|
+
s.spans = s.spans.pop();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (innerSpan) {
|
|
114
|
+
s.spans = s.spans.pop();
|
|
115
|
+
}
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
default:
|
|
119
|
+
throw new Error();
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const __evaluate = function* bablrStrategy(ctx, rootLanguage, rootSource, agastState, strategy) {
|
|
124
|
+
let s = State.from(ctx, rootSource, agastState);
|
|
125
|
+
|
|
126
|
+
let co = new Coroutine(strategy(facades.get(s), facades.get(ctx)));
|
|
127
|
+
|
|
128
|
+
co.advance();
|
|
129
|
+
|
|
130
|
+
{
|
|
131
|
+
s.source.advance();
|
|
132
|
+
|
|
133
|
+
const sourceStep = s.source.fork.head.step;
|
|
134
|
+
|
|
135
|
+
if (sourceStep instanceof Promise) {
|
|
136
|
+
yield sourceStep;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
for (;;) {
|
|
141
|
+
if (co.current instanceof Promise) {
|
|
142
|
+
co.current = yield co.current;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (co.done) break;
|
|
146
|
+
|
|
147
|
+
const sourceInstr = co.value;
|
|
148
|
+
const instr = reifyExpression(sourceInstr);
|
|
149
|
+
let returnValue = undefined;
|
|
150
|
+
|
|
151
|
+
const { verb } = instr;
|
|
152
|
+
|
|
153
|
+
switch (verb) {
|
|
154
|
+
case 'advance': {
|
|
155
|
+
const {
|
|
156
|
+
arguments: { 0: terminal = [] },
|
|
157
|
+
} = instr;
|
|
158
|
+
|
|
159
|
+
switch (terminal?.type || 'Null') {
|
|
160
|
+
case 'DoctypeTag': {
|
|
161
|
+
const doctypeTag = yield sourceInstr;
|
|
162
|
+
|
|
163
|
+
returnValue = doctypeTag;
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
case 'OpenNodeTag': {
|
|
168
|
+
const { flags, type, language: tagLanguage, intrinsicValue } = terminal.value;
|
|
169
|
+
|
|
170
|
+
const reference = s.result;
|
|
171
|
+
|
|
172
|
+
if (
|
|
173
|
+
s.path.depth > 1 &&
|
|
174
|
+
!flags.trivia &&
|
|
175
|
+
!flags.escape &&
|
|
176
|
+
reference?.type !== 'Reference' &&
|
|
177
|
+
reference?.type !== 'OpenFragmentTag'
|
|
178
|
+
) {
|
|
179
|
+
throw new Error('Invalid location for OpenNodeTag');
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const oldNode = s.node;
|
|
183
|
+
|
|
184
|
+
const openTag = yield sourceInstr;
|
|
185
|
+
|
|
186
|
+
const resolvedLanguage =
|
|
187
|
+
oldNode.depth > 1 ? resolvedLanguages.get(oldNode) : rootLanguage;
|
|
188
|
+
const nextResolvedLanguage = resolveLanguage(resolvedLanguage, tagLanguage);
|
|
189
|
+
|
|
190
|
+
if (!nextResolvedLanguage) {
|
|
191
|
+
throw new Error(`Resolve failed { language: ${tagLanguage} }`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const grammar = ctx.grammars.get(nextResolvedLanguage);
|
|
195
|
+
const isNode = grammar.covers.get(nodeTopType).has(type);
|
|
196
|
+
|
|
197
|
+
let intrinsicResult = intrinsicValue && s.guardedMatch(terminal.value);
|
|
198
|
+
|
|
199
|
+
if (intrinsicResult instanceof Promise) {
|
|
200
|
+
intrinsicResult = yield intrinsicResult;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
updateSpans(ctx, s, intrinsicValue ? ctx.agast.nodeForTag(openTag) : s.node, 'open');
|
|
204
|
+
|
|
205
|
+
if (intrinsicValue) {
|
|
206
|
+
if (!intrinsicResult) {
|
|
207
|
+
throw new Error('advance failed to match an intrinsic node');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
const sourceStep = s.source.advance(intrinsicResult.length);
|
|
211
|
+
|
|
212
|
+
if (sourceStep instanceof Promise) {
|
|
213
|
+
yield sourceStep;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
updateSpans(ctx, s, ctx.agast.nodeForTag(openTag), 'close');
|
|
217
|
+
} else {
|
|
218
|
+
if (isNode) {
|
|
219
|
+
resolvedLanguages.set(s.node, nextResolvedLanguage);
|
|
220
|
+
} else {
|
|
221
|
+
resolvedLanguages.set(s.node, resolvedLanguage);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
returnValue = openTag;
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
case 'OpenFragmentTag': {
|
|
230
|
+
const openTag = yield sourceInstr;
|
|
231
|
+
|
|
232
|
+
resolvedLanguages.set(s.node, rootLanguage);
|
|
233
|
+
|
|
234
|
+
returnValue = openTag;
|
|
235
|
+
break;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
case 'CloseNodeTag': {
|
|
239
|
+
const { node } = s;
|
|
240
|
+
|
|
241
|
+
const endTag = yield sourceInstr;
|
|
242
|
+
|
|
243
|
+
if (s.path) {
|
|
244
|
+
updateSpans(ctx, s, node, 'close');
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
returnValue = endTag;
|
|
248
|
+
break;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
case 'CloseFragmentTag': {
|
|
252
|
+
returnValue = yield sourceInstr;
|
|
253
|
+
|
|
254
|
+
if (!s.path) {
|
|
255
|
+
if (!s.source.done) {
|
|
256
|
+
throw new Error('Parser failed to consume input');
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (s.balanced.size) {
|
|
260
|
+
throw new Error('Parser did not match all balanced nodes');
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
case 'Literal': {
|
|
267
|
+
const { value: pattern } = terminal;
|
|
268
|
+
|
|
269
|
+
let result = s.guardedMatch(pattern);
|
|
270
|
+
|
|
271
|
+
if (result instanceof Promise) {
|
|
272
|
+
result = yield result;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (result) {
|
|
276
|
+
let sourceStep = s.source.advance(result.length);
|
|
277
|
+
|
|
278
|
+
if (sourceStep instanceof Promise) {
|
|
279
|
+
sourceStep = yield sourceStep;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
returnValue = yield sourceInstr;
|
|
283
|
+
} else {
|
|
284
|
+
throw new Error('Failed to advance literal');
|
|
285
|
+
}
|
|
286
|
+
break;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
case 'Gap': {
|
|
290
|
+
if (s.source.value == null && !s.source.done) {
|
|
291
|
+
const sourceStep = s.source.advance(1);
|
|
292
|
+
|
|
293
|
+
if (sourceStep instanceof Promise) {
|
|
294
|
+
yield sourceStep;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
returnValue = yield sourceInstr;
|
|
298
|
+
} else {
|
|
299
|
+
throw new Error('Failed to advance gap');
|
|
300
|
+
}
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
default: {
|
|
305
|
+
returnValue = yield sourceInstr;
|
|
306
|
+
break;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
case 'match': {
|
|
314
|
+
let { arguments: { 0: pattern } = [] } = instr;
|
|
315
|
+
|
|
316
|
+
let result = s.guardedMatch(pattern);
|
|
317
|
+
|
|
318
|
+
if (result instanceof Promise) {
|
|
319
|
+
result = yield result;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
returnValue = result && ctx.agast.buildRange(buildTokens(result));
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
case 'shift': {
|
|
327
|
+
s.source.shift();
|
|
328
|
+
|
|
329
|
+
yield sourceInstr;
|
|
330
|
+
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
case 'unshift': {
|
|
335
|
+
s.source.unshift();
|
|
336
|
+
|
|
337
|
+
yield sourceInstr;
|
|
338
|
+
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
case 'branch': {
|
|
343
|
+
const baseState = s;
|
|
344
|
+
let { context, source, agast, balanced, spans, node } = baseState;
|
|
345
|
+
|
|
346
|
+
agast = yield sourceInstr;
|
|
347
|
+
|
|
348
|
+
s = s.push(context, source.branch(), agast, balanced, spans);
|
|
349
|
+
|
|
350
|
+
resolvedLanguages.set(s.node, resolvedLanguages.get(node));
|
|
351
|
+
|
|
352
|
+
returnValue = facades.get(s);
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
case 'accept': {
|
|
357
|
+
const accepted = s;
|
|
358
|
+
|
|
359
|
+
const agastState = yield sourceInstr;
|
|
360
|
+
|
|
361
|
+
s = s.parent;
|
|
362
|
+
|
|
363
|
+
if (!s) {
|
|
364
|
+
throw new Error('accepted the root state');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
s.spans = accepted.spans;
|
|
368
|
+
s.balanced = accepted.balanced;
|
|
369
|
+
|
|
370
|
+
s.source.accept(accepted.source);
|
|
371
|
+
s.agast = agastState;
|
|
372
|
+
|
|
373
|
+
returnValue = facades.get(s);
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
case 'reject': {
|
|
378
|
+
const rejectedState = s;
|
|
379
|
+
|
|
380
|
+
yield sourceInstr;
|
|
381
|
+
|
|
382
|
+
s = s.parent;
|
|
383
|
+
|
|
384
|
+
if (!s) throw new Error('rejected root state');
|
|
385
|
+
|
|
386
|
+
rejectedState.source.reject();
|
|
387
|
+
|
|
388
|
+
returnValue = facades.get(s);
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
case 'bindAttribute': {
|
|
393
|
+
returnValue = yield sourceInstr;
|
|
394
|
+
break;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
case 'getState': {
|
|
398
|
+
returnValue = facades.get(s);
|
|
399
|
+
break;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
case 'getContext': {
|
|
403
|
+
returnValue = facades.get(ctx);
|
|
404
|
+
break;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
default: {
|
|
408
|
+
throw new Error(`Unexpected call of {type: ${formatType(verb)}}`);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
co.advance(returnValue);
|
|
413
|
+
}
|
|
414
|
+
};
|
package/lib/facades.js
ADDED
package/lib/index.js
ADDED
package/lib/source.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { WeakStackFrame } from '@bablr/weak-stack';
|
|
2
|
+
import { maybeWait, getStreamIterator } from '@bablr/agast-helpers/stream';
|
|
3
|
+
import { facades, actuals } from './facades.js';
|
|
4
|
+
|
|
5
|
+
// Queue item instances are shared between all forks.
|
|
6
|
+
class QueueItem {
|
|
7
|
+
constructor(step) {
|
|
8
|
+
this.step = step;
|
|
9
|
+
this.next = null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class Exchange {
|
|
14
|
+
constructor(iterator) {
|
|
15
|
+
if (!iterator) throw new Error();
|
|
16
|
+
|
|
17
|
+
this.iterator = iterator;
|
|
18
|
+
this.tail = new QueueItem(null);
|
|
19
|
+
this.head = this.tail;
|
|
20
|
+
this.forks = 0;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static from(iterable) {
|
|
24
|
+
return new Exchange(getStreamIterator(iterable));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
get isWaiting() {
|
|
28
|
+
return this.head.step instanceof Promise;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
allocateFork(fork) {
|
|
32
|
+
const { head = this.tail, exchange = this, current } = fork || {};
|
|
33
|
+
++this.forks;
|
|
34
|
+
return new Fork(head, exchange, current);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
advance() {
|
|
38
|
+
this.tail = this.tail.next;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
fetch() {
|
|
42
|
+
let step = this.iterator.next();
|
|
43
|
+
|
|
44
|
+
if (step instanceof Promise) {
|
|
45
|
+
step = step.then((step) => {
|
|
46
|
+
newItem.step = step;
|
|
47
|
+
return step;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let newItem = new QueueItem(step);
|
|
52
|
+
|
|
53
|
+
this.head.next = newItem;
|
|
54
|
+
this.head = this.head.next;
|
|
55
|
+
|
|
56
|
+
return newItem;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
releaseFork(fork) {
|
|
60
|
+
--this.forks;
|
|
61
|
+
if (this.forks === 0) {
|
|
62
|
+
this.iterator.return?.();
|
|
63
|
+
}
|
|
64
|
+
return { value: undefined, done: true };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
class ForkIterator {
|
|
69
|
+
constructor(fork) {
|
|
70
|
+
facades.set(fork.clone(), this);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
next() {
|
|
74
|
+
const fork = actuals.get(this);
|
|
75
|
+
if (!fork.done) {
|
|
76
|
+
const { head } = fork;
|
|
77
|
+
fork.advance();
|
|
78
|
+
return maybeWait(fork.head.step, () => head.step);
|
|
79
|
+
} else {
|
|
80
|
+
return { value: undefined, done: true };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return() {
|
|
85
|
+
actuals.get(this).return();
|
|
86
|
+
return { value: undefined, done: true };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
[Symbol.for('@@streamIterator')]() {
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
class Fork {
|
|
95
|
+
constructor(head, exchange, done = false) {
|
|
96
|
+
this.head = head; // QueueItem
|
|
97
|
+
this.exchange = exchange;
|
|
98
|
+
this._done = done;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
get done() {
|
|
102
|
+
return this._done || this.head.step?.done;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
get value() {
|
|
106
|
+
return this.done ? { value: undefined, done: true } : this.head.step?.value;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
advance() {
|
|
110
|
+
const { exchange } = this;
|
|
111
|
+
|
|
112
|
+
if (this.done) {
|
|
113
|
+
throw new Error('cannot advance a fork that is done');
|
|
114
|
+
} else {
|
|
115
|
+
let { head } = this;
|
|
116
|
+
|
|
117
|
+
let nextItem = this.head.next;
|
|
118
|
+
|
|
119
|
+
if (!head.step?.done) {
|
|
120
|
+
if (!nextItem) {
|
|
121
|
+
nextItem = exchange.fetch();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
this.head = nextItem;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return nextItem;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return() {
|
|
132
|
+
const { done, exchange } = this;
|
|
133
|
+
|
|
134
|
+
if (!done) exchange.releaseFork(this);
|
|
135
|
+
|
|
136
|
+
const step = { value: undefined, done: true };
|
|
137
|
+
|
|
138
|
+
this._current = step;
|
|
139
|
+
|
|
140
|
+
return step;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
clone() {
|
|
144
|
+
const { exchange } = this;
|
|
145
|
+
|
|
146
|
+
return exchange.allocateFork(this);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
[Symbol.for('@@streamIterator')]() {
|
|
150
|
+
return new ForkIterator(this);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export const SourceFacade = class BABLRSourceFacade {
|
|
155
|
+
static from(iterable) {
|
|
156
|
+
return facades.get(Source.from(iterable));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
constructor(source) {
|
|
160
|
+
facades.set(source, this);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
[Symbol.for('@@streamIterator')]() {
|
|
164
|
+
return actuals.get(this)[Symbol.for('@@streamIterator')]();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
get done() {
|
|
168
|
+
return actuals.get(this).done;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
get index() {
|
|
172
|
+
return actuals.get(this).index;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
get atGap() {
|
|
176
|
+
return actuals.get(this).atGap;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export const Source = class BABLRSource extends WeakStackFrame {
|
|
181
|
+
static from(iterable) {
|
|
182
|
+
const exchange = Exchange.from(iterable);
|
|
183
|
+
return Source.create(exchange.allocateFork(), exchange);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
constructor(fork, exchange, index = -1, holding = false) {
|
|
187
|
+
super();
|
|
188
|
+
|
|
189
|
+
if (!fork || !exchange) throw new Error();
|
|
190
|
+
|
|
191
|
+
this.fork = fork;
|
|
192
|
+
this.exchange = exchange;
|
|
193
|
+
this.index = index;
|
|
194
|
+
this.holding = holding;
|
|
195
|
+
|
|
196
|
+
new SourceFacade(this);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
get value() {
|
|
200
|
+
return this.fork.value;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
get done() {
|
|
204
|
+
return this.fork.done;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
get atGap() {
|
|
208
|
+
return this.holding || (!this.done && this.value == null);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
advance(n = 1) {
|
|
212
|
+
return new Array(n).fill(null).reduce((acc) => {
|
|
213
|
+
return maybeWait(acc, () => {
|
|
214
|
+
this.fork.advance();
|
|
215
|
+
this.index++;
|
|
216
|
+
return this.fork.step;
|
|
217
|
+
});
|
|
218
|
+
}, this.fork.step);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
shift() {
|
|
222
|
+
this.holding = true;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
unshift() {
|
|
226
|
+
this.holding = false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
branch() {
|
|
230
|
+
const { fork, exchange, index, holding } = this;
|
|
231
|
+
return this.push(exchange.allocateFork(fork), exchange, index, holding);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
release() {
|
|
235
|
+
this.fork.return();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
accept(source) {
|
|
239
|
+
this.release();
|
|
240
|
+
this.fork = source.fork;
|
|
241
|
+
this.index = source.index;
|
|
242
|
+
this.holding = source.holding;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
reject() {
|
|
246
|
+
this.release();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
[Symbol.for('@@streamIterator')]() {
|
|
250
|
+
return this.fork.clone()[Symbol.for('@@streamIterator')]();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
formatIndex() {
|
|
254
|
+
return `source[${this.source.index}]`;
|
|
255
|
+
}
|
|
256
|
+
};
|
package/lib/state.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import emptyStack from '@iter-tools/imm-stack';
|
|
2
|
+
import { WeakStackFrame } from '@bablr/weak-stack';
|
|
3
|
+
import { getCooked } from '@bablr/agast-helpers/stream';
|
|
4
|
+
import { match, guardWithPattern } from './utils/pattern.js';
|
|
5
|
+
import { facades, actuals } from './facades.js';
|
|
6
|
+
|
|
7
|
+
export const StateFacade = class BABLRStateFacade {
|
|
8
|
+
constructor(state) {
|
|
9
|
+
facades.set(state, this);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
static from(context, source) {
|
|
13
|
+
return State.from(actuals.get(context), actuals.get(source));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
get span() {
|
|
17
|
+
return actuals.get(this).span.name;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get result() {
|
|
21
|
+
return actuals.get(this).result;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get holding() {
|
|
25
|
+
return actuals.get(this).holding;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get context() {
|
|
29
|
+
return facades.get(actuals.get(this).context);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
get path() {
|
|
33
|
+
return actuals.get(this).path;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
get node() {
|
|
37
|
+
return actuals.get(this).node;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
get source() {
|
|
41
|
+
return facades.get(actuals.get(this).source);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get depth() {
|
|
45
|
+
return actuals.get(this).depth;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
get ctx() {
|
|
49
|
+
return this.context;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
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
|
+
) {
|
|
61
|
+
super();
|
|
62
|
+
|
|
63
|
+
if (!context || !source || !agast) throw new Error('invalid args to State');
|
|
64
|
+
|
|
65
|
+
this.context = context;
|
|
66
|
+
this.source = source;
|
|
67
|
+
this.agast = agast;
|
|
68
|
+
this.balanced = balanced;
|
|
69
|
+
this.spans = spans;
|
|
70
|
+
|
|
71
|
+
new StateFacade(this);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static from(context, source, agast) {
|
|
75
|
+
return State.create(context, source, agast);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get guardedSource() {
|
|
79
|
+
const { source, span } = this;
|
|
80
|
+
const { guard } = span;
|
|
81
|
+
|
|
82
|
+
return guard ? guardWithPattern(guard, source) : source;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get ctx() {
|
|
86
|
+
return this.context;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
get span() {
|
|
90
|
+
return this.spans.value;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
get path() {
|
|
94
|
+
return this.agast.path;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
get node() {
|
|
98
|
+
return this.agast.node;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
get holding() {
|
|
102
|
+
return this.agast.holding;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
get result() {
|
|
106
|
+
return this.agast.result;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
get isGap() {
|
|
110
|
+
return this.tag.type === 'NodeGapTag';
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
get speculative() {
|
|
114
|
+
return !!this.parent;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
guardedMatch(pattern) {
|
|
118
|
+
let { span, spans, source } = this;
|
|
119
|
+
let { guard } = span;
|
|
120
|
+
|
|
121
|
+
if (pattern?.flags?.intrinsic) {
|
|
122
|
+
if (pattern.type === 'OpenNodeTag') {
|
|
123
|
+
// TODO differntiate better between self-closing tags and matchers
|
|
124
|
+
pattern = pattern.value;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
({ guard } = span);
|
|
128
|
+
|
|
129
|
+
if (span.type === 'Lexical' && pattern.attributes.balancer) {
|
|
130
|
+
// also check that the open node starts a lexical span?
|
|
131
|
+
span = spans.prev.value;
|
|
132
|
+
({ guard } = span);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
pattern = pattern.intrinsicValue || getCooked(pattern.children);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return match(pattern, guard ? guardWithPattern(guard, source) : source);
|
|
139
|
+
}
|
|
140
|
+
};
|
package/lib/symbols.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const node = Symbol.for('@bablr/node');
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export const isArray = Array;
|
|
2
|
+
|
|
3
|
+
export const notEmpty = (arr) => arr != null && arr.length > 0;
|
|
4
|
+
|
|
5
|
+
export const nullOr = (arr) => (arr.length === 0 ? null : arr);
|
|
6
|
+
|
|
7
|
+
export function* arraySlice(arr, start, end) {
|
|
8
|
+
const increment = end > start ? 1 : -1;
|
|
9
|
+
|
|
10
|
+
for (let i = start; i < end; i += increment) {
|
|
11
|
+
yield arr[i];
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const formatType = (type) => {
|
|
2
|
+
return typeof type === 'symbol' ? `${type.description.replace(/^@bablr\//y, '')}` : `'${type}'`;
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
export const formatGraveString = (str) => {
|
|
6
|
+
return `\`${str
|
|
7
|
+
.replace(/`/g, '\\`')
|
|
8
|
+
.replace(/[\r\n\u{00}\u{08}\u{0B}\u{0C}\u{0E}-\u{1F}]/gu, '')}\``;
|
|
9
|
+
};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
export const { hasOwn, freeze, isFrozen, seal, isSealed, getOwnPropertySymbols } = Object;
|
|
2
|
+
export const { isArray } = Array;
|
|
3
|
+
|
|
4
|
+
const intFrom = (str) => {
|
|
5
|
+
const value = parseInt(str, 10);
|
|
6
|
+
return isNaN(value) ? null : value;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const has = (obj, property) => {
|
|
10
|
+
let value = obj;
|
|
11
|
+
for (const part of property.split('.')) {
|
|
12
|
+
if (!hasOwn(value, part)) return false;
|
|
13
|
+
value = value[part];
|
|
14
|
+
}
|
|
15
|
+
return true;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export const get = (obj, property) => {
|
|
19
|
+
let value = obj;
|
|
20
|
+
for (const part of property.split('.')) {
|
|
21
|
+
value = value[part];
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const set = (obj, property, value) => {
|
|
27
|
+
const parts = property.split('.');
|
|
28
|
+
let obj_ = obj;
|
|
29
|
+
|
|
30
|
+
let lastKey;
|
|
31
|
+
for (let i = 0; i < parts.length; i++) {
|
|
32
|
+
const intKey = intFrom(parts[i]);
|
|
33
|
+
const key = intKey !== null ? intKey : parts[i];
|
|
34
|
+
let value = obj_[key];
|
|
35
|
+
|
|
36
|
+
if (parts.length - 1 === i) {
|
|
37
|
+
lastKey = key;
|
|
38
|
+
} else if (value !== undefined) {
|
|
39
|
+
obj_ = value;
|
|
40
|
+
} else if (intFrom(parts[i + 1]) !== null) {
|
|
41
|
+
obj_ = value = obj_[key] = [];
|
|
42
|
+
} else {
|
|
43
|
+
throw new Error(`Unable to set {property: '${property}'} in obj`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
obj_[lastKey] = value;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export function objectKeys(obj) {
|
|
51
|
+
return {
|
|
52
|
+
*[Symbol.iterator]() {
|
|
53
|
+
for (let key in obj) if (hasOwn(obj, key)) yield key;
|
|
54
|
+
yield* getOwnPropertySymbols(obj);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function objectValues(obj) {
|
|
60
|
+
return {
|
|
61
|
+
*[Symbol.iterator]() {
|
|
62
|
+
for (let key in obj) if (hasOwn(obj, key)) yield obj[key];
|
|
63
|
+
yield* getOwnPropertySymbols(obj).map((sym) => obj[sym]);
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function objectEntries(obj) {
|
|
69
|
+
return {
|
|
70
|
+
*[Symbol.iterator]() {
|
|
71
|
+
for (let key in obj) if (hasOwn(obj, key)) yield [key, obj[key]];
|
|
72
|
+
yield* getOwnPropertySymbols(obj).map((sym) => [sym, obj[sym]]);
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export const isObject = (obj) => obj !== null && typeof obj === 'object';
|
|
78
|
+
export const isFunction = (obj) => typeof obj === 'function';
|
|
79
|
+
export const isSymbol = (obj) => typeof obj === 'symbol';
|
|
80
|
+
export const isString = (obj) => typeof obj === 'string';
|
|
81
|
+
export const isType = (obj) => isSymbol(obj) || isString(obj);
|
|
82
|
+
export const isRegex = (obj) => obj instanceof RegExp;
|
|
83
|
+
export const isPattern = (obj) => isString(obj) || isRegex(obj);
|
|
84
|
+
|
|
85
|
+
export const memoize = (fn) => {
|
|
86
|
+
const cache = new WeakMap();
|
|
87
|
+
return (obj) => {
|
|
88
|
+
let result;
|
|
89
|
+
if (cache.has(obj)) {
|
|
90
|
+
result = cache.get(obj);
|
|
91
|
+
} else {
|
|
92
|
+
result = fn(obj);
|
|
93
|
+
cache.set(obj, result);
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
};
|
|
97
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import isString from 'iter-tools-es/methods/is-string';
|
|
2
|
+
import { generateMatches } from '@bablr/regex-vm';
|
|
3
|
+
import { getStreamIterator, maybeWait } from '@bablr/agast-helpers/stream';
|
|
4
|
+
import * as t from '@bablr/agast-helpers/shorthand';
|
|
5
|
+
import * as l from '@bablr/agast-vm-helpers/languages';
|
|
6
|
+
|
|
7
|
+
export const assertValidRegex = (expr) => {
|
|
8
|
+
const { flags } = expr;
|
|
9
|
+
|
|
10
|
+
if (!expr.language === 'Spamex' && expr.type === 'Regex') {
|
|
11
|
+
throw new Error();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// TODO validate the rest of it
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const buildStringRegex = (str) => {
|
|
18
|
+
return t.node(l.Regex, 'Pattern', [t.ref`open`, t.ref`alternatives[]`, t.ref`close`], {
|
|
19
|
+
open: t.s_node(l.Regex, 'Punctuator', '/'),
|
|
20
|
+
alternatives: [
|
|
21
|
+
t.node(l.Regex, 'Alternative', [t.ref`elements[]`], {
|
|
22
|
+
elements: [...str].map((chr) => t.s_node(l.Regex, 'Character', chr)),
|
|
23
|
+
}),
|
|
24
|
+
],
|
|
25
|
+
close: t.s_node(l.Regex, 'Punctuator', '/'),
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const match = (pattern, source) => {
|
|
30
|
+
const pattern_ = isString(pattern) ? buildStringRegex(pattern) : pattern;
|
|
31
|
+
|
|
32
|
+
if (pattern_.type !== 'Pattern') throw new Error();
|
|
33
|
+
|
|
34
|
+
assertValidRegex(pattern_);
|
|
35
|
+
|
|
36
|
+
const iter = getStreamIterator(generateMatches(pattern_, source));
|
|
37
|
+
|
|
38
|
+
const step = iter.next();
|
|
39
|
+
|
|
40
|
+
return maybeWait(step, (step) => (step.done ? null : step.value[0]));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
class GuardedIterator {
|
|
44
|
+
constructor(pattern, source) {
|
|
45
|
+
this.pattern = pattern;
|
|
46
|
+
this.fork = source.fork.clone();
|
|
47
|
+
this.done = false;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
next() {
|
|
51
|
+
const { pattern, fork } = this;
|
|
52
|
+
|
|
53
|
+
const guardMatch = match(pattern, fork.clone());
|
|
54
|
+
|
|
55
|
+
return maybeWait(guardMatch, (guardMatch) => {
|
|
56
|
+
if (guardMatch || this.done) {
|
|
57
|
+
this.done = true;
|
|
58
|
+
return { value: undefined, done: true };
|
|
59
|
+
} else {
|
|
60
|
+
const { value } = fork;
|
|
61
|
+
return maybeWait(fork.advance(), (_) => ({ value, done: false }));
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return() {
|
|
67
|
+
this.fork.return();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
[Symbol.for('@@streamIterator')]() {
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const guardWithPattern = (pattern, source) => {
|
|
76
|
+
return new GuardedIterator(pattern, source.branch());
|
|
77
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { buildLiteral, buildGap } from '@bablr/agast-helpers/builders';
|
|
2
|
+
|
|
3
|
+
export const isNewlineToken = (token) => /^\r|\r\n|\n$/.test(token.value);
|
|
4
|
+
|
|
5
|
+
export function* ownChildrenFor(range) {
|
|
6
|
+
throw new Error('unimplemented');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function* allChildrenFor(range) {
|
|
10
|
+
throw new Error('unimplemented');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function* buildTokens(array) {
|
|
14
|
+
let str = '';
|
|
15
|
+
for (const chr of array) {
|
|
16
|
+
if (chr == null) {
|
|
17
|
+
if (str) {
|
|
18
|
+
yield buildLiteral(str);
|
|
19
|
+
str = '';
|
|
20
|
+
}
|
|
21
|
+
yield buildGap();
|
|
22
|
+
} else {
|
|
23
|
+
str += chr;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (str) {
|
|
28
|
+
yield buildLiteral(str);
|
|
29
|
+
}
|
|
30
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bablr/bablr-vm",
|
|
3
|
+
"description": "A VM for parsing using BABLR languages",
|
|
4
|
+
"version": "0.13.0",
|
|
5
|
+
"author": "Conrad Buck<conartist6@gmail.com>",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"lib"
|
|
9
|
+
],
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./lib/index.js"
|
|
12
|
+
},
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@bablr/agast-helpers": "0.1.0",
|
|
16
|
+
"@bablr/agast-vm-helpers": "0.1.0",
|
|
17
|
+
"@bablr/coroutine": "0.1.0",
|
|
18
|
+
"@bablr/helpers": "0.15.0",
|
|
19
|
+
"@bablr/regex-vm": "0.5.0",
|
|
20
|
+
"@bablr/weak-stack": "0.1.0",
|
|
21
|
+
"@iter-tools/imm-stack": "1.1.0",
|
|
22
|
+
"iter-tools-es": "^7.5.3"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@bablr/eslint-config-base": "github:bablr-lang/eslint-config-base#d834ccc52795d6c3b96ecc6c419960fceed221a6",
|
|
26
|
+
"enhanced-resolve": "^5.12.0",
|
|
27
|
+
"eslint": "^8.32.0",
|
|
28
|
+
"eslint-import-resolver-enhanced-resolve": "^1.0.5",
|
|
29
|
+
"eslint-plugin-import": "^2.27.5",
|
|
30
|
+
"iter-tools-es": "^7.3.1",
|
|
31
|
+
"prettier": "^2.6.2"
|
|
32
|
+
},
|
|
33
|
+
"repository": "github:bablr-lang/bablr-vm",
|
|
34
|
+
"homepage": "https://github.com/bablr-lang/bablr-vm",
|
|
35
|
+
"license": "MIT"
|
|
36
|
+
}
|