grammatik 0.1.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 +443 -0
- package/bin/grammatik.js +115 -0
- package/dist/cjs/index.js +3010 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/runtime.js +639 -0
- package/dist/esm/index.js +2959 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/runtime.js +605 -0
- package/dist/types/index.d.ts +214 -0
- package/dist/types/runtime.d.ts +40 -0
- package/package.json +65 -0
- package/src/analyze.d.ts +78 -0
- package/src/analyze.js +602 -0
- package/src/codegen.d.ts +36 -0
- package/src/codegen.js +635 -0
- package/src/definition.d.ts +133 -0
- package/src/definition.js +291 -0
- package/src/emit.d.ts +23 -0
- package/src/emit.js +374 -0
- package/src/firstchars.d.ts +6 -0
- package/src/firstchars.js +353 -0
- package/src/index.d.ts +7 -0
- package/src/index.js +15 -0
- package/src/lexer.d.ts +96 -0
- package/src/lexer.js +358 -0
- package/src/methods.d.ts +20 -0
- package/src/methods.js +61 -0
- package/src/methodsblock.js +197 -0
- package/src/parser.d.ts +130 -0
- package/src/parser.js +194 -0
- package/src/plan.d.ts +29 -0
- package/src/plan.js +938 -0
- package/src/runtime.js +16 -0
- package/src/state.d.ts +98 -0
- package/src/state.js +121 -0
- package/src/values.js +67 -0
|
@@ -0,0 +1,2959 @@
|
|
|
1
|
+
// src/parser.js
|
|
2
|
+
import { LogicEngine } from "json-logic-engine";
|
|
3
|
+
|
|
4
|
+
// src/firstchars.js
|
|
5
|
+
var DIGITS = [];
|
|
6
|
+
for (let c = 48; c <= 57; c++) DIGITS.push(c);
|
|
7
|
+
var WORD = DIGITS.concat([95]);
|
|
8
|
+
for (let c = 65; c <= 90; c++) WORD.push(c);
|
|
9
|
+
for (let c = 97; c <= 122; c++) WORD.push(c);
|
|
10
|
+
var SPACE = [9, 10, 11, 12, 13, 32, 160, 5760, 8232, 8233, 8239, 8287, 12288, 65279];
|
|
11
|
+
for (let c = 8192; c <= 8202; c++) SPACE.push(c);
|
|
12
|
+
var SIMPLE_ESCAPES = {
|
|
13
|
+
n: 10,
|
|
14
|
+
r: 13,
|
|
15
|
+
t: 9,
|
|
16
|
+
f: 12,
|
|
17
|
+
v: 11,
|
|
18
|
+
0: 0
|
|
19
|
+
};
|
|
20
|
+
function firstCharSet(source, ignoreCase) {
|
|
21
|
+
const found = /* @__PURE__ */ new Set();
|
|
22
|
+
let pos = 0;
|
|
23
|
+
let bailed = false;
|
|
24
|
+
const bail = () => {
|
|
25
|
+
bailed = true;
|
|
26
|
+
};
|
|
27
|
+
function add(code) {
|
|
28
|
+
found.add(code);
|
|
29
|
+
if (!ignoreCase) return;
|
|
30
|
+
const char = String.fromCharCode(code);
|
|
31
|
+
const upper = char.toUpperCase();
|
|
32
|
+
const lower = char.toLowerCase();
|
|
33
|
+
if (upper.length === 1) found.add(upper.charCodeAt(0));
|
|
34
|
+
if (lower.length === 1) found.add(lower.charCodeAt(0));
|
|
35
|
+
}
|
|
36
|
+
function addAll(codes) {
|
|
37
|
+
for (const code of codes) add(code);
|
|
38
|
+
}
|
|
39
|
+
function alternation(collect) {
|
|
40
|
+
let nullable = false;
|
|
41
|
+
for (; ; ) {
|
|
42
|
+
if (sequence(collect)) nullable = true;
|
|
43
|
+
if (bailed) return true;
|
|
44
|
+
if (source[pos] === "|") {
|
|
45
|
+
pos++;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
return nullable;
|
|
51
|
+
}
|
|
52
|
+
function sequence(collect) {
|
|
53
|
+
let stillNullable = true;
|
|
54
|
+
while (pos < source.length && source[pos] !== "|" && source[pos] !== ")") {
|
|
55
|
+
const nullable = element(collect && stillNullable);
|
|
56
|
+
if (bailed) return true;
|
|
57
|
+
if (!nullable) stillNullable = false;
|
|
58
|
+
}
|
|
59
|
+
return stillNullable;
|
|
60
|
+
}
|
|
61
|
+
function element(collect) {
|
|
62
|
+
const zeroWidth = atom(collect);
|
|
63
|
+
if (bailed) return true;
|
|
64
|
+
const quantifier = source[pos];
|
|
65
|
+
if (quantifier === "?" || quantifier === "*") {
|
|
66
|
+
pos++;
|
|
67
|
+
if (source[pos] === "?" || source[pos] === "+") pos++;
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
if (quantifier === "+") {
|
|
71
|
+
pos++;
|
|
72
|
+
if (source[pos] === "?" || source[pos] === "+") pos++;
|
|
73
|
+
return zeroWidth;
|
|
74
|
+
}
|
|
75
|
+
if (quantifier === "{") {
|
|
76
|
+
const close = source.indexOf("}", pos);
|
|
77
|
+
if (close === -1) {
|
|
78
|
+
bail();
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
const body = source.slice(pos + 1, close);
|
|
82
|
+
if (!/^\d+(,\d*)?$/.test(body)) {
|
|
83
|
+
bail();
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
pos = close + 1;
|
|
87
|
+
if (source[pos] === "?" || source[pos] === "+") pos++;
|
|
88
|
+
const min = parseInt(body, 10);
|
|
89
|
+
return min === 0 ? true : zeroWidth;
|
|
90
|
+
}
|
|
91
|
+
return zeroWidth;
|
|
92
|
+
}
|
|
93
|
+
function atom(collect) {
|
|
94
|
+
const char = source[pos];
|
|
95
|
+
if (char === "(") {
|
|
96
|
+
let lookaround = false;
|
|
97
|
+
if (source.startsWith("(?:", pos)) pos += 3;
|
|
98
|
+
else if (source.startsWith("(?=", pos) || source.startsWith("(?!", pos)) {
|
|
99
|
+
pos += 3;
|
|
100
|
+
lookaround = true;
|
|
101
|
+
} else if (source.startsWith("(?<=", pos) || source.startsWith("(?<!", pos)) {
|
|
102
|
+
pos += 4;
|
|
103
|
+
lookaround = true;
|
|
104
|
+
} else if (source.startsWith("(?<", pos)) {
|
|
105
|
+
const close = source.indexOf(">", pos);
|
|
106
|
+
if (close === -1) {
|
|
107
|
+
if (collect) bail();
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
pos = close + 1;
|
|
111
|
+
} else pos += 1;
|
|
112
|
+
const nullable = alternation(collect && !lookaround);
|
|
113
|
+
if (bailed) return true;
|
|
114
|
+
if (source[pos] !== ")") {
|
|
115
|
+
if (collect) bail();
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
pos++;
|
|
119
|
+
return lookaround ? true : nullable;
|
|
120
|
+
}
|
|
121
|
+
if (char === "[") return charClass(collect);
|
|
122
|
+
if (char === "\\") return escape(collect);
|
|
123
|
+
if (char === ".") {
|
|
124
|
+
if (collect) {
|
|
125
|
+
bail();
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
pos++;
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
if (char === "^" || char === "$") {
|
|
132
|
+
pos++;
|
|
133
|
+
return true;
|
|
134
|
+
}
|
|
135
|
+
if (char === void 0) {
|
|
136
|
+
bail();
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
if (collect) add(source.charCodeAt(pos));
|
|
140
|
+
pos++;
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
function skipCharClass() {
|
|
144
|
+
pos++;
|
|
145
|
+
if (source[pos] === "^") pos++;
|
|
146
|
+
if (source[pos] === "]") pos++;
|
|
147
|
+
while (pos < source.length && source[pos] !== "]") {
|
|
148
|
+
if (source[pos] === "\\") pos += 2;
|
|
149
|
+
else pos++;
|
|
150
|
+
}
|
|
151
|
+
if (source[pos] !== "]") {
|
|
152
|
+
bail();
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
pos++;
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
function charClass(collect) {
|
|
159
|
+
if (source[pos + 1] === "^") {
|
|
160
|
+
if (collect) {
|
|
161
|
+
bail();
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
return skipCharClass();
|
|
165
|
+
}
|
|
166
|
+
if (!collect) return skipCharClass();
|
|
167
|
+
pos++;
|
|
168
|
+
while (pos < source.length && source[pos] !== "]") {
|
|
169
|
+
let low;
|
|
170
|
+
if (source[pos] === "\\") {
|
|
171
|
+
const before = pos;
|
|
172
|
+
const zero = escape(collect, true);
|
|
173
|
+
if (bailed) return false;
|
|
174
|
+
if (zero === null) {
|
|
175
|
+
low = null;
|
|
176
|
+
} else {
|
|
177
|
+
low = zero;
|
|
178
|
+
}
|
|
179
|
+
if (pos === before) {
|
|
180
|
+
bail();
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
low = source.charCodeAt(pos);
|
|
185
|
+
pos++;
|
|
186
|
+
}
|
|
187
|
+
if (low !== null && source[pos] === "-" && source[pos + 1] !== "]" && pos + 1 < source.length) {
|
|
188
|
+
pos++;
|
|
189
|
+
let high;
|
|
190
|
+
if (source[pos] === "\\") {
|
|
191
|
+
high = escape(collect, true);
|
|
192
|
+
if (bailed) return false;
|
|
193
|
+
if (high === null) {
|
|
194
|
+
bail();
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
high = source.charCodeAt(pos);
|
|
199
|
+
pos++;
|
|
200
|
+
}
|
|
201
|
+
if (high < low) {
|
|
202
|
+
bail();
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
if (high - low > 4096) {
|
|
206
|
+
bail();
|
|
207
|
+
return false;
|
|
208
|
+
}
|
|
209
|
+
if (collect) for (let c = low; c <= high; c++) add(c);
|
|
210
|
+
} else if (low !== null && collect) {
|
|
211
|
+
add(low);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (source[pos] !== "]") {
|
|
215
|
+
bail();
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
pos++;
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
function escape(collect, inClass = false) {
|
|
222
|
+
pos++;
|
|
223
|
+
const char = source[pos];
|
|
224
|
+
if (char === void 0) {
|
|
225
|
+
bail();
|
|
226
|
+
return inClass ? null : true;
|
|
227
|
+
}
|
|
228
|
+
if (char === "d" || char === "w" || char === "s") {
|
|
229
|
+
pos++;
|
|
230
|
+
if (collect) addAll(char === "d" ? DIGITS : char === "w" ? WORD : SPACE);
|
|
231
|
+
return inClass ? null : false;
|
|
232
|
+
}
|
|
233
|
+
if (char === "D" || char === "W" || char === "S" || char === "p" || char === "P") {
|
|
234
|
+
if (collect) {
|
|
235
|
+
bail();
|
|
236
|
+
return inClass ? null : false;
|
|
237
|
+
}
|
|
238
|
+
pos++;
|
|
239
|
+
if ((char === "p" || char === "P") && source[pos] === "{") {
|
|
240
|
+
const close = source.indexOf("}", pos);
|
|
241
|
+
if (close === -1) {
|
|
242
|
+
bail();
|
|
243
|
+
return inClass ? null : false;
|
|
244
|
+
}
|
|
245
|
+
pos = close + 1;
|
|
246
|
+
}
|
|
247
|
+
return inClass ? null : false;
|
|
248
|
+
}
|
|
249
|
+
if (char === "b" || char === "B") {
|
|
250
|
+
pos++;
|
|
251
|
+
if (inClass) {
|
|
252
|
+
if (collect) add(8);
|
|
253
|
+
return 8;
|
|
254
|
+
}
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
if (char === "u") {
|
|
258
|
+
if (source[pos + 1] === "{") {
|
|
259
|
+
const close = source.indexOf("}", pos);
|
|
260
|
+
if (close === -1) {
|
|
261
|
+
bail();
|
|
262
|
+
return inClass ? null : false;
|
|
263
|
+
}
|
|
264
|
+
const code3 = parseInt(source.slice(pos + 2, close), 16);
|
|
265
|
+
pos = close + 1;
|
|
266
|
+
if (Number.isNaN(code3)) {
|
|
267
|
+
bail();
|
|
268
|
+
return inClass ? null : false;
|
|
269
|
+
}
|
|
270
|
+
if (collect) add(code3);
|
|
271
|
+
return inClass ? code3 : false;
|
|
272
|
+
}
|
|
273
|
+
const hex = source.slice(pos + 1, pos + 5);
|
|
274
|
+
if (!/^[0-9a-fA-F]{4}$/.test(hex)) {
|
|
275
|
+
bail();
|
|
276
|
+
return inClass ? null : false;
|
|
277
|
+
}
|
|
278
|
+
pos += 5;
|
|
279
|
+
const code2 = parseInt(hex, 16);
|
|
280
|
+
if (collect) add(code2);
|
|
281
|
+
return inClass ? code2 : false;
|
|
282
|
+
}
|
|
283
|
+
if (char === "x") {
|
|
284
|
+
const hex = source.slice(pos + 1, pos + 3);
|
|
285
|
+
if (!/^[0-9a-fA-F]{2}$/.test(hex)) {
|
|
286
|
+
bail();
|
|
287
|
+
return inClass ? null : false;
|
|
288
|
+
}
|
|
289
|
+
pos += 3;
|
|
290
|
+
const code2 = parseInt(hex, 16);
|
|
291
|
+
if (collect) add(code2);
|
|
292
|
+
return inClass ? code2 : false;
|
|
293
|
+
}
|
|
294
|
+
if (char >= "1" && char <= "9" && !inClass) {
|
|
295
|
+
if (collect) {
|
|
296
|
+
bail();
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
while (source[pos] >= "0" && source[pos] <= "9") pos++;
|
|
300
|
+
return false;
|
|
301
|
+
}
|
|
302
|
+
if (char in SIMPLE_ESCAPES) {
|
|
303
|
+
pos++;
|
|
304
|
+
const code2 = SIMPLE_ESCAPES[char];
|
|
305
|
+
if (collect) add(code2);
|
|
306
|
+
return inClass ? code2 : false;
|
|
307
|
+
}
|
|
308
|
+
pos++;
|
|
309
|
+
const code = char.charCodeAt(0);
|
|
310
|
+
if (collect) add(code);
|
|
311
|
+
return inClass ? code : false;
|
|
312
|
+
}
|
|
313
|
+
alternation(true);
|
|
314
|
+
if (bailed || pos !== source.length || found.size === 0) return null;
|
|
315
|
+
return found;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// src/lexer.js
|
|
319
|
+
var LexError = class extends Error {
|
|
320
|
+
/**
|
|
321
|
+
* @param {string} message
|
|
322
|
+
* @param {{ offset: number, line: number, col: number }} position
|
|
323
|
+
*/
|
|
324
|
+
constructor(message, position) {
|
|
325
|
+
super(message);
|
|
326
|
+
this.name = "LexError";
|
|
327
|
+
this.offset = position.offset;
|
|
328
|
+
this.line = position.line;
|
|
329
|
+
this.col = position.col;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
var DEFAULT_MODE = "default";
|
|
333
|
+
function modesOf(def) {
|
|
334
|
+
if (Array.isArray(def.modes) && def.modes.length > 0) return def.modes;
|
|
335
|
+
if (def.mode) return [def.mode];
|
|
336
|
+
return [DEFAULT_MODE];
|
|
337
|
+
}
|
|
338
|
+
function createLexer(defs, options = {}) {
|
|
339
|
+
const positions = options.positions === "offset" ? "offset" : "full";
|
|
340
|
+
const trackLines = positions === "full";
|
|
341
|
+
if (!Array.isArray(defs) || defs.length === 0) throw new Error("createLexer: expected a non-empty array of token definitions");
|
|
342
|
+
const tokenNames = defs.map((d) => d.name);
|
|
343
|
+
const seen = /* @__PURE__ */ new Set();
|
|
344
|
+
for (const name of tokenNames) {
|
|
345
|
+
if (seen.has(name)) throw new Error(`createLexer: duplicate token name '${name}'`);
|
|
346
|
+
seen.add(name);
|
|
347
|
+
}
|
|
348
|
+
const modeIds = /* @__PURE__ */ new Map([[DEFAULT_MODE, 0]]);
|
|
349
|
+
const modeNames = [DEFAULT_MODE];
|
|
350
|
+
const idOf = (name) => {
|
|
351
|
+
let id = modeIds.get(name);
|
|
352
|
+
if (id === void 0) {
|
|
353
|
+
id = modeNames.length;
|
|
354
|
+
modeIds.set(name, id);
|
|
355
|
+
modeNames.push(name);
|
|
356
|
+
}
|
|
357
|
+
return id;
|
|
358
|
+
};
|
|
359
|
+
for (const def of defs) for (const mode of modesOf(def)) idOf(mode);
|
|
360
|
+
const compiled = defs.map((def) => {
|
|
361
|
+
if (!def.name) throw new Error("createLexer: every token definition needs a name");
|
|
362
|
+
if (!def.pattern && !def.literal) throw new Error(`createLexer: token '${def.name}' needs a pattern or a literal`);
|
|
363
|
+
if (def.pattern && def.literal) throw new Error(`createLexer: token '${def.name}' has both a pattern and a literal; pick one`);
|
|
364
|
+
if (def.longerAlt && !seen.has(def.longerAlt)) throw new Error(`createLexer: token '${def.name}' has longerAlt '${def.longerAlt}', which is not a defined token`);
|
|
365
|
+
if (def.pushMode && def.popMode) {
|
|
366
|
+
throw new Error(
|
|
367
|
+
`createLexer: token '${def.name}' both pushes '${def.pushMode}' and pops. One token cannot do both \u2014 split it into two tokens, one per mode.`
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
if (def.pushMode && !modeIds.has(def.pushMode)) {
|
|
371
|
+
throw new Error(`createLexer: token '${def.name}' pushes mode '${def.pushMode}', which no token belongs to`);
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
name: def.name,
|
|
375
|
+
skip: !!def.skip,
|
|
376
|
+
literal: def.literal,
|
|
377
|
+
longerAlt: def.longerAlt || null,
|
|
378
|
+
regex: def.pattern ? new RegExp(def.pattern, def.ignoreCase ? "iy" : "y") : null,
|
|
379
|
+
modes: modesOf(def),
|
|
380
|
+
pushMode: def.pushMode ? idOf(def.pushMode) : -1,
|
|
381
|
+
popMode: !!def.popMode
|
|
382
|
+
};
|
|
383
|
+
});
|
|
384
|
+
const byName = new Map(compiled.map((c) => [c.name, c]));
|
|
385
|
+
for (const c of compiled) if (c.longerAlt) c.longerAltDef = byName.get(c.longerAlt);
|
|
386
|
+
for (const c of compiled) {
|
|
387
|
+
if (!c.longerAltDef) continue;
|
|
388
|
+
for (const mode of c.modes) {
|
|
389
|
+
if (c.longerAltDef.modes.includes(mode)) continue;
|
|
390
|
+
throw new Error(
|
|
391
|
+
`createLexer: token '${c.name}' is active in mode '${mode}' with longerAlt '${c.longerAlt}', which is not active there. Add '${mode}' to '${c.longerAlt}'.`
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
for (const c of compiled) {
|
|
396
|
+
c.firstChars = c.literal !== void 0 ? c.literal.length > 0 ? /* @__PURE__ */ new Set([c.literal.charCodeAt(0)]) : null : firstCharSet(c.regex.source, !!c.regex.ignoreCase);
|
|
397
|
+
}
|
|
398
|
+
const modes = modeNames.map((name) => {
|
|
399
|
+
const members = compiled.filter((c) => c.modes.includes(name));
|
|
400
|
+
const alwaysTry = members.filter((c) => c.firstChars === null);
|
|
401
|
+
const candidatesFor = (code) => members.filter((c) => c.firstChars === null || c.firstChars.has(code));
|
|
402
|
+
const ascii = new Array(128);
|
|
403
|
+
for (let code = 0; code < 128; code++) ascii[code] = candidatesFor(code);
|
|
404
|
+
return {
|
|
405
|
+
name,
|
|
406
|
+
members,
|
|
407
|
+
ascii,
|
|
408
|
+
wide: /* @__PURE__ */ new Map(),
|
|
409
|
+
candidatesFor,
|
|
410
|
+
// If nothing could be analyzed, dispatch is pure overhead; skip it.
|
|
411
|
+
dispatch: alwaysTry.length < members.length
|
|
412
|
+
};
|
|
413
|
+
});
|
|
414
|
+
for (const mode of modes) {
|
|
415
|
+
if (mode.members.length === 0) throw new Error(`createLexer: mode '${mode.name}' has no tokens`);
|
|
416
|
+
}
|
|
417
|
+
function candidates(mode, code) {
|
|
418
|
+
if (code < 128) return mode.ascii[code];
|
|
419
|
+
let list = mode.wide.get(code);
|
|
420
|
+
if (list === void 0) {
|
|
421
|
+
list = mode.candidatesFor(code);
|
|
422
|
+
mode.wide.set(code, list);
|
|
423
|
+
}
|
|
424
|
+
return list;
|
|
425
|
+
}
|
|
426
|
+
const multiMode = modes.length > 1 || compiled.some((c) => c.popMode || c.pushMode !== -1);
|
|
427
|
+
const namesModes = modes.length > 1;
|
|
428
|
+
const initial = modes[0];
|
|
429
|
+
function longerAltEnd(def, text, offset) {
|
|
430
|
+
const alt2 = def.longerAltDef;
|
|
431
|
+
if (alt2.literal !== void 0) return text.startsWith(alt2.literal, offset) ? offset + alt2.literal.length : -1;
|
|
432
|
+
alt2.regex.lastIndex = offset;
|
|
433
|
+
return alt2.regex.test(text) ? alt2.regex.lastIndex : -1;
|
|
434
|
+
}
|
|
435
|
+
function tokenize(text) {
|
|
436
|
+
if (typeof text !== "string") throw new TypeError("tokenize: expected a string");
|
|
437
|
+
const tokens = [];
|
|
438
|
+
let offset = 0;
|
|
439
|
+
let line = 1;
|
|
440
|
+
let lineStart = 0;
|
|
441
|
+
const length = text.length;
|
|
442
|
+
let mode = initial;
|
|
443
|
+
const stack = multiMode ? [] : null;
|
|
444
|
+
let depth = 0;
|
|
445
|
+
outer: while (offset < length) {
|
|
446
|
+
const applicable = mode.dispatch ? candidates(mode, text.charCodeAt(offset)) : mode.members;
|
|
447
|
+
for (let i = 0; i < applicable.length; i++) {
|
|
448
|
+
const def = applicable[i];
|
|
449
|
+
let end = -1;
|
|
450
|
+
let matched = null;
|
|
451
|
+
if (def.literal !== void 0) {
|
|
452
|
+
if (text.startsWith(def.literal, offset)) end = offset + def.literal.length;
|
|
453
|
+
} else if (def.skip) {
|
|
454
|
+
def.regex.lastIndex = offset;
|
|
455
|
+
if (def.regex.test(text) && def.regex.lastIndex > offset) end = def.regex.lastIndex;
|
|
456
|
+
} else {
|
|
457
|
+
def.regex.lastIndex = offset;
|
|
458
|
+
const match = def.regex.exec(text);
|
|
459
|
+
if (match !== null && match[0].length > 0) matched = match[0];
|
|
460
|
+
if (matched !== null) end = offset + matched.length;
|
|
461
|
+
}
|
|
462
|
+
if (end === -1) continue;
|
|
463
|
+
let winner = def;
|
|
464
|
+
if (def.longerAltDef) {
|
|
465
|
+
const altEnd = longerAltEnd(def, text, offset);
|
|
466
|
+
if (altEnd > end) {
|
|
467
|
+
end = altEnd;
|
|
468
|
+
winner = def.longerAltDef;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
if (!winner.skip) {
|
|
472
|
+
const image = winner.literal !== void 0 ? winner.literal : winner === def && matched !== null ? matched : text.slice(offset, end);
|
|
473
|
+
tokens.push({
|
|
474
|
+
type: winner.name,
|
|
475
|
+
image,
|
|
476
|
+
start: offset,
|
|
477
|
+
end,
|
|
478
|
+
line: trackLines ? line : 0,
|
|
479
|
+
col: trackLines ? offset - lineStart + 1 : 0
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
if (trackLines) {
|
|
483
|
+
for (let j = offset; j < end; j++) {
|
|
484
|
+
if (text.charCodeAt(j) !== 10) continue;
|
|
485
|
+
line++;
|
|
486
|
+
lineStart = j + 1;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (multiMode) {
|
|
490
|
+
if (winner.popMode) {
|
|
491
|
+
if (depth === 0) {
|
|
492
|
+
throw new LexError(
|
|
493
|
+
`Token '${winner.name}' popped the initial lexer mode at ${describe(offset, line, lineStart)}`,
|
|
494
|
+
{ offset, line: trackLines ? line : 0, col: trackLines ? offset - lineStart + 1 : 0 }
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
mode = stack[--depth];
|
|
498
|
+
} else if (winner.pushMode !== -1) {
|
|
499
|
+
stack[depth++] = mode;
|
|
500
|
+
mode = modes[winner.pushMode];
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
offset = end;
|
|
504
|
+
continue outer;
|
|
505
|
+
}
|
|
506
|
+
const column = offset - lineStart + 1;
|
|
507
|
+
throw new LexError(
|
|
508
|
+
trackLines ? `Unexpected character '${text[offset]}' at line ${line} column ${column}${inMode(mode)}` : `Unexpected character '${text[offset]}' at offset ${offset}${inMode(mode)}`,
|
|
509
|
+
{ offset, line: trackLines ? line : 0, col: trackLines ? column : 0 }
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
return tokens;
|
|
513
|
+
}
|
|
514
|
+
const inMode = (mode) => namesModes ? ` (in lexer mode '${mode.name}')` : "";
|
|
515
|
+
const describe = (offset, line, lineStart) => trackLines ? `line ${line} column ${offset - lineStart + 1}` : `offset ${offset}`;
|
|
516
|
+
return { tokenize, tokenNames, positions, modeNames };
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// src/state.js
|
|
520
|
+
var FAIL = /* @__PURE__ */ Symbol("grammatik.fail");
|
|
521
|
+
var createState = (tokens, grammar, options = {}) => ({
|
|
522
|
+
tokens,
|
|
523
|
+
idx: 0,
|
|
524
|
+
maxIdx: 0,
|
|
525
|
+
expected: [],
|
|
526
|
+
grammar,
|
|
527
|
+
memo: options.memo ? /* @__PURE__ */ new Map() : null,
|
|
528
|
+
// Entering a rule is one array store rather than an object allocation, and
|
|
529
|
+
// the stack is snapshotted only on a furthest failure — by the time an error
|
|
530
|
+
// is built it would otherwise have unwound to just the start rule.
|
|
531
|
+
ruleNames: [],
|
|
532
|
+
ruleDepth: 0,
|
|
533
|
+
failureStack: null,
|
|
534
|
+
track: options.track === true,
|
|
535
|
+
steps: 0,
|
|
536
|
+
maxSteps: options.maxSteps || 0
|
|
537
|
+
});
|
|
538
|
+
function expect(state, expectation) {
|
|
539
|
+
if (state.idx > state.maxIdx) {
|
|
540
|
+
state.maxIdx = state.idx;
|
|
541
|
+
if (!state.track) return;
|
|
542
|
+
state.expected = [];
|
|
543
|
+
state.failureStack = state.ruleNames.slice(0, state.ruleDepth);
|
|
544
|
+
}
|
|
545
|
+
if (!state.track || state.idx !== state.maxIdx) return;
|
|
546
|
+
if (!state.expected.includes(expectation)) state.expected.push(expectation);
|
|
547
|
+
}
|
|
548
|
+
var ParseError = class extends Error {
|
|
549
|
+
/**
|
|
550
|
+
* @param {string} message
|
|
551
|
+
* @param {{ token: import('./lexer.js').Token | null, expected: string[], ruleStack: string[] }} details
|
|
552
|
+
*/
|
|
553
|
+
constructor(message, details) {
|
|
554
|
+
super(message);
|
|
555
|
+
this.name = "ParseError";
|
|
556
|
+
this.token = details.token;
|
|
557
|
+
this.expected = details.expected;
|
|
558
|
+
this.ruleStack = details.ruleStack;
|
|
559
|
+
}
|
|
560
|
+
};
|
|
561
|
+
var describeExpected = (expected) => expected.length === 0 ? "nothing" : expected.length === 1 ? expected[0] : `one of [${expected.join(", ")}]`;
|
|
562
|
+
function buildParseError(state) {
|
|
563
|
+
const token = state.tokens[state.maxIdx] || null;
|
|
564
|
+
const expected = [...state.expected].sort();
|
|
565
|
+
const ruleStack = state.failureStack || state.ruleNames.slice(0, state.ruleDepth);
|
|
566
|
+
const at = (t) => state.positions === "offset" ? `offset ${t.start}` : `line ${t.line} column ${t.col}`;
|
|
567
|
+
const found = token ? `--> ${token.image} <-- (${token.type}) at ${at(token)}` : "end of input";
|
|
568
|
+
return new ParseError(
|
|
569
|
+
`Expecting ${describeExpected(expected)} but found ${found}`,
|
|
570
|
+
{ token, expected, ruleStack }
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// src/analyze.js
|
|
575
|
+
var emptyFirst = () => ({ tokens: /* @__PURE__ */ new Set(), nullable: false, unknown: false });
|
|
576
|
+
var epsilon = () => ({ tokens: /* @__PURE__ */ new Set(), nullable: true, unknown: false });
|
|
577
|
+
var opaque = () => ({ tokens: /* @__PURE__ */ new Set(), nullable: true, unknown: true });
|
|
578
|
+
function absorb(target, source) {
|
|
579
|
+
let changed = false;
|
|
580
|
+
for (const t of source.tokens) {
|
|
581
|
+
if (!target.tokens.has(t)) {
|
|
582
|
+
target.tokens.add(t);
|
|
583
|
+
changed = true;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
if (source.unknown && !target.unknown) {
|
|
587
|
+
target.unknown = true;
|
|
588
|
+
changed = true;
|
|
589
|
+
}
|
|
590
|
+
return changed;
|
|
591
|
+
}
|
|
592
|
+
function opOf(node) {
|
|
593
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return null;
|
|
594
|
+
const keys = Object.keys(node);
|
|
595
|
+
return keys.length === 1 ? keys[0] : null;
|
|
596
|
+
}
|
|
597
|
+
var PARSER_OPS = /* @__PURE__ */ new Set([
|
|
598
|
+
"consume",
|
|
599
|
+
"seq",
|
|
600
|
+
"alt",
|
|
601
|
+
"many",
|
|
602
|
+
"many1",
|
|
603
|
+
"option",
|
|
604
|
+
"manySep",
|
|
605
|
+
"many1Sep",
|
|
606
|
+
"infixLeft",
|
|
607
|
+
"infixRight",
|
|
608
|
+
"postfix",
|
|
609
|
+
"prefix",
|
|
610
|
+
"between",
|
|
611
|
+
"as",
|
|
612
|
+
"oneOf",
|
|
613
|
+
"eof",
|
|
614
|
+
"subrule",
|
|
615
|
+
"label",
|
|
616
|
+
"action",
|
|
617
|
+
"lookahead",
|
|
618
|
+
"negLookahead",
|
|
619
|
+
"text",
|
|
620
|
+
"epsilon"
|
|
621
|
+
]);
|
|
622
|
+
function firstOf(node, ruleFirsts) {
|
|
623
|
+
const op = opOf(node);
|
|
624
|
+
if (op === null) return opaque();
|
|
625
|
+
const arg = node[op];
|
|
626
|
+
switch (op) {
|
|
627
|
+
case "consume": {
|
|
628
|
+
const result = emptyFirst();
|
|
629
|
+
const name = Array.isArray(arg) ? arg[0] : arg;
|
|
630
|
+
if (typeof name === "string") result.tokens.add(name);
|
|
631
|
+
else result.unknown = true;
|
|
632
|
+
return result;
|
|
633
|
+
}
|
|
634
|
+
case "epsilon":
|
|
635
|
+
case "action":
|
|
636
|
+
return epsilon();
|
|
637
|
+
case "text":
|
|
638
|
+
case "label":
|
|
639
|
+
return firstOf(op === "label" ? arg[1] : arg, ruleFirsts);
|
|
640
|
+
case "seq": {
|
|
641
|
+
const result = epsilon();
|
|
642
|
+
if (!Array.isArray(arg)) return opaque();
|
|
643
|
+
for (const child of arg) {
|
|
644
|
+
const childFirst = firstOf(child, ruleFirsts);
|
|
645
|
+
absorb(result, childFirst);
|
|
646
|
+
if (!childFirst.nullable) {
|
|
647
|
+
result.nullable = false;
|
|
648
|
+
break;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
return result;
|
|
652
|
+
}
|
|
653
|
+
case "alt": {
|
|
654
|
+
const result = emptyFirst();
|
|
655
|
+
if (!Array.isArray(arg)) return opaque();
|
|
656
|
+
for (const child of arg) {
|
|
657
|
+
const childFirst = firstOf(child, ruleFirsts);
|
|
658
|
+
absorb(result, childFirst);
|
|
659
|
+
if (childFirst.nullable) result.nullable = true;
|
|
660
|
+
}
|
|
661
|
+
return result;
|
|
662
|
+
}
|
|
663
|
+
case "many":
|
|
664
|
+
case "option": {
|
|
665
|
+
const inner = firstOf(arg, ruleFirsts);
|
|
666
|
+
return { tokens: inner.tokens, nullable: true, unknown: inner.unknown };
|
|
667
|
+
}
|
|
668
|
+
case "many1": {
|
|
669
|
+
const inner = firstOf(arg, ruleFirsts);
|
|
670
|
+
return { tokens: inner.tokens, nullable: inner.nullable, unknown: inner.unknown };
|
|
671
|
+
}
|
|
672
|
+
case "oneOf": {
|
|
673
|
+
const result = emptyFirst();
|
|
674
|
+
const names = Array.isArray(arg) ? arg : [arg];
|
|
675
|
+
for (const name of names) {
|
|
676
|
+
if (typeof name === "string") result.tokens.add(name);
|
|
677
|
+
else result.unknown = true;
|
|
678
|
+
}
|
|
679
|
+
return result;
|
|
680
|
+
}
|
|
681
|
+
case "eof":
|
|
682
|
+
return opaque();
|
|
683
|
+
case "as":
|
|
684
|
+
return firstOf(Array.isArray(arg) ? arg[0] : arg && arg.parser, ruleFirsts);
|
|
685
|
+
case "between":
|
|
686
|
+
return firstOf({ seq: [arg && arg.open, arg && arg.body, arg && arg.close] }, ruleFirsts);
|
|
687
|
+
case "prefix": {
|
|
688
|
+
const ops = firstOf(arg && arg.operator, ruleFirsts);
|
|
689
|
+
const inner = firstOf(arg && arg.operand, ruleFirsts);
|
|
690
|
+
const result = emptyFirst();
|
|
691
|
+
absorb(result, ops);
|
|
692
|
+
absorb(result, inner);
|
|
693
|
+
result.nullable = inner.nullable;
|
|
694
|
+
return result;
|
|
695
|
+
}
|
|
696
|
+
case "postfix": {
|
|
697
|
+
const inner = firstOf(arg && arg.operand, ruleFirsts);
|
|
698
|
+
return { tokens: inner.tokens, nullable: inner.nullable, unknown: inner.unknown };
|
|
699
|
+
}
|
|
700
|
+
case "infixRight":
|
|
701
|
+
case "infixLeft": {
|
|
702
|
+
const inner = firstOf(arg && arg.operand, ruleFirsts);
|
|
703
|
+
return { tokens: inner.tokens, nullable: inner.nullable, unknown: inner.unknown };
|
|
704
|
+
}
|
|
705
|
+
case "manySep":
|
|
706
|
+
case "many1Sep": {
|
|
707
|
+
const inner = firstOf(arg && arg.rule, ruleFirsts);
|
|
708
|
+
return {
|
|
709
|
+
tokens: inner.tokens,
|
|
710
|
+
nullable: op === "manySep" ? true : inner.nullable,
|
|
711
|
+
unknown: inner.unknown
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
case "subrule": {
|
|
715
|
+
const name = Array.isArray(arg) ? arg[0] : arg;
|
|
716
|
+
const known = ruleFirsts.get(name);
|
|
717
|
+
if (!known) return opaque();
|
|
718
|
+
return { tokens: known.tokens, nullable: known.nullable, unknown: known.unknown };
|
|
719
|
+
}
|
|
720
|
+
// Zero-width assertions constrain the input without consuming it. They are
|
|
721
|
+
// nullable, but treating them as transparent would let `alt` dispatch past a
|
|
722
|
+
// guard that was meant to reject, so they are deliberately opaque.
|
|
723
|
+
case "lookahead":
|
|
724
|
+
case "negLookahead":
|
|
725
|
+
return opaque();
|
|
726
|
+
default:
|
|
727
|
+
return epsilon();
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
function recognizeSeqIdiom(children) {
|
|
731
|
+
if (!Array.isArray(children)) return null;
|
|
732
|
+
if (children.length === 4) {
|
|
733
|
+
const [open, labelled, close, act] = children;
|
|
734
|
+
if (opOf(labelled) === "label" && opOf(act) === "action" && isPlainParser(open) && isPlainParser(close)) {
|
|
735
|
+
const [name, body] = labelled.label;
|
|
736
|
+
const logic = act.action;
|
|
737
|
+
if (opOf(logic) === "val" && logic.val === name) {
|
|
738
|
+
return { between: { open, body, close } };
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
if (children.length === 2) {
|
|
743
|
+
const [labelled, act] = children;
|
|
744
|
+
if (opOf(labelled) === "label" && opOf(act) === "action") {
|
|
745
|
+
const [name, parser] = labelled.label;
|
|
746
|
+
const rewritten = rebaseOnLabel(act.action, name);
|
|
747
|
+
if (rewritten !== BAIL) return { as: [parser, rewritten] };
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return null;
|
|
751
|
+
}
|
|
752
|
+
function isPlainParser(node) {
|
|
753
|
+
const op = opOf(node);
|
|
754
|
+
return op !== null && op !== "label" && op !== "action";
|
|
755
|
+
}
|
|
756
|
+
var BAIL = /* @__PURE__ */ Symbol("grammatik.bail");
|
|
757
|
+
function rebaseOnLabel(logic, name) {
|
|
758
|
+
if (Array.isArray(logic)) {
|
|
759
|
+
const out2 = new Array(logic.length);
|
|
760
|
+
for (let i = 0; i < logic.length; i++) {
|
|
761
|
+
const value = rebaseOnLabel(logic[i], name);
|
|
762
|
+
if (value === BAIL) return BAIL;
|
|
763
|
+
out2[i] = value;
|
|
764
|
+
}
|
|
765
|
+
return out2;
|
|
766
|
+
}
|
|
767
|
+
if (!logic || typeof logic !== "object") return logic;
|
|
768
|
+
const keys = Object.keys(logic);
|
|
769
|
+
if (keys.length === 1 && keys[0] === "val") {
|
|
770
|
+
const path = logic.val;
|
|
771
|
+
if (path === name) return { val: [] };
|
|
772
|
+
if (Array.isArray(path) && path[0] === name) {
|
|
773
|
+
const rest = path.slice(1);
|
|
774
|
+
return { val: rest.length === 1 ? rest[0] : rest };
|
|
775
|
+
}
|
|
776
|
+
return BAIL;
|
|
777
|
+
}
|
|
778
|
+
if (keys.length === 1 && keys[0] === "var") return BAIL;
|
|
779
|
+
const out = {};
|
|
780
|
+
for (const key of keys) {
|
|
781
|
+
const value = rebaseOnLabel(logic[key], name);
|
|
782
|
+
if (value === BAIL) return BAIL;
|
|
783
|
+
out[key] = value;
|
|
784
|
+
}
|
|
785
|
+
return out;
|
|
786
|
+
}
|
|
787
|
+
function computeFirsts(grammar) {
|
|
788
|
+
const ruleFirsts = /* @__PURE__ */ new Map();
|
|
789
|
+
for (const name of Object.keys(grammar)) ruleFirsts.set(name, emptyFirst());
|
|
790
|
+
let changed = true;
|
|
791
|
+
while (changed) {
|
|
792
|
+
changed = false;
|
|
793
|
+
for (const name of Object.keys(grammar)) {
|
|
794
|
+
const current = ruleFirsts.get(name);
|
|
795
|
+
const computed = firstOf(grammar[name], ruleFirsts);
|
|
796
|
+
if (absorb(current, computed)) changed = true;
|
|
797
|
+
if (computed.nullable && !current.nullable) {
|
|
798
|
+
current.nullable = true;
|
|
799
|
+
changed = true;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return ruleFirsts;
|
|
804
|
+
}
|
|
805
|
+
function leftmostCalls(node, ruleFirsts, out) {
|
|
806
|
+
const op = opOf(node);
|
|
807
|
+
if (op === null) return;
|
|
808
|
+
const arg = node[op];
|
|
809
|
+
switch (op) {
|
|
810
|
+
case "subrule": {
|
|
811
|
+
const name = Array.isArray(arg) ? arg[0] : arg;
|
|
812
|
+
out.add(name);
|
|
813
|
+
break;
|
|
814
|
+
}
|
|
815
|
+
case "label":
|
|
816
|
+
leftmostCalls(arg[1], ruleFirsts, out);
|
|
817
|
+
break;
|
|
818
|
+
case "text":
|
|
819
|
+
case "many":
|
|
820
|
+
case "many1":
|
|
821
|
+
case "option":
|
|
822
|
+
case "lookahead":
|
|
823
|
+
case "negLookahead":
|
|
824
|
+
leftmostCalls(arg, ruleFirsts, out);
|
|
825
|
+
break;
|
|
826
|
+
case "as":
|
|
827
|
+
leftmostCalls(Array.isArray(arg) ? arg[0] : arg && arg.parser, ruleFirsts, out);
|
|
828
|
+
break;
|
|
829
|
+
case "between":
|
|
830
|
+
if (arg) leftmostCalls({ seq: [arg.open, arg.body, arg.close] }, ruleFirsts, out);
|
|
831
|
+
break;
|
|
832
|
+
case "prefix":
|
|
833
|
+
if (arg) {
|
|
834
|
+
leftmostCalls(arg.operator, ruleFirsts, out);
|
|
835
|
+
leftmostCalls(arg.operand, ruleFirsts, out);
|
|
836
|
+
}
|
|
837
|
+
break;
|
|
838
|
+
case "infixLeft":
|
|
839
|
+
case "infixRight":
|
|
840
|
+
case "postfix":
|
|
841
|
+
if (arg) leftmostCalls(arg.operand, ruleFirsts, out);
|
|
842
|
+
break;
|
|
843
|
+
case "manySep":
|
|
844
|
+
case "many1Sep":
|
|
845
|
+
if (arg) leftmostCalls(arg.rule, ruleFirsts, out);
|
|
846
|
+
break;
|
|
847
|
+
case "alt":
|
|
848
|
+
if (Array.isArray(arg)) for (const child of arg) leftmostCalls(child, ruleFirsts, out);
|
|
849
|
+
break;
|
|
850
|
+
case "seq":
|
|
851
|
+
if (Array.isArray(arg)) {
|
|
852
|
+
for (const child of arg) {
|
|
853
|
+
leftmostCalls(child, ruleFirsts, out);
|
|
854
|
+
if (!firstOf(child, ruleFirsts).nullable) break;
|
|
855
|
+
}
|
|
856
|
+
}
|
|
857
|
+
break;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
function validateNode(node, grammar, tokenNames, errors, path) {
|
|
861
|
+
const op = opOf(node);
|
|
862
|
+
if (op === null) {
|
|
863
|
+
if (Array.isArray(node)) node.forEach((c, i) => validateNode(c, grammar, tokenNames, errors, `${path}[${i}]`));
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
const arg = node[op];
|
|
867
|
+
switch (op) {
|
|
868
|
+
case "consume": {
|
|
869
|
+
const name = Array.isArray(arg) ? arg[0] : arg;
|
|
870
|
+
if (typeof name !== "string") errors.push(`${path}: 'consume' expects a token name, got ${JSON.stringify(name)}`);
|
|
871
|
+
else if (tokenNames && !tokenNames.has(name)) errors.push(`${path}: 'consume' references unknown token '${name}'`);
|
|
872
|
+
break;
|
|
873
|
+
}
|
|
874
|
+
case "subrule": {
|
|
875
|
+
const name = Array.isArray(arg) ? arg[0] : arg;
|
|
876
|
+
if (typeof name !== "string") errors.push(`${path}: 'subrule' expects a rule name, got ${JSON.stringify(name)}`);
|
|
877
|
+
else if (!(name in grammar)) errors.push(`${path}: 'subrule' references unknown rule '${name}'`);
|
|
878
|
+
break;
|
|
879
|
+
}
|
|
880
|
+
case "label": {
|
|
881
|
+
if (!Array.isArray(arg) || arg.length !== 2 || typeof arg[0] !== "string") {
|
|
882
|
+
errors.push(`${path}: 'label' expects [name, parser]`);
|
|
883
|
+
} else validateNode(arg[1], grammar, tokenNames, errors, `${path}.label(${arg[0]})`);
|
|
884
|
+
break;
|
|
885
|
+
}
|
|
886
|
+
case "seq":
|
|
887
|
+
case "alt": {
|
|
888
|
+
if (!Array.isArray(arg)) errors.push(`${path}: '${op}' expects an array`);
|
|
889
|
+
else {
|
|
890
|
+
if (op === "alt" && arg.length === 0) errors.push(`${path}: 'alt' needs at least one branch`);
|
|
891
|
+
arg.forEach((c, i) => validateNode(c, grammar, tokenNames, errors, `${path}.${op}[${i}]`));
|
|
892
|
+
}
|
|
893
|
+
break;
|
|
894
|
+
}
|
|
895
|
+
case "many":
|
|
896
|
+
case "many1":
|
|
897
|
+
case "option":
|
|
898
|
+
case "text":
|
|
899
|
+
case "lookahead":
|
|
900
|
+
case "negLookahead":
|
|
901
|
+
validateNode(arg, grammar, tokenNames, errors, `${path}.${op}`);
|
|
902
|
+
break;
|
|
903
|
+
case "as": {
|
|
904
|
+
const parser = Array.isArray(arg) ? arg[0] : arg && arg.parser;
|
|
905
|
+
if (parser === void 0) errors.push(`${path}: 'as' expects [parser, logic]`);
|
|
906
|
+
else validateNode(parser, grammar, tokenNames, errors, `${path}.as`);
|
|
907
|
+
break;
|
|
908
|
+
}
|
|
909
|
+
case "between": {
|
|
910
|
+
if (!arg || typeof arg !== "object" || !("open" in arg) || !("body" in arg) || !("close" in arg)) {
|
|
911
|
+
errors.push(`${path}: 'between' expects { open, body, close }`);
|
|
912
|
+
} else {
|
|
913
|
+
validateNode(arg.open, grammar, tokenNames, errors, `${path}.between.open`);
|
|
914
|
+
validateNode(arg.body, grammar, tokenNames, errors, `${path}.between.body`);
|
|
915
|
+
validateNode(arg.close, grammar, tokenNames, errors, `${path}.between.close`);
|
|
916
|
+
}
|
|
917
|
+
break;
|
|
918
|
+
}
|
|
919
|
+
case "prefix": {
|
|
920
|
+
if (!arg || typeof arg !== "object" || !("operator" in arg) || !("operand" in arg) || !("combine" in arg)) {
|
|
921
|
+
errors.push(`${path}: 'prefix' expects { operator, operand, combine }`);
|
|
922
|
+
} else {
|
|
923
|
+
validateNode(arg.operator, grammar, tokenNames, errors, `${path}.prefix.operator`);
|
|
924
|
+
validateNode(arg.operand, grammar, tokenNames, errors, `${path}.prefix.operand`);
|
|
925
|
+
}
|
|
926
|
+
break;
|
|
927
|
+
}
|
|
928
|
+
case "postfix": {
|
|
929
|
+
if (!arg || typeof arg !== "object" || !("operand" in arg) || !("suffix" in arg) || !("combine" in arg)) {
|
|
930
|
+
errors.push(`${path}: 'postfix' expects { operand, suffix, combine }`);
|
|
931
|
+
} else {
|
|
932
|
+
validateNode(arg.operand, grammar, tokenNames, errors, `${path}.postfix.operand`);
|
|
933
|
+
validateNode(arg.suffix, grammar, tokenNames, errors, `${path}.postfix.suffix`);
|
|
934
|
+
}
|
|
935
|
+
break;
|
|
936
|
+
}
|
|
937
|
+
case "oneOf": {
|
|
938
|
+
const names = Array.isArray(arg) ? arg : [arg];
|
|
939
|
+
for (const name of names) {
|
|
940
|
+
if (typeof name !== "string") errors.push(`${path}: 'oneOf' expects token names`);
|
|
941
|
+
else if (tokenNames && !tokenNames.has(name)) errors.push(`${path}: 'oneOf' references unknown token '${name}'`);
|
|
942
|
+
}
|
|
943
|
+
break;
|
|
944
|
+
}
|
|
945
|
+
case "eof":
|
|
946
|
+
break;
|
|
947
|
+
case "infixRight":
|
|
948
|
+
case "infixLeft": {
|
|
949
|
+
if (!arg || typeof arg !== "object" || !("operand" in arg) || !("combine" in arg)) {
|
|
950
|
+
errors.push(`${path}: '${op}' expects { operand, combine, operator? }`);
|
|
951
|
+
} else {
|
|
952
|
+
validateNode(arg.operand, grammar, tokenNames, errors, `${path}.${op}.operand`);
|
|
953
|
+
if (arg.operator !== void 0) validateNode(arg.operator, grammar, tokenNames, errors, `${path}.${op}.operator`);
|
|
954
|
+
}
|
|
955
|
+
break;
|
|
956
|
+
}
|
|
957
|
+
case "manySep":
|
|
958
|
+
case "many1Sep": {
|
|
959
|
+
if (!arg || typeof arg !== "object" || !("rule" in arg) || !("sep" in arg)) {
|
|
960
|
+
errors.push(`${path}: '${op}' expects { rule, sep }`);
|
|
961
|
+
} else {
|
|
962
|
+
validateNode(arg.rule, grammar, tokenNames, errors, `${path}.${op}.rule`);
|
|
963
|
+
if (typeof arg.sep === "string") {
|
|
964
|
+
if (tokenNames && !tokenNames.has(arg.sep)) errors.push(`${path}: '${op}' separator references unknown token '${arg.sep}'`);
|
|
965
|
+
} else validateNode(arg.sep, grammar, tokenNames, errors, `${path}.${op}.sep`);
|
|
966
|
+
}
|
|
967
|
+
break;
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
function analyze(grammar, options = {}) {
|
|
972
|
+
const errors = [];
|
|
973
|
+
const tokenNames = options.tokenNames ? new Set(options.tokenNames) : null;
|
|
974
|
+
for (const name of Object.keys(grammar)) {
|
|
975
|
+
validateNode(grammar[name], grammar, tokenNames, errors, name);
|
|
976
|
+
}
|
|
977
|
+
const firsts = computeFirsts(grammar);
|
|
978
|
+
const leftEdges = /* @__PURE__ */ new Map();
|
|
979
|
+
for (const name of Object.keys(grammar)) {
|
|
980
|
+
const out = /* @__PURE__ */ new Set();
|
|
981
|
+
leftmostCalls(grammar[name], firsts, out);
|
|
982
|
+
leftEdges.set(name, out);
|
|
983
|
+
}
|
|
984
|
+
const leftRecursive = [];
|
|
985
|
+
for (const start of Object.keys(grammar)) {
|
|
986
|
+
const seen = /* @__PURE__ */ new Set();
|
|
987
|
+
const stack = [...leftEdges.get(start) || []];
|
|
988
|
+
while (stack.length) {
|
|
989
|
+
const next = stack.pop();
|
|
990
|
+
if (next === start) {
|
|
991
|
+
leftRecursive.push(start);
|
|
992
|
+
break;
|
|
993
|
+
}
|
|
994
|
+
if (seen.has(next)) continue;
|
|
995
|
+
seen.add(next);
|
|
996
|
+
for (const edge of leftEdges.get(next) || []) stack.push(edge);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
for (const name of leftRecursive) {
|
|
1000
|
+
errors.push(`rule '${name}' is left-recursive; this parser is LL-style and would not terminate. Rewrite it with 'many'/'many1Sep' (the standard left-recursion elimination).`);
|
|
1001
|
+
}
|
|
1002
|
+
return { firsts, errors, leftRecursive };
|
|
1003
|
+
}
|
|
1004
|
+
function buildDispatch(branches, firsts) {
|
|
1005
|
+
const table = /* @__PURE__ */ new Map();
|
|
1006
|
+
for (const branch of branches) {
|
|
1007
|
+
const first = firstOf(branch, firsts);
|
|
1008
|
+
if (first.nullable || first.unknown || first.tokens.size === 0) return null;
|
|
1009
|
+
for (const token of first.tokens) {
|
|
1010
|
+
if (table.has(token)) return null;
|
|
1011
|
+
table.set(token, branch);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
return table.size > 0 ? table : null;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// src/plan.js
|
|
1018
|
+
var SYNC = /* @__PURE__ */ Symbol.for("json_logic_sync");
|
|
1019
|
+
function declareSync(fn) {
|
|
1020
|
+
fn[SYNC] = true;
|
|
1021
|
+
return fn;
|
|
1022
|
+
}
|
|
1023
|
+
function usesOuterScope(logic) {
|
|
1024
|
+
if (Array.isArray(logic)) return logic.some(usesOuterScope);
|
|
1025
|
+
if (!logic || typeof logic !== "object") return false;
|
|
1026
|
+
for (const key of Object.keys(logic)) {
|
|
1027
|
+
const arg = logic[key];
|
|
1028
|
+
if (key === "val" && Array.isArray(arg) && Array.isArray(arg[0])) return true;
|
|
1029
|
+
if (key === "var") {
|
|
1030
|
+
if (typeof arg === "string" && arg.includes("../")) return true;
|
|
1031
|
+
if (Array.isArray(arg) && typeof arg[0] === "string" && arg[0].includes("../")) return true;
|
|
1032
|
+
}
|
|
1033
|
+
if (usesOuterScope(arg)) return true;
|
|
1034
|
+
}
|
|
1035
|
+
return false;
|
|
1036
|
+
}
|
|
1037
|
+
function mayReturnContext(logic) {
|
|
1038
|
+
if (Array.isArray(logic)) return logic.some(mayReturnContext);
|
|
1039
|
+
if (!logic || typeof logic !== "object") return false;
|
|
1040
|
+
for (const key of Object.keys(logic)) {
|
|
1041
|
+
const arg = logic[key];
|
|
1042
|
+
if (key === "val" && Array.isArray(arg) && arg.length === 0) return true;
|
|
1043
|
+
if (key === "var" && (arg === "" || arg === null || Array.isArray(arg) && (arg.length === 0 || arg[0] === ""))) return true;
|
|
1044
|
+
if (mayReturnContext(arg)) return true;
|
|
1045
|
+
}
|
|
1046
|
+
return false;
|
|
1047
|
+
}
|
|
1048
|
+
function createPlanner(config) {
|
|
1049
|
+
const { engine, rules, firsts, memo, maxSteps, unsafeEval = true } = config;
|
|
1050
|
+
const slots = /* @__PURE__ */ new Map();
|
|
1051
|
+
const planCache = /* @__PURE__ */ new WeakMap();
|
|
1052
|
+
const opCaches = /* @__PURE__ */ new Map();
|
|
1053
|
+
const guard = maxSteps > 0;
|
|
1054
|
+
function tick(state) {
|
|
1055
|
+
if (guard && ++state.steps > maxSteps) {
|
|
1056
|
+
throw new Error(`Parser exceeded maxSteps (${maxSteps}); the grammar is likely looping.`);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
function assertProgress(state, before, op) {
|
|
1060
|
+
if (state.idx === before) {
|
|
1061
|
+
throw new Error(
|
|
1062
|
+
`'${op}' matched without consuming any input at token index ${before}; this would loop forever. Make the repeated parser consume at least one token.`
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
function slotFor(name) {
|
|
1067
|
+
let slot = slots.get(name);
|
|
1068
|
+
if (slot !== void 0) return slot;
|
|
1069
|
+
slot = { fn: null };
|
|
1070
|
+
slots.set(name, slot);
|
|
1071
|
+
const body = rules[name];
|
|
1072
|
+
if (body === void 0) {
|
|
1073
|
+
slot.fn = () => {
|
|
1074
|
+
throw new Error(`Unknown rule '${name}'`);
|
|
1075
|
+
};
|
|
1076
|
+
return slot;
|
|
1077
|
+
}
|
|
1078
|
+
slot.fn = plan(body);
|
|
1079
|
+
return slot;
|
|
1080
|
+
}
|
|
1081
|
+
function planAction(logic) {
|
|
1082
|
+
const interpret = (bindings, state) => engine.run(logic, bindings, { above: [state] });
|
|
1083
|
+
if (!unsafeEval || usesOuterScope(logic)) return interpret;
|
|
1084
|
+
try {
|
|
1085
|
+
const built = engine.build(logic);
|
|
1086
|
+
return typeof built === "function" ? (bindings) => built(bindings) : () => built;
|
|
1087
|
+
} catch {
|
|
1088
|
+
return interpret;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
function planOp(op, args) {
|
|
1092
|
+
let cache = opCaches.get(op);
|
|
1093
|
+
if (cache === void 0) {
|
|
1094
|
+
cache = { byObject: /* @__PURE__ */ new WeakMap(), byPrimitive: /* @__PURE__ */ new Map() };
|
|
1095
|
+
opCaches.set(op, cache);
|
|
1096
|
+
}
|
|
1097
|
+
if (args !== null && typeof args === "object") {
|
|
1098
|
+
let planned2 = cache.byObject.get(args);
|
|
1099
|
+
if (planned2 === void 0) {
|
|
1100
|
+
planned2 = planUncached({ [op]: args });
|
|
1101
|
+
cache.byObject.set(args, planned2);
|
|
1102
|
+
}
|
|
1103
|
+
return planned2;
|
|
1104
|
+
}
|
|
1105
|
+
const key = typeof args + ":" + String(args);
|
|
1106
|
+
let planned = cache.byPrimitive.get(key);
|
|
1107
|
+
if (planned === void 0) {
|
|
1108
|
+
planned = planUncached({ [op]: args });
|
|
1109
|
+
cache.byPrimitive.set(key, planned);
|
|
1110
|
+
}
|
|
1111
|
+
return planned;
|
|
1112
|
+
}
|
|
1113
|
+
function plan(node) {
|
|
1114
|
+
if (node === null || typeof node !== "object") {
|
|
1115
|
+
const constant = node;
|
|
1116
|
+
return () => constant;
|
|
1117
|
+
}
|
|
1118
|
+
const cached = planCache.get(node);
|
|
1119
|
+
if (cached !== void 0) return cached;
|
|
1120
|
+
const planned = planUncached(node);
|
|
1121
|
+
planCache.set(node, planned);
|
|
1122
|
+
publish(node, planned);
|
|
1123
|
+
return planned;
|
|
1124
|
+
}
|
|
1125
|
+
function publish(node, planned) {
|
|
1126
|
+
if (engine && engine.optimizedMap && !engine.disableInterpretedOptimization) {
|
|
1127
|
+
engine.optimizedMap.set(node, planned);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
const buildConsume = (arg) => {
|
|
1131
|
+
const name = typeof arg === "string" ? arg : arg[0];
|
|
1132
|
+
return (state) => {
|
|
1133
|
+
const token = state.tokens[state.idx];
|
|
1134
|
+
if (token !== void 0 && token.type === name) {
|
|
1135
|
+
state.idx++;
|
|
1136
|
+
return token;
|
|
1137
|
+
}
|
|
1138
|
+
expect(state, name);
|
|
1139
|
+
return FAIL;
|
|
1140
|
+
};
|
|
1141
|
+
};
|
|
1142
|
+
const buildMany = (arg) => {
|
|
1143
|
+
const child = plan(arg);
|
|
1144
|
+
return (state) => {
|
|
1145
|
+
tick(state);
|
|
1146
|
+
const results = [];
|
|
1147
|
+
for (; ; ) {
|
|
1148
|
+
const before = state.idx;
|
|
1149
|
+
const value = child(state);
|
|
1150
|
+
if (value === FAIL) {
|
|
1151
|
+
state.idx = before;
|
|
1152
|
+
return results;
|
|
1153
|
+
}
|
|
1154
|
+
assertProgress(state, before, "many");
|
|
1155
|
+
results.push(value);
|
|
1156
|
+
}
|
|
1157
|
+
};
|
|
1158
|
+
};
|
|
1159
|
+
const buildMany1 = (arg) => {
|
|
1160
|
+
const child = plan(arg);
|
|
1161
|
+
return (state) => {
|
|
1162
|
+
tick(state);
|
|
1163
|
+
const start = state.idx;
|
|
1164
|
+
const first = child(state);
|
|
1165
|
+
if (first === FAIL) {
|
|
1166
|
+
state.idx = start;
|
|
1167
|
+
return FAIL;
|
|
1168
|
+
}
|
|
1169
|
+
assertProgress(state, start, "many1");
|
|
1170
|
+
const results = [first];
|
|
1171
|
+
for (; ; ) {
|
|
1172
|
+
const before = state.idx;
|
|
1173
|
+
const value = child(state);
|
|
1174
|
+
if (value === FAIL) {
|
|
1175
|
+
state.idx = before;
|
|
1176
|
+
return results;
|
|
1177
|
+
}
|
|
1178
|
+
assertProgress(state, before, "many1");
|
|
1179
|
+
results.push(value);
|
|
1180
|
+
}
|
|
1181
|
+
};
|
|
1182
|
+
};
|
|
1183
|
+
const buildOption = (arg) => {
|
|
1184
|
+
const child = plan(arg);
|
|
1185
|
+
return (state) => {
|
|
1186
|
+
const start = state.idx;
|
|
1187
|
+
const value = child(state);
|
|
1188
|
+
if (value === FAIL) {
|
|
1189
|
+
state.idx = start;
|
|
1190
|
+
return null;
|
|
1191
|
+
}
|
|
1192
|
+
return value;
|
|
1193
|
+
};
|
|
1194
|
+
};
|
|
1195
|
+
const buildInfixLeft = (arg) => {
|
|
1196
|
+
const operand = plan(arg.operand);
|
|
1197
|
+
const operator = arg.operator === void 0 ? null : plan(arg.operator);
|
|
1198
|
+
const combine = planAction(arg.combine);
|
|
1199
|
+
return (state) => {
|
|
1200
|
+
tick(state);
|
|
1201
|
+
let left = operand(state);
|
|
1202
|
+
if (left === FAIL) return FAIL;
|
|
1203
|
+
let bindings = null;
|
|
1204
|
+
for (; ; ) {
|
|
1205
|
+
const mark = state.idx;
|
|
1206
|
+
let op = null;
|
|
1207
|
+
if (operator !== null) {
|
|
1208
|
+
op = operator(state);
|
|
1209
|
+
if (op === FAIL) {
|
|
1210
|
+
state.idx = mark;
|
|
1211
|
+
return left;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
const right = operand(state);
|
|
1215
|
+
if (right === FAIL) {
|
|
1216
|
+
state.idx = mark;
|
|
1217
|
+
return left;
|
|
1218
|
+
}
|
|
1219
|
+
if (bindings === null) bindings = { left: null, op: null, right: null };
|
|
1220
|
+
bindings.left = left;
|
|
1221
|
+
bindings.op = op;
|
|
1222
|
+
bindings.right = right;
|
|
1223
|
+
left = combine(bindings, state);
|
|
1224
|
+
}
|
|
1225
|
+
};
|
|
1226
|
+
};
|
|
1227
|
+
const buildOneOf = (arg) => {
|
|
1228
|
+
const names = Array.isArray(arg) ? arg : [arg];
|
|
1229
|
+
const set = new Set(names);
|
|
1230
|
+
const count = names.length;
|
|
1231
|
+
return (state) => {
|
|
1232
|
+
const token = state.tokens[state.idx];
|
|
1233
|
+
if (token !== void 0 && set.has(token.type)) {
|
|
1234
|
+
state.idx++;
|
|
1235
|
+
return token;
|
|
1236
|
+
}
|
|
1237
|
+
for (let i = 0; i < count; i++) expect(state, names[i]);
|
|
1238
|
+
return FAIL;
|
|
1239
|
+
};
|
|
1240
|
+
};
|
|
1241
|
+
const buildEof = (arg) => {
|
|
1242
|
+
return (state) => {
|
|
1243
|
+
if (state.idx >= state.tokens.length) return null;
|
|
1244
|
+
expect(state, "end of input");
|
|
1245
|
+
return FAIL;
|
|
1246
|
+
};
|
|
1247
|
+
};
|
|
1248
|
+
const buildInfixRight = (arg) => {
|
|
1249
|
+
const operand = plan(arg.operand);
|
|
1250
|
+
const operator = arg.operator === void 0 ? null : plan(arg.operator);
|
|
1251
|
+
const combine = planAction(arg.combine);
|
|
1252
|
+
return (state) => {
|
|
1253
|
+
tick(state);
|
|
1254
|
+
const left = operand(state);
|
|
1255
|
+
if (left === FAIL) return FAIL;
|
|
1256
|
+
let items = null;
|
|
1257
|
+
for (; ; ) {
|
|
1258
|
+
const mark = state.idx;
|
|
1259
|
+
let op = null;
|
|
1260
|
+
if (operator !== null) {
|
|
1261
|
+
op = operator(state);
|
|
1262
|
+
if (op === FAIL) {
|
|
1263
|
+
state.idx = mark;
|
|
1264
|
+
break;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
const right = operand(state);
|
|
1268
|
+
if (right === FAIL) {
|
|
1269
|
+
state.idx = mark;
|
|
1270
|
+
break;
|
|
1271
|
+
}
|
|
1272
|
+
if (items === null) items = [];
|
|
1273
|
+
items.push(op, right);
|
|
1274
|
+
}
|
|
1275
|
+
if (items === null) return left;
|
|
1276
|
+
const bindings = { left: null, op: null, right: null };
|
|
1277
|
+
let value = items[items.length - 1];
|
|
1278
|
+
for (let i = items.length - 2; i >= 0; i -= 2) {
|
|
1279
|
+
bindings.left = i === 0 ? left : items[i - 1];
|
|
1280
|
+
bindings.op = items[i];
|
|
1281
|
+
bindings.right = value;
|
|
1282
|
+
value = combine(bindings, state);
|
|
1283
|
+
}
|
|
1284
|
+
return value;
|
|
1285
|
+
};
|
|
1286
|
+
};
|
|
1287
|
+
const buildAs = (arg) => {
|
|
1288
|
+
const child = plan(Array.isArray(arg) ? arg[0] : arg.parser);
|
|
1289
|
+
const transform = planAction(Array.isArray(arg) ? arg[1] : arg.to);
|
|
1290
|
+
return (state) => {
|
|
1291
|
+
const value = child(state);
|
|
1292
|
+
if (value === FAIL) return FAIL;
|
|
1293
|
+
return transform(value, state);
|
|
1294
|
+
};
|
|
1295
|
+
};
|
|
1296
|
+
const buildBetween = (arg) => {
|
|
1297
|
+
const open = plan(arg.open);
|
|
1298
|
+
const body = plan(arg.body);
|
|
1299
|
+
const close = plan(arg.close);
|
|
1300
|
+
return (state) => {
|
|
1301
|
+
tick(state);
|
|
1302
|
+
const start = state.idx;
|
|
1303
|
+
if (open(state) === FAIL) {
|
|
1304
|
+
state.idx = start;
|
|
1305
|
+
return FAIL;
|
|
1306
|
+
}
|
|
1307
|
+
const value = body(state);
|
|
1308
|
+
if (value === FAIL) {
|
|
1309
|
+
state.idx = start;
|
|
1310
|
+
return FAIL;
|
|
1311
|
+
}
|
|
1312
|
+
if (close(state) === FAIL) {
|
|
1313
|
+
state.idx = start;
|
|
1314
|
+
return FAIL;
|
|
1315
|
+
}
|
|
1316
|
+
return value;
|
|
1317
|
+
};
|
|
1318
|
+
};
|
|
1319
|
+
const buildPrefix = (arg) => {
|
|
1320
|
+
const operator = plan(arg.operator);
|
|
1321
|
+
const operand = plan(arg.operand);
|
|
1322
|
+
const combine = planAction(arg.combine);
|
|
1323
|
+
return (state) => {
|
|
1324
|
+
tick(state);
|
|
1325
|
+
const start = state.idx;
|
|
1326
|
+
let ops = null;
|
|
1327
|
+
for (; ; ) {
|
|
1328
|
+
const mark = state.idx;
|
|
1329
|
+
const op = operator(state);
|
|
1330
|
+
if (op === FAIL) {
|
|
1331
|
+
state.idx = mark;
|
|
1332
|
+
break;
|
|
1333
|
+
}
|
|
1334
|
+
if (ops === null) ops = [];
|
|
1335
|
+
ops.push(op);
|
|
1336
|
+
}
|
|
1337
|
+
let value = operand(state);
|
|
1338
|
+
if (value === FAIL) {
|
|
1339
|
+
state.idx = start;
|
|
1340
|
+
return FAIL;
|
|
1341
|
+
}
|
|
1342
|
+
if (ops !== null) {
|
|
1343
|
+
const bindings = { op: null, operand: null };
|
|
1344
|
+
for (let i = ops.length - 1; i >= 0; i--) {
|
|
1345
|
+
bindings.op = ops[i];
|
|
1346
|
+
bindings.operand = value;
|
|
1347
|
+
value = combine(bindings, state);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
return value;
|
|
1351
|
+
};
|
|
1352
|
+
};
|
|
1353
|
+
const buildPostfix = (arg) => {
|
|
1354
|
+
const operand = plan(arg.operand);
|
|
1355
|
+
const suffix = plan(arg.suffix);
|
|
1356
|
+
const combine = planAction(arg.combine);
|
|
1357
|
+
return (state) => {
|
|
1358
|
+
tick(state);
|
|
1359
|
+
let left = operand(state);
|
|
1360
|
+
if (left === FAIL) return FAIL;
|
|
1361
|
+
let bindings = null;
|
|
1362
|
+
for (; ; ) {
|
|
1363
|
+
const mark = state.idx;
|
|
1364
|
+
const found = suffix(state);
|
|
1365
|
+
if (found === FAIL) {
|
|
1366
|
+
state.idx = mark;
|
|
1367
|
+
return left;
|
|
1368
|
+
}
|
|
1369
|
+
if (bindings === null) bindings = { left: null, suffix: null };
|
|
1370
|
+
bindings.left = left;
|
|
1371
|
+
bindings.suffix = found;
|
|
1372
|
+
left = combine(bindings, state);
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
};
|
|
1376
|
+
const buildSubrule = (arg) => {
|
|
1377
|
+
const name = typeof arg === "string" ? arg : arg[0];
|
|
1378
|
+
const slot = slotFor(name);
|
|
1379
|
+
if (memo) {
|
|
1380
|
+
return (state) => {
|
|
1381
|
+
tick(state);
|
|
1382
|
+
const key = name + ":" + state.idx;
|
|
1383
|
+
const hit = state.memo.get(key);
|
|
1384
|
+
if (hit !== void 0) {
|
|
1385
|
+
state.idx = hit.end;
|
|
1386
|
+
return hit.value;
|
|
1387
|
+
}
|
|
1388
|
+
const start = state.idx;
|
|
1389
|
+
let depth = 0;
|
|
1390
|
+
if (state.track) {
|
|
1391
|
+
depth = state.ruleDepth;
|
|
1392
|
+
state.ruleNames[depth] = name;
|
|
1393
|
+
state.ruleDepth = depth + 1;
|
|
1394
|
+
}
|
|
1395
|
+
const value = slot.fn(state);
|
|
1396
|
+
if (state.track) state.ruleDepth = depth;
|
|
1397
|
+
if (value === FAIL) state.idx = start;
|
|
1398
|
+
state.memo.set(key, { value, end: state.idx });
|
|
1399
|
+
return value;
|
|
1400
|
+
};
|
|
1401
|
+
}
|
|
1402
|
+
return (state) => {
|
|
1403
|
+
tick(state);
|
|
1404
|
+
if (!state.track) return slot.fn(state);
|
|
1405
|
+
const depth = state.ruleDepth;
|
|
1406
|
+
state.ruleNames[depth] = name;
|
|
1407
|
+
state.ruleDepth = depth + 1;
|
|
1408
|
+
const value = slot.fn(state);
|
|
1409
|
+
state.ruleDepth = depth;
|
|
1410
|
+
return value;
|
|
1411
|
+
};
|
|
1412
|
+
};
|
|
1413
|
+
const buildLabel = (arg) => {
|
|
1414
|
+
const name = arg[0];
|
|
1415
|
+
const child = plan(arg[1]);
|
|
1416
|
+
return (state) => {
|
|
1417
|
+
const value = child(state);
|
|
1418
|
+
if (value !== FAIL && state.bindings) state.bindings[name] = value;
|
|
1419
|
+
return value;
|
|
1420
|
+
};
|
|
1421
|
+
};
|
|
1422
|
+
const buildAction = (arg) => {
|
|
1423
|
+
const action2 = planAction(arg);
|
|
1424
|
+
return (state) => action2(state.bindings || {}, state);
|
|
1425
|
+
};
|
|
1426
|
+
const buildLookahead = (arg) => {
|
|
1427
|
+
const child = plan(arg);
|
|
1428
|
+
return (state) => {
|
|
1429
|
+
const start = state.idx;
|
|
1430
|
+
const value = child(state);
|
|
1431
|
+
state.idx = start;
|
|
1432
|
+
return value;
|
|
1433
|
+
};
|
|
1434
|
+
};
|
|
1435
|
+
const buildNegLookahead = (arg) => {
|
|
1436
|
+
const child = plan(arg);
|
|
1437
|
+
return (state) => {
|
|
1438
|
+
const start = state.idx;
|
|
1439
|
+
const value = child(state);
|
|
1440
|
+
state.idx = start;
|
|
1441
|
+
return value === FAIL ? null : FAIL;
|
|
1442
|
+
};
|
|
1443
|
+
};
|
|
1444
|
+
const buildText = (arg) => {
|
|
1445
|
+
const child = plan(arg);
|
|
1446
|
+
return (state) => {
|
|
1447
|
+
const start = state.idx;
|
|
1448
|
+
const value = child(state);
|
|
1449
|
+
if (value === FAIL) {
|
|
1450
|
+
state.idx = start;
|
|
1451
|
+
return FAIL;
|
|
1452
|
+
}
|
|
1453
|
+
if (state.idx === start) return "";
|
|
1454
|
+
const first = state.tokens[start];
|
|
1455
|
+
const last = state.tokens[state.idx - 1];
|
|
1456
|
+
if (state.source !== void 0) return state.source.slice(first.start, last.end);
|
|
1457
|
+
let out = "";
|
|
1458
|
+
for (let i = start; i < state.idx; i++) out += state.tokens[i].image;
|
|
1459
|
+
return out;
|
|
1460
|
+
};
|
|
1461
|
+
};
|
|
1462
|
+
const buildValue = (node) => {
|
|
1463
|
+
const built = planAction(node);
|
|
1464
|
+
return (state) => built(state.bindings || {}, state);
|
|
1465
|
+
};
|
|
1466
|
+
const BUILDERS = {
|
|
1467
|
+
consume: buildConsume,
|
|
1468
|
+
epsilon: () => () => null,
|
|
1469
|
+
seq: planSeq,
|
|
1470
|
+
alt: planAlt,
|
|
1471
|
+
many: buildMany,
|
|
1472
|
+
many1: buildMany1,
|
|
1473
|
+
option: buildOption,
|
|
1474
|
+
infixLeft: buildInfixLeft,
|
|
1475
|
+
oneOf: buildOneOf,
|
|
1476
|
+
eof: buildEof,
|
|
1477
|
+
infixRight: buildInfixRight,
|
|
1478
|
+
as: buildAs,
|
|
1479
|
+
between: buildBetween,
|
|
1480
|
+
prefix: buildPrefix,
|
|
1481
|
+
postfix: buildPostfix,
|
|
1482
|
+
manySep: (arg) => planSeparated(arg, false),
|
|
1483
|
+
many1Sep: (arg) => planSeparated(arg, true),
|
|
1484
|
+
subrule: buildSubrule,
|
|
1485
|
+
label: buildLabel,
|
|
1486
|
+
action: buildAction,
|
|
1487
|
+
lookahead: buildLookahead,
|
|
1488
|
+
negLookahead: buildNegLookahead,
|
|
1489
|
+
text: buildText
|
|
1490
|
+
};
|
|
1491
|
+
function planUncached(node) {
|
|
1492
|
+
const op = opOf(node);
|
|
1493
|
+
if (op === null) return () => node;
|
|
1494
|
+
const build = BUILDERS[op];
|
|
1495
|
+
return build ? build(node[op]) : buildValue(node);
|
|
1496
|
+
}
|
|
1497
|
+
function planSeq(children) {
|
|
1498
|
+
if (!Array.isArray(children)) throw new Error("'seq' expects an array of parsers");
|
|
1499
|
+
const idiom = recognizeSeqIdiom(children);
|
|
1500
|
+
if (idiom !== null) return planUncached(idiom);
|
|
1501
|
+
const PARSER = 0;
|
|
1502
|
+
const LABELLED = 1;
|
|
1503
|
+
const ACTION = 2;
|
|
1504
|
+
const TERMINAL = 3;
|
|
1505
|
+
const LABELLED_TERMINAL = 4;
|
|
1506
|
+
const kinds = [];
|
|
1507
|
+
const names = [];
|
|
1508
|
+
const tokens = [];
|
|
1509
|
+
const fns = [];
|
|
1510
|
+
const actionLogic = [];
|
|
1511
|
+
let hasAction = false;
|
|
1512
|
+
let hasLabels = false;
|
|
1513
|
+
const terminalOf = (node) => {
|
|
1514
|
+
if (opOf(node) !== "consume") return null;
|
|
1515
|
+
const arg = node.consume;
|
|
1516
|
+
const name = typeof arg === "string" ? arg : Array.isArray(arg) ? arg[0] : null;
|
|
1517
|
+
return typeof name === "string" ? name : null;
|
|
1518
|
+
};
|
|
1519
|
+
for (const child of children) {
|
|
1520
|
+
const childOp = opOf(child);
|
|
1521
|
+
if (childOp === "label") {
|
|
1522
|
+
const [name, inner] = child.label;
|
|
1523
|
+
const terminal2 = terminalOf(inner);
|
|
1524
|
+
hasLabels = true;
|
|
1525
|
+
if (terminal2 !== null) {
|
|
1526
|
+
kinds.push(LABELLED_TERMINAL);
|
|
1527
|
+
names.push(name);
|
|
1528
|
+
tokens.push(terminal2);
|
|
1529
|
+
fns.push(null);
|
|
1530
|
+
} else {
|
|
1531
|
+
kinds.push(LABELLED);
|
|
1532
|
+
names.push(name);
|
|
1533
|
+
tokens.push(null);
|
|
1534
|
+
fns.push(plan(inner));
|
|
1535
|
+
}
|
|
1536
|
+
continue;
|
|
1537
|
+
}
|
|
1538
|
+
if (childOp === "action") {
|
|
1539
|
+
kinds.push(ACTION);
|
|
1540
|
+
names.push(null);
|
|
1541
|
+
tokens.push(null);
|
|
1542
|
+
fns.push(planAction(child.action));
|
|
1543
|
+
actionLogic.push(child.action);
|
|
1544
|
+
hasAction = true;
|
|
1545
|
+
continue;
|
|
1546
|
+
}
|
|
1547
|
+
const terminal = terminalOf(child);
|
|
1548
|
+
if (terminal !== null) {
|
|
1549
|
+
kinds.push(TERMINAL);
|
|
1550
|
+
names.push(null);
|
|
1551
|
+
tokens.push(terminal);
|
|
1552
|
+
fns.push(null);
|
|
1553
|
+
} else {
|
|
1554
|
+
kinds.push(PARSER);
|
|
1555
|
+
names.push(null);
|
|
1556
|
+
tokens.push(null);
|
|
1557
|
+
fns.push(plan(child));
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
const count = fns.length;
|
|
1561
|
+
const needsScope = hasLabels || hasAction;
|
|
1562
|
+
const reusableScope = needsScope && kinds.every((k) => k === TERMINAL || k === LABELLED_TERMINAL || k === ACTION) && !actionLogic.some(mayReturnContext);
|
|
1563
|
+
const scratch = reusableScope ? {} : null;
|
|
1564
|
+
return (state) => {
|
|
1565
|
+
tick(state);
|
|
1566
|
+
const start = state.idx;
|
|
1567
|
+
const previousBindings = state.bindings;
|
|
1568
|
+
const bindings = reusableScope ? scratch : needsScope ? {} : previousBindings;
|
|
1569
|
+
if (needsScope) state.bindings = bindings;
|
|
1570
|
+
const results = hasAction ? null : [];
|
|
1571
|
+
let actionResult;
|
|
1572
|
+
for (let i = 0; i < count; i++) {
|
|
1573
|
+
const kind = kinds[i];
|
|
1574
|
+
let value;
|
|
1575
|
+
if (kind === TERMINAL || kind === LABELLED_TERMINAL) {
|
|
1576
|
+
const token = state.tokens[state.idx];
|
|
1577
|
+
if (token !== void 0 && token.type === tokens[i]) {
|
|
1578
|
+
state.idx++;
|
|
1579
|
+
value = token;
|
|
1580
|
+
} else {
|
|
1581
|
+
expect(state, tokens[i]);
|
|
1582
|
+
state.idx = start;
|
|
1583
|
+
if (needsScope) state.bindings = previousBindings;
|
|
1584
|
+
return FAIL;
|
|
1585
|
+
}
|
|
1586
|
+
if (kind === LABELLED_TERMINAL) bindings[names[i]] = value;
|
|
1587
|
+
} else if (kind === ACTION) {
|
|
1588
|
+
actionResult = fns[i](bindings, state);
|
|
1589
|
+
continue;
|
|
1590
|
+
} else {
|
|
1591
|
+
value = fns[i](state);
|
|
1592
|
+
if (value === FAIL) {
|
|
1593
|
+
state.idx = start;
|
|
1594
|
+
if (needsScope) state.bindings = previousBindings;
|
|
1595
|
+
return FAIL;
|
|
1596
|
+
}
|
|
1597
|
+
if (kind === LABELLED) bindings[names[i]] = value;
|
|
1598
|
+
}
|
|
1599
|
+
if (results !== null) results.push(value);
|
|
1600
|
+
}
|
|
1601
|
+
if (needsScope) state.bindings = previousBindings;
|
|
1602
|
+
return hasAction ? actionResult : results;
|
|
1603
|
+
};
|
|
1604
|
+
}
|
|
1605
|
+
function planAlt(branches) {
|
|
1606
|
+
if (!Array.isArray(branches)) throw new Error("'alt' expects an array of branches");
|
|
1607
|
+
const planned = branches.map(plan);
|
|
1608
|
+
const table = firsts ? buildDispatch(branches, firsts) : null;
|
|
1609
|
+
if (table !== null) {
|
|
1610
|
+
const byToken = /* @__PURE__ */ Object.create(null);
|
|
1611
|
+
for (const [token, branch] of table) byToken[token] = planned[branches.indexOf(branch)];
|
|
1612
|
+
const expectedTokens = [...table.keys()];
|
|
1613
|
+
const expectedCount = expectedTokens.length;
|
|
1614
|
+
return (state) => {
|
|
1615
|
+
tick(state);
|
|
1616
|
+
const token = state.tokens[state.idx];
|
|
1617
|
+
if (token !== void 0) {
|
|
1618
|
+
const branch = byToken[token.type];
|
|
1619
|
+
if (branch !== void 0) return branch(state);
|
|
1620
|
+
}
|
|
1621
|
+
for (let i = 0; i < expectedCount; i++) expect(state, expectedTokens[i]);
|
|
1622
|
+
return FAIL;
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
const count = planned.length;
|
|
1626
|
+
return (state) => {
|
|
1627
|
+
tick(state);
|
|
1628
|
+
const start = state.idx;
|
|
1629
|
+
for (let i = 0; i < count; i++) {
|
|
1630
|
+
const value = planned[i](state);
|
|
1631
|
+
if (value !== FAIL) return value;
|
|
1632
|
+
state.idx = start;
|
|
1633
|
+
}
|
|
1634
|
+
return FAIL;
|
|
1635
|
+
};
|
|
1636
|
+
}
|
|
1637
|
+
function planSeparated(arg, atLeastOne) {
|
|
1638
|
+
if (!arg || typeof arg !== "object") throw new Error("'manySep' expects { rule, sep }");
|
|
1639
|
+
const { rule, sep, trailing = false } = arg;
|
|
1640
|
+
const item = plan(rule);
|
|
1641
|
+
const sepToken = typeof sep === "string" ? sep : null;
|
|
1642
|
+
const separator = sepToken === null ? plan(sep) : null;
|
|
1643
|
+
return (state) => {
|
|
1644
|
+
tick(state);
|
|
1645
|
+
const start = state.idx;
|
|
1646
|
+
const first = item(state);
|
|
1647
|
+
if (first === FAIL) {
|
|
1648
|
+
state.idx = start;
|
|
1649
|
+
return atLeastOne ? FAIL : [];
|
|
1650
|
+
}
|
|
1651
|
+
const results = [first];
|
|
1652
|
+
for (; ; ) {
|
|
1653
|
+
const beforeSep = state.idx;
|
|
1654
|
+
if (sepToken !== null) {
|
|
1655
|
+
const token = state.tokens[state.idx];
|
|
1656
|
+
if (token === void 0 || token.type !== sepToken) {
|
|
1657
|
+
expect(state, sepToken);
|
|
1658
|
+
return results;
|
|
1659
|
+
}
|
|
1660
|
+
state.idx++;
|
|
1661
|
+
} else if (separator(state) === FAIL) {
|
|
1662
|
+
state.idx = beforeSep;
|
|
1663
|
+
return results;
|
|
1664
|
+
}
|
|
1665
|
+
const beforeItem = state.idx;
|
|
1666
|
+
const value = item(state);
|
|
1667
|
+
if (value === FAIL) {
|
|
1668
|
+
state.idx = trailing ? beforeItem : beforeSep;
|
|
1669
|
+
return results;
|
|
1670
|
+
}
|
|
1671
|
+
results.push(value);
|
|
1672
|
+
}
|
|
1673
|
+
};
|
|
1674
|
+
}
|
|
1675
|
+
return {
|
|
1676
|
+
plan,
|
|
1677
|
+
planOp,
|
|
1678
|
+
planAction,
|
|
1679
|
+
planRule: (name) => slotFor(name),
|
|
1680
|
+
/** Wraps a plan so json-logic-engine's compiler can embed it. */
|
|
1681
|
+
declareSync
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
// src/methods.js
|
|
1686
|
+
var OPERATORS = [
|
|
1687
|
+
"consume",
|
|
1688
|
+
"epsilon",
|
|
1689
|
+
"seq",
|
|
1690
|
+
"alt",
|
|
1691
|
+
"oneOf",
|
|
1692
|
+
"eof",
|
|
1693
|
+
"many",
|
|
1694
|
+
"many1",
|
|
1695
|
+
"option",
|
|
1696
|
+
"manySep",
|
|
1697
|
+
"many1Sep",
|
|
1698
|
+
"infixLeft",
|
|
1699
|
+
"infixRight",
|
|
1700
|
+
"prefix",
|
|
1701
|
+
"postfix",
|
|
1702
|
+
"between",
|
|
1703
|
+
"as",
|
|
1704
|
+
"subrule",
|
|
1705
|
+
"label",
|
|
1706
|
+
"action",
|
|
1707
|
+
"lookahead",
|
|
1708
|
+
"negLookahead",
|
|
1709
|
+
"text"
|
|
1710
|
+
];
|
|
1711
|
+
var plannerOf = (engine) => {
|
|
1712
|
+
const context = engine.__jlGrammar;
|
|
1713
|
+
if (!context) throw new Error("grammar combinators require a parser built by createParser");
|
|
1714
|
+
return context.planner;
|
|
1715
|
+
};
|
|
1716
|
+
var compileVia = (op) => (logic, buildState) => {
|
|
1717
|
+
const context = buildState.engine.__jlGrammar;
|
|
1718
|
+
if (!context) return false;
|
|
1719
|
+
return buildState.compile`${declareSync(context.planner.planOp(op, logic))}(context)`;
|
|
1720
|
+
};
|
|
1721
|
+
var combinator = (op) => ({
|
|
1722
|
+
lazy: true,
|
|
1723
|
+
deterministic: false,
|
|
1724
|
+
compile: compileVia(op),
|
|
1725
|
+
method: (logic, state, above, engine) => plannerOf(engine).planOp(op, logic)(state)
|
|
1726
|
+
});
|
|
1727
|
+
var createMethods = () => Object.fromEntries(OPERATORS.map((op) => [op, combinator(op)]));
|
|
1728
|
+
|
|
1729
|
+
// src/values.js
|
|
1730
|
+
var badArity = (length) => new Error(`obj expects an even number of arguments (key, value, ...), received ${length}`);
|
|
1731
|
+
var objectFrom = (args) => {
|
|
1732
|
+
if (!Array.isArray(args) || args.length === 0 || args.length % 2 !== 0) {
|
|
1733
|
+
throw badArity(Array.isArray(args) ? args.length : 1);
|
|
1734
|
+
}
|
|
1735
|
+
const result = {};
|
|
1736
|
+
for (let i = 0; i < args.length; i += 2) result[args[i]] = args[i + 1];
|
|
1737
|
+
return result;
|
|
1738
|
+
};
|
|
1739
|
+
var compileObject = (args, buildState) => {
|
|
1740
|
+
if (!Array.isArray(args) || args.length === 0 || args.length % 2 !== 0) return false;
|
|
1741
|
+
const strings = ["({["];
|
|
1742
|
+
const items = [];
|
|
1743
|
+
for (let i = 0; i < args.length; i += 2) {
|
|
1744
|
+
items.push(args[i]);
|
|
1745
|
+
strings.push("]: ");
|
|
1746
|
+
items.push(args[i + 1]);
|
|
1747
|
+
strings.push(i + 2 < args.length ? ", [" : "})");
|
|
1748
|
+
}
|
|
1749
|
+
return buildState.compile(strings, ...items);
|
|
1750
|
+
};
|
|
1751
|
+
var createValueMethods = () => ({
|
|
1752
|
+
obj: { method: objectFrom, compile: compileObject }
|
|
1753
|
+
});
|
|
1754
|
+
|
|
1755
|
+
// src/codegen.js
|
|
1756
|
+
function createCodegen(config) {
|
|
1757
|
+
const { planner, rules, firsts, memo, maxSteps } = config;
|
|
1758
|
+
const HELPERS = [];
|
|
1759
|
+
const ORIGINS = [];
|
|
1760
|
+
const RULES = [];
|
|
1761
|
+
const SOURCES = [];
|
|
1762
|
+
const ruleIndex = /* @__PURE__ */ new Map();
|
|
1763
|
+
const pending = [];
|
|
1764
|
+
let counter = 0;
|
|
1765
|
+
const nextId = () => "_" + counter++;
|
|
1766
|
+
function helper(value, origin) {
|
|
1767
|
+
HELPERS.push(value);
|
|
1768
|
+
ORIGINS.push(origin);
|
|
1769
|
+
return `H[${HELPERS.length - 1}]`;
|
|
1770
|
+
}
|
|
1771
|
+
const actionHelper = (logic) => helper(planner.planAction(logic), { kind: "action", logic });
|
|
1772
|
+
const constantHelper = (node) => helper(planner.plan(node), { kind: "constant", node });
|
|
1773
|
+
function indexOfRule(name) {
|
|
1774
|
+
let index = ruleIndex.get(name);
|
|
1775
|
+
if (index !== void 0) return index;
|
|
1776
|
+
index = RULES.length;
|
|
1777
|
+
ruleIndex.set(name, index);
|
|
1778
|
+
RULES.push(null);
|
|
1779
|
+
pending.push(name);
|
|
1780
|
+
return index;
|
|
1781
|
+
}
|
|
1782
|
+
const guard = maxSteps > 0;
|
|
1783
|
+
const tickSrc = guard ? `if (++c.steps > ${maxSteps}) T();` : "";
|
|
1784
|
+
function gen(node, out, bindingsVar) {
|
|
1785
|
+
const op = opOf(node);
|
|
1786
|
+
const result = nextId();
|
|
1787
|
+
if (op === null) {
|
|
1788
|
+
out.push(`const ${result} = ${constantHelper(node)}(c);`);
|
|
1789
|
+
return result;
|
|
1790
|
+
}
|
|
1791
|
+
const arg = node[op];
|
|
1792
|
+
switch (op) {
|
|
1793
|
+
case "consume": {
|
|
1794
|
+
const name = typeof arg === "string" ? arg : arg[0];
|
|
1795
|
+
const token = nextId();
|
|
1796
|
+
out.push(
|
|
1797
|
+
`let ${result};`,
|
|
1798
|
+
`const ${token} = c.tokens[c.idx];`,
|
|
1799
|
+
`if (${token} !== undefined && ${token}.type === ${JSON.stringify(name)}) { c.idx++; ${result} = ${token}; }`,
|
|
1800
|
+
`else { E(c, ${JSON.stringify(name)}); ${result} = F; }`
|
|
1801
|
+
);
|
|
1802
|
+
return result;
|
|
1803
|
+
}
|
|
1804
|
+
case "epsilon":
|
|
1805
|
+
out.push(`const ${result} = null;`);
|
|
1806
|
+
return result;
|
|
1807
|
+
case "label": {
|
|
1808
|
+
const value = gen(arg[1], out, bindingsVar);
|
|
1809
|
+
if (bindingsVar) out.push(`if (${value} !== F) ${bindingsVar}[${JSON.stringify(arg[0])}] = ${value};`);
|
|
1810
|
+
else out.push(`if (${value} !== F && c.bindings) c.bindings[${JSON.stringify(arg[0])}] = ${value};`);
|
|
1811
|
+
out.push(`const ${result} = ${value};`);
|
|
1812
|
+
return result;
|
|
1813
|
+
}
|
|
1814
|
+
case "action": {
|
|
1815
|
+
const fn = actionHelper(arg);
|
|
1816
|
+
out.push(`const ${result} = ${fn}(${bindingsVar || "c.bindings || {}"}, c);`);
|
|
1817
|
+
return result;
|
|
1818
|
+
}
|
|
1819
|
+
case "seq":
|
|
1820
|
+
return genSeq(arg, out, result);
|
|
1821
|
+
case "alt":
|
|
1822
|
+
return genAlt(arg, out, result, bindingsVar);
|
|
1823
|
+
case "option": {
|
|
1824
|
+
const start = nextId();
|
|
1825
|
+
out.push(`let ${result};`, `const ${start} = c.idx;`, "{");
|
|
1826
|
+
const value = gen(arg, out, bindingsVar);
|
|
1827
|
+
out.push(
|
|
1828
|
+
`if (${value} === F) { c.idx = ${start}; ${result} = null; } else ${result} = ${value};`,
|
|
1829
|
+
"}"
|
|
1830
|
+
);
|
|
1831
|
+
return result;
|
|
1832
|
+
}
|
|
1833
|
+
case "many":
|
|
1834
|
+
case "many1": {
|
|
1835
|
+
const atLeastOne = op === "many1";
|
|
1836
|
+
const before = nextId();
|
|
1837
|
+
const label2 = "L" + nextId();
|
|
1838
|
+
out.push(tickSrc, `let ${result} = [];`, `${label2}: {`);
|
|
1839
|
+
if (atLeastOne) {
|
|
1840
|
+
const start = nextId();
|
|
1841
|
+
out.push(`const ${start} = c.idx;`);
|
|
1842
|
+
const first = gen(arg, out, bindingsVar);
|
|
1843
|
+
out.push(
|
|
1844
|
+
`if (${first} === F) { c.idx = ${start}; ${result} = F; break ${label2}; }`,
|
|
1845
|
+
`if (c.idx === ${start}) P(${JSON.stringify(op)}, ${start});`,
|
|
1846
|
+
`${result}.push(${first});`
|
|
1847
|
+
);
|
|
1848
|
+
}
|
|
1849
|
+
out.push("for (;;) {", `const ${before} = c.idx;`);
|
|
1850
|
+
const value = gen(arg, out, bindingsVar);
|
|
1851
|
+
out.push(
|
|
1852
|
+
`if (${value} === F) { c.idx = ${before}; break; }`,
|
|
1853
|
+
`if (c.idx === ${before}) P(${JSON.stringify(op)}, ${before});`,
|
|
1854
|
+
`${result}.push(${value});`,
|
|
1855
|
+
"}",
|
|
1856
|
+
"}"
|
|
1857
|
+
);
|
|
1858
|
+
return result;
|
|
1859
|
+
}
|
|
1860
|
+
case "infixLeft": {
|
|
1861
|
+
const label2 = "L" + nextId();
|
|
1862
|
+
const left = nextId();
|
|
1863
|
+
const bindings = nextId();
|
|
1864
|
+
const mark = nextId();
|
|
1865
|
+
const combine = actionHelper(arg.combine);
|
|
1866
|
+
out.push(tickSrc, `let ${result};`, `${label2}: {`);
|
|
1867
|
+
const first = gen(arg.operand, out, bindingsVar);
|
|
1868
|
+
out.push(
|
|
1869
|
+
`if (${first} === F) { ${result} = F; break ${label2}; }`,
|
|
1870
|
+
`let ${left} = ${first};`,
|
|
1871
|
+
`let ${bindings} = null;`,
|
|
1872
|
+
"for (;;) {",
|
|
1873
|
+
`const ${mark} = c.idx;`
|
|
1874
|
+
);
|
|
1875
|
+
let operatorVar = "null";
|
|
1876
|
+
if (arg.operator !== void 0) {
|
|
1877
|
+
operatorVar = gen(arg.operator, out, bindingsVar);
|
|
1878
|
+
out.push(`if (${operatorVar} === F) { c.idx = ${mark}; break; }`);
|
|
1879
|
+
}
|
|
1880
|
+
const right = gen(arg.operand, out, bindingsVar);
|
|
1881
|
+
out.push(
|
|
1882
|
+
`if (${right} === F) { c.idx = ${mark}; break; }`,
|
|
1883
|
+
`if (${bindings} === null) ${bindings} = { left: null, op: null, right: null };`,
|
|
1884
|
+
`${bindings}.left = ${left}; ${bindings}.op = ${operatorVar}; ${bindings}.right = ${right};`,
|
|
1885
|
+
`${left} = ${combine}(${bindings}, c);`,
|
|
1886
|
+
"}",
|
|
1887
|
+
`${result} = ${left};`,
|
|
1888
|
+
"}"
|
|
1889
|
+
);
|
|
1890
|
+
return result;
|
|
1891
|
+
}
|
|
1892
|
+
case "oneOf": {
|
|
1893
|
+
const names = Array.isArray(arg) ? arg : [arg];
|
|
1894
|
+
const token = nextId();
|
|
1895
|
+
const test = names.map((n) => `${token}.type === ${JSON.stringify(n)}`).join(" || ");
|
|
1896
|
+
out.push(
|
|
1897
|
+
`let ${result};`,
|
|
1898
|
+
`const ${token} = c.tokens[c.idx];`,
|
|
1899
|
+
`if (${token} !== undefined && (${test})) { c.idx++; ${result} = ${token}; }`,
|
|
1900
|
+
`else { ${names.map((n) => `E(c, ${JSON.stringify(n)});`).join(" ")} ${result} = F; }`
|
|
1901
|
+
);
|
|
1902
|
+
return result;
|
|
1903
|
+
}
|
|
1904
|
+
case "eof":
|
|
1905
|
+
out.push(
|
|
1906
|
+
`let ${result};`,
|
|
1907
|
+
`if (c.idx >= c.tokens.length) ${result} = null;`,
|
|
1908
|
+
`else { E(c, "end of input"); ${result} = F; }`
|
|
1909
|
+
);
|
|
1910
|
+
return result;
|
|
1911
|
+
case "infixRight": {
|
|
1912
|
+
const label2 = "L" + nextId();
|
|
1913
|
+
const left = nextId();
|
|
1914
|
+
const items = nextId();
|
|
1915
|
+
const mark = nextId();
|
|
1916
|
+
const bindings = nextId();
|
|
1917
|
+
const value = nextId();
|
|
1918
|
+
const combine = actionHelper(arg.combine);
|
|
1919
|
+
out.push(tickSrc, `let ${result};`, `${label2}: {`);
|
|
1920
|
+
const first = gen(arg.operand, out, bindingsVar);
|
|
1921
|
+
out.push(
|
|
1922
|
+
`if (${first} === F) { ${result} = F; break ${label2}; }`,
|
|
1923
|
+
`const ${left} = ${first};`,
|
|
1924
|
+
`let ${items} = null;`,
|
|
1925
|
+
"for (;;) {",
|
|
1926
|
+
`const ${mark} = c.idx;`
|
|
1927
|
+
);
|
|
1928
|
+
let operatorVar = "null";
|
|
1929
|
+
if (arg.operator !== void 0) {
|
|
1930
|
+
operatorVar = gen(arg.operator, out, bindingsVar);
|
|
1931
|
+
out.push(`if (${operatorVar} === F) { c.idx = ${mark}; break; }`);
|
|
1932
|
+
}
|
|
1933
|
+
const right = gen(arg.operand, out, bindingsVar);
|
|
1934
|
+
out.push(
|
|
1935
|
+
`if (${right} === F) { c.idx = ${mark}; break; }`,
|
|
1936
|
+
`if (${items} === null) ${items} = [];`,
|
|
1937
|
+
`${items}.push(${operatorVar}, ${right});`,
|
|
1938
|
+
"}",
|
|
1939
|
+
`if (${items} === null) { ${result} = ${left}; break ${label2}; }`,
|
|
1940
|
+
`const ${bindings} = { left: null, op: null, right: null };`,
|
|
1941
|
+
`let ${value} = ${items}[${items}.length - 1];`,
|
|
1942
|
+
`for (let i = ${items}.length - 2; i >= 0; i -= 2) {`,
|
|
1943
|
+
`${bindings}.left = i === 0 ? ${left} : ${items}[i - 1];`,
|
|
1944
|
+
`${bindings}.op = ${items}[i]; ${bindings}.right = ${value};`,
|
|
1945
|
+
`${value} = ${combine}(${bindings}, c);`,
|
|
1946
|
+
"}",
|
|
1947
|
+
`${result} = ${value};`,
|
|
1948
|
+
"}"
|
|
1949
|
+
);
|
|
1950
|
+
return result;
|
|
1951
|
+
}
|
|
1952
|
+
case "lookahead": {
|
|
1953
|
+
const start = nextId();
|
|
1954
|
+
out.push(`const ${start} = c.idx;`);
|
|
1955
|
+
const value = gen(arg, out, bindingsVar);
|
|
1956
|
+
out.push(`c.idx = ${start};`, `const ${result} = ${value};`);
|
|
1957
|
+
return result;
|
|
1958
|
+
}
|
|
1959
|
+
case "negLookahead": {
|
|
1960
|
+
const start = nextId();
|
|
1961
|
+
out.push(`const ${start} = c.idx;`);
|
|
1962
|
+
const value = gen(arg, out, bindingsVar);
|
|
1963
|
+
out.push(`c.idx = ${start};`, `const ${result} = ${value} === F ? null : F;`);
|
|
1964
|
+
return result;
|
|
1965
|
+
}
|
|
1966
|
+
case "text": {
|
|
1967
|
+
const label2 = "L" + nextId();
|
|
1968
|
+
const start = nextId();
|
|
1969
|
+
const first = nextId();
|
|
1970
|
+
const last = nextId();
|
|
1971
|
+
out.push(`let ${result};`, `${label2}: {`, `const ${start} = c.idx;`);
|
|
1972
|
+
const value = gen(arg, out, bindingsVar);
|
|
1973
|
+
out.push(
|
|
1974
|
+
`if (${value} === F) { c.idx = ${start}; ${result} = F; break ${label2}; }`,
|
|
1975
|
+
`if (c.idx === ${start}) { ${result} = ''; break ${label2}; }`,
|
|
1976
|
+
`const ${first} = c.tokens[${start}];`,
|
|
1977
|
+
`const ${last} = c.tokens[c.idx - 1];`,
|
|
1978
|
+
`if (c.source !== undefined) { ${result} = c.source.slice(${first}.start, ${last}.end); break ${label2}; }`,
|
|
1979
|
+
`let ${result}_s = '';`,
|
|
1980
|
+
`for (let i = ${start}; i < c.idx; i++) ${result}_s += c.tokens[i].image;`,
|
|
1981
|
+
`${result} = ${result}_s;`,
|
|
1982
|
+
"}"
|
|
1983
|
+
);
|
|
1984
|
+
return result;
|
|
1985
|
+
}
|
|
1986
|
+
case "as": {
|
|
1987
|
+
const transform = actionHelper(Array.isArray(arg) ? arg[1] : arg.to);
|
|
1988
|
+
const value = gen(Array.isArray(arg) ? arg[0] : arg.parser, out, bindingsVar);
|
|
1989
|
+
out.push(`const ${result} = ${value} === F ? F : ${transform}(${value}, c);`);
|
|
1990
|
+
return result;
|
|
1991
|
+
}
|
|
1992
|
+
case "between": {
|
|
1993
|
+
const label2 = "L" + nextId();
|
|
1994
|
+
const start = nextId();
|
|
1995
|
+
out.push(tickSrc, `let ${result};`, `${label2}: {`, `const ${start} = c.idx;`);
|
|
1996
|
+
const open = gen(arg.open, out, bindingsVar);
|
|
1997
|
+
out.push(`if (${open} === F) { c.idx = ${start}; ${result} = F; break ${label2}; }`);
|
|
1998
|
+
const body = gen(arg.body, out, bindingsVar);
|
|
1999
|
+
out.push(`if (${body} === F) { c.idx = ${start}; ${result} = F; break ${label2}; }`);
|
|
2000
|
+
const close = gen(arg.close, out, bindingsVar);
|
|
2001
|
+
out.push(
|
|
2002
|
+
`if (${close} === F) { c.idx = ${start}; ${result} = F; break ${label2}; }`,
|
|
2003
|
+
`${result} = ${body};`,
|
|
2004
|
+
"}"
|
|
2005
|
+
);
|
|
2006
|
+
return result;
|
|
2007
|
+
}
|
|
2008
|
+
case "prefix": {
|
|
2009
|
+
const label2 = "L" + nextId();
|
|
2010
|
+
const start = nextId();
|
|
2011
|
+
const ops = nextId();
|
|
2012
|
+
const mark = nextId();
|
|
2013
|
+
const bindings = nextId();
|
|
2014
|
+
const value = nextId();
|
|
2015
|
+
const combine = actionHelper(arg.combine);
|
|
2016
|
+
out.push(tickSrc, `let ${result};`, `${label2}: {`, `const ${start} = c.idx;`, `let ${ops} = null;`, "for (;;) {", `const ${mark} = c.idx;`);
|
|
2017
|
+
const op2 = gen(arg.operator, out, bindingsVar);
|
|
2018
|
+
out.push(
|
|
2019
|
+
`if (${op2} === F) { c.idx = ${mark}; break; }`,
|
|
2020
|
+
`if (${ops} === null) ${ops} = [];`,
|
|
2021
|
+
`${ops}.push(${op2});`,
|
|
2022
|
+
"}"
|
|
2023
|
+
);
|
|
2024
|
+
const operand = gen(arg.operand, out, bindingsVar);
|
|
2025
|
+
out.push(
|
|
2026
|
+
`if (${operand} === F) { c.idx = ${start}; ${result} = F; break ${label2}; }`,
|
|
2027
|
+
`let ${value} = ${operand};`,
|
|
2028
|
+
`if (${ops} !== null) {`,
|
|
2029
|
+
`const ${bindings} = { op: null, operand: null };`,
|
|
2030
|
+
`for (let i = ${ops}.length - 1; i >= 0; i--) { ${bindings}.op = ${ops}[i]; ${bindings}.operand = ${value}; ${value} = ${combine}(${bindings}, c); }`,
|
|
2031
|
+
"}",
|
|
2032
|
+
`${result} = ${value};`,
|
|
2033
|
+
"}"
|
|
2034
|
+
);
|
|
2035
|
+
return result;
|
|
2036
|
+
}
|
|
2037
|
+
case "postfix": {
|
|
2038
|
+
const label2 = "L" + nextId();
|
|
2039
|
+
const left = nextId();
|
|
2040
|
+
const bindings = nextId();
|
|
2041
|
+
const mark = nextId();
|
|
2042
|
+
const combine = actionHelper(arg.combine);
|
|
2043
|
+
out.push(tickSrc, `let ${result};`, `${label2}: {`);
|
|
2044
|
+
const first = gen(arg.operand, out, bindingsVar);
|
|
2045
|
+
out.push(
|
|
2046
|
+
`if (${first} === F) { ${result} = F; break ${label2}; }`,
|
|
2047
|
+
`let ${left} = ${first};`,
|
|
2048
|
+
`let ${bindings} = null;`,
|
|
2049
|
+
"for (;;) {",
|
|
2050
|
+
`const ${mark} = c.idx;`
|
|
2051
|
+
);
|
|
2052
|
+
const found = gen(arg.suffix, out, bindingsVar);
|
|
2053
|
+
out.push(
|
|
2054
|
+
`if (${found} === F) { c.idx = ${mark}; break; }`,
|
|
2055
|
+
`if (${bindings} === null) ${bindings} = { left: null, suffix: null };`,
|
|
2056
|
+
`${bindings}.left = ${left}; ${bindings}.suffix = ${found};`,
|
|
2057
|
+
`${left} = ${combine}(${bindings}, c);`,
|
|
2058
|
+
"}",
|
|
2059
|
+
`${result} = ${left};`,
|
|
2060
|
+
"}"
|
|
2061
|
+
);
|
|
2062
|
+
return result;
|
|
2063
|
+
}
|
|
2064
|
+
case "manySep":
|
|
2065
|
+
case "many1Sep": {
|
|
2066
|
+
if (!arg || typeof arg !== "object") throw new Error("'manySep' expects { rule, sep }");
|
|
2067
|
+
const atLeastOne = op === "many1Sep";
|
|
2068
|
+
const sepNode = typeof arg.sep === "string" ? { consume: arg.sep } : arg.sep;
|
|
2069
|
+
const trailing = arg.trailing === true;
|
|
2070
|
+
const start = nextId();
|
|
2071
|
+
const beforeSep = nextId();
|
|
2072
|
+
const beforeItem = nextId();
|
|
2073
|
+
const label2 = "L" + nextId();
|
|
2074
|
+
out.push(tickSrc, `let ${result} = [];`, `${label2}: {`, `const ${start} = c.idx;`);
|
|
2075
|
+
const first = gen(arg.rule, out, bindingsVar);
|
|
2076
|
+
out.push(
|
|
2077
|
+
`if (${first} === F) { c.idx = ${start}; ${result} = ${atLeastOne ? "F" : "[]"}; break ${label2}; }`,
|
|
2078
|
+
`${result}.push(${first});`,
|
|
2079
|
+
"for (;;) {",
|
|
2080
|
+
`const ${beforeSep} = c.idx;`
|
|
2081
|
+
);
|
|
2082
|
+
const sep = gen(sepNode, out, bindingsVar);
|
|
2083
|
+
out.push(`if (${sep} === F) { c.idx = ${beforeSep}; break; }`, `const ${beforeItem} = c.idx;`);
|
|
2084
|
+
const item = gen(arg.rule, out, bindingsVar);
|
|
2085
|
+
out.push(
|
|
2086
|
+
`if (${item} === F) { c.idx = ${trailing ? beforeItem : beforeSep}; break; }`,
|
|
2087
|
+
`${result}.push(${item});`,
|
|
2088
|
+
"}",
|
|
2089
|
+
"}"
|
|
2090
|
+
);
|
|
2091
|
+
return result;
|
|
2092
|
+
}
|
|
2093
|
+
case "subrule": {
|
|
2094
|
+
const name = typeof arg === "string" ? arg : arg[0];
|
|
2095
|
+
if (!(name in rules)) {
|
|
2096
|
+
out.push(`const ${result} = ${helper(planner.plan(node), { kind: "unknownRule", name })}(c);`);
|
|
2097
|
+
return result;
|
|
2098
|
+
}
|
|
2099
|
+
const index = indexOfRule(name);
|
|
2100
|
+
const parent = nextId();
|
|
2101
|
+
out.push(tickSrc);
|
|
2102
|
+
if (memo) {
|
|
2103
|
+
const key = nextId();
|
|
2104
|
+
const hit = nextId();
|
|
2105
|
+
const start = nextId();
|
|
2106
|
+
out.push(
|
|
2107
|
+
`let ${result};`,
|
|
2108
|
+
`const ${key} = ${JSON.stringify(name + ":")} + c.idx;`,
|
|
2109
|
+
`const ${hit} = c.memo.get(${key});`,
|
|
2110
|
+
`if (${hit} !== undefined) { c.idx = ${hit}.end; ${result} = ${hit}.value; }`,
|
|
2111
|
+
"else {",
|
|
2112
|
+
`const ${start} = c.idx;`,
|
|
2113
|
+
`const ${parent} = c.ruleDepth;`,
|
|
2114
|
+
`if (c.track) { c.ruleNames[${parent}] = ${JSON.stringify(name)}; c.ruleDepth = ${parent} + 1; }`,
|
|
2115
|
+
`${result} = R[${index}](c);`,
|
|
2116
|
+
`if (c.track) c.ruleDepth = ${parent};`,
|
|
2117
|
+
`if (${result} === F) c.idx = ${start};`,
|
|
2118
|
+
`c.memo.set(${key}, { value: ${result}, end: c.idx });`,
|
|
2119
|
+
"}"
|
|
2120
|
+
);
|
|
2121
|
+
} else {
|
|
2122
|
+
out.push(
|
|
2123
|
+
`const ${parent} = c.ruleDepth;`,
|
|
2124
|
+
`if (c.track) { c.ruleNames[${parent}] = ${JSON.stringify(name)}; c.ruleDepth = ${parent} + 1; }`,
|
|
2125
|
+
`const ${result} = R[${index}](c);`,
|
|
2126
|
+
`if (c.track) c.ruleDepth = ${parent};`
|
|
2127
|
+
);
|
|
2128
|
+
}
|
|
2129
|
+
return result;
|
|
2130
|
+
}
|
|
2131
|
+
default:
|
|
2132
|
+
out.push(`const ${result} = ${helper(planner.plan(node))}(c);`);
|
|
2133
|
+
return result;
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
function genSeq(children, out, result) {
|
|
2137
|
+
if (!Array.isArray(children)) throw new Error("'seq' expects an array of parsers");
|
|
2138
|
+
const idiom = recognizeSeqIdiom(children);
|
|
2139
|
+
if (idiom !== null) return gen(idiom, out, null);
|
|
2140
|
+
let hasLabels = false;
|
|
2141
|
+
let hasAction = false;
|
|
2142
|
+
for (const child of children) {
|
|
2143
|
+
const childOp = opOf(child);
|
|
2144
|
+
if (childOp === "label") hasLabels = true;
|
|
2145
|
+
else if (childOp === "action") hasAction = true;
|
|
2146
|
+
}
|
|
2147
|
+
const needsScope = hasLabels || hasAction;
|
|
2148
|
+
const start = nextId();
|
|
2149
|
+
const label2 = "L" + nextId();
|
|
2150
|
+
const bindingsVar = needsScope ? nextId() : null;
|
|
2151
|
+
const saved = nextId();
|
|
2152
|
+
const collected = [];
|
|
2153
|
+
out.push(tickSrc, `let ${result};`, `${label2}: {`, `const ${start} = c.idx;`);
|
|
2154
|
+
if (needsScope) {
|
|
2155
|
+
out.push(`const ${saved} = c.bindings;`, `const ${bindingsVar} = {};`, `c.bindings = ${bindingsVar};`);
|
|
2156
|
+
}
|
|
2157
|
+
let actionResult = null;
|
|
2158
|
+
for (const child of children) {
|
|
2159
|
+
const childOp = opOf(child);
|
|
2160
|
+
if (childOp === "action") {
|
|
2161
|
+
actionResult = gen(child, out, bindingsVar);
|
|
2162
|
+
continue;
|
|
2163
|
+
}
|
|
2164
|
+
const value = gen(child, out, bindingsVar);
|
|
2165
|
+
out.push(
|
|
2166
|
+
`if (${value} === F) { c.idx = ${start};` + (needsScope ? ` c.bindings = ${saved};` : "") + ` ${result} = F; break ${label2}; }`
|
|
2167
|
+
);
|
|
2168
|
+
collected.push(value);
|
|
2169
|
+
}
|
|
2170
|
+
if (needsScope) out.push(`c.bindings = ${saved};`);
|
|
2171
|
+
out.push(`${result} = ${hasAction ? actionResult : "[" + collected.join(", ") + "]"};`, "}");
|
|
2172
|
+
return result;
|
|
2173
|
+
}
|
|
2174
|
+
function genAlt(branches, out, result, bindingsVar) {
|
|
2175
|
+
if (!Array.isArray(branches)) throw new Error("'alt' expects an array of branches");
|
|
2176
|
+
const table = firsts ? buildDispatch(branches, firsts) : null;
|
|
2177
|
+
const label2 = "L" + nextId();
|
|
2178
|
+
if (table !== null) {
|
|
2179
|
+
const expected = [...table.keys()];
|
|
2180
|
+
const token = nextId();
|
|
2181
|
+
out.push(tickSrc, `let ${result};`, `${label2}: {`, `const ${token} = c.tokens[c.idx];`);
|
|
2182
|
+
out.push(`if (${token} === undefined) { ${expected.map((t) => `E(c, ${JSON.stringify(t)});`).join(" ")} ${result} = F; break ${label2}; }`);
|
|
2183
|
+
out.push(`switch (${token}.type) {`);
|
|
2184
|
+
const byBranch = /* @__PURE__ */ new Map();
|
|
2185
|
+
for (const [tokenName, branch] of table) {
|
|
2186
|
+
if (!byBranch.has(branch)) byBranch.set(branch, []);
|
|
2187
|
+
byBranch.get(branch).push(tokenName);
|
|
2188
|
+
}
|
|
2189
|
+
for (const [branch, tokenNames] of byBranch) {
|
|
2190
|
+
out.push(tokenNames.map((t) => `case ${JSON.stringify(t)}:`).join(" ") + " {");
|
|
2191
|
+
const value = gen(branch, out, bindingsVar);
|
|
2192
|
+
out.push(`${result} = ${value}; break ${label2};`, "}");
|
|
2193
|
+
}
|
|
2194
|
+
out.push(
|
|
2195
|
+
`default: { ${expected.map((t) => `E(c, ${JSON.stringify(t)});`).join(" ")} ${result} = F; break ${label2}; }`,
|
|
2196
|
+
"}",
|
|
2197
|
+
"}"
|
|
2198
|
+
);
|
|
2199
|
+
return result;
|
|
2200
|
+
}
|
|
2201
|
+
const start = nextId();
|
|
2202
|
+
out.push(tickSrc, `let ${result};`, `${label2}: {`, `const ${start} = c.idx;`);
|
|
2203
|
+
for (const branch of branches) {
|
|
2204
|
+
out.push("{");
|
|
2205
|
+
const value = gen(branch, out, bindingsVar);
|
|
2206
|
+
out.push(`if (${value} !== F) { ${result} = ${value}; break ${label2}; }`, `c.idx = ${start};`, "}");
|
|
2207
|
+
}
|
|
2208
|
+
out.push(`${result} = F;`, "}");
|
|
2209
|
+
return result;
|
|
2210
|
+
}
|
|
2211
|
+
function buildRule(name) {
|
|
2212
|
+
const out = [];
|
|
2213
|
+
const value = gen(rules[name], out, null);
|
|
2214
|
+
const body = `(c) => {
|
|
2215
|
+
${out.join("\n")}
|
|
2216
|
+
return ${value};
|
|
2217
|
+
}`;
|
|
2218
|
+
SOURCES[ruleIndex.get(name)] = body;
|
|
2219
|
+
const source = `return ${body}`;
|
|
2220
|
+
const factory = new Function("F", "E", "P", "T", "R", "H", source);
|
|
2221
|
+
return factory(FAIL, expect, progressError, stepsError, RULES, HELPERS);
|
|
2222
|
+
}
|
|
2223
|
+
function progressError(op, at) {
|
|
2224
|
+
throw new Error(
|
|
2225
|
+
`'${op}' matched without consuming any input at token index ${at}; this would loop forever. Make the repeated parser consume at least one token.`
|
|
2226
|
+
);
|
|
2227
|
+
}
|
|
2228
|
+
function stepsError() {
|
|
2229
|
+
throw new Error(`Parser exceeded maxSteps (${maxSteps}); the grammar is likely looping.`);
|
|
2230
|
+
}
|
|
2231
|
+
function compile(start) {
|
|
2232
|
+
const index = indexOfRule(start);
|
|
2233
|
+
while (pending.length) {
|
|
2234
|
+
const name = pending.shift();
|
|
2235
|
+
RULES[ruleIndex.get(name)] = buildRule(name);
|
|
2236
|
+
}
|
|
2237
|
+
return RULES[index];
|
|
2238
|
+
}
|
|
2239
|
+
function functionFor(name) {
|
|
2240
|
+
const index = ruleIndex.get(name);
|
|
2241
|
+
return index === void 0 ? null : RULES[index];
|
|
2242
|
+
}
|
|
2243
|
+
function sourceFor(name) {
|
|
2244
|
+
const helperCount = HELPERS.length;
|
|
2245
|
+
const pendingCount = pending.length;
|
|
2246
|
+
try {
|
|
2247
|
+
const out = [];
|
|
2248
|
+
const value = gen(rules[name], out, null);
|
|
2249
|
+
return `(c) => {
|
|
2250
|
+
${out.join("\n")}
|
|
2251
|
+
return ${value};
|
|
2252
|
+
}`;
|
|
2253
|
+
} finally {
|
|
2254
|
+
HELPERS.length = helperCount;
|
|
2255
|
+
ORIGINS.length = helperCount;
|
|
2256
|
+
pending.length = pendingCount;
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
return { compile, functionFor, sourceFor, ruleIndex, RULES, SOURCES, ORIGINS, guard };
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
// src/parser.js
|
|
2263
|
+
var EXECUTION_MODES = /* @__PURE__ */ new Set(["generated", "interpreted"]);
|
|
2264
|
+
var GrammarError = class extends Error {
|
|
2265
|
+
/** @param {string[]} problems */
|
|
2266
|
+
constructor(problems) {
|
|
2267
|
+
super(`Invalid grammar:
|
|
2268
|
+
- ${problems.join("\n - ")}`);
|
|
2269
|
+
this.name = "GrammarError";
|
|
2270
|
+
this.problems = problems;
|
|
2271
|
+
}
|
|
2272
|
+
};
|
|
2273
|
+
function createParser(spec, options = {}) {
|
|
2274
|
+
const { tokens: tokenDefs, rules, start } = spec;
|
|
2275
|
+
const {
|
|
2276
|
+
memo = false,
|
|
2277
|
+
strict = true,
|
|
2278
|
+
validate = true,
|
|
2279
|
+
maxSteps = 0,
|
|
2280
|
+
ll1 = true,
|
|
2281
|
+
execution = "generated",
|
|
2282
|
+
positions = "full",
|
|
2283
|
+
methods: extraMethods
|
|
2284
|
+
} = options;
|
|
2285
|
+
const mode = options.execution !== void 0 ? execution : options.compile === false || options.unsafeEval === false ? "interpreted" : "generated";
|
|
2286
|
+
if (!EXECUTION_MODES.has(mode)) {
|
|
2287
|
+
throw new Error(`createParser: \`execution\` must be one of ${[...EXECUTION_MODES].join(", ")}, got ${JSON.stringify(mode)}`);
|
|
2288
|
+
}
|
|
2289
|
+
const compile = mode === "generated";
|
|
2290
|
+
const unsafeEval = mode === "generated";
|
|
2291
|
+
if (!rules || typeof rules !== "object") throw new Error("createParser: `rules` must be an object of rule name -> logic");
|
|
2292
|
+
if (!start) throw new Error("createParser: `start` is required");
|
|
2293
|
+
if (!(start in rules)) throw new Error(`createParser: start rule '${start}' is not defined in rules`);
|
|
2294
|
+
const lexer = createLexer(tokenDefs, { positions });
|
|
2295
|
+
const analysis = analyze(rules, { tokenNames: lexer.tokenNames });
|
|
2296
|
+
if (validate && analysis.errors.length > 0) throw new GrammarError(analysis.errors);
|
|
2297
|
+
const engine = new LogicEngine();
|
|
2298
|
+
for (const [name, method] of Object.entries(createMethods())) engine.addMethod(name, method);
|
|
2299
|
+
for (const [name, method] of Object.entries(createValueMethods())) engine.addMethod(name, method);
|
|
2300
|
+
for (const [name, method] of Object.entries(extraMethods || {})) engine.addMethod(name, method);
|
|
2301
|
+
const startRule = rules[start];
|
|
2302
|
+
const firsts = ll1 ? analysis.firsts : null;
|
|
2303
|
+
const planner = createPlanner({ engine, rules, firsts, memo, maxSteps, unsafeEval });
|
|
2304
|
+
engine.__jlGrammar = { planner };
|
|
2305
|
+
const codegen = compile && unsafeEval ? createCodegen({ planner, rules, firsts, memo, maxSteps }) : null;
|
|
2306
|
+
const compiledStart = codegen && codegen.compile(start);
|
|
2307
|
+
if (codegen && !engine.disableInterpretedOptimization) {
|
|
2308
|
+
for (const name of Object.keys(rules)) {
|
|
2309
|
+
const generated = codegen.functionFor(name);
|
|
2310
|
+
if (generated) engine.optimizedMap.set(rules[name], generated);
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
function parseTokens(tokens, source, track = false) {
|
|
2314
|
+
const state = createState(tokens, rules, { memo, maxSteps, track });
|
|
2315
|
+
state.firsts = firsts;
|
|
2316
|
+
state.source = source;
|
|
2317
|
+
state.positions = lexer.positions;
|
|
2318
|
+
state.ruleNames[0] = start;
|
|
2319
|
+
state.ruleDepth = 1;
|
|
2320
|
+
const result = compiledStart ? compiledStart(state) : engine.run(startRule, state, { above: [] });
|
|
2321
|
+
const trailing = strict && state.idx < tokens.length;
|
|
2322
|
+
if (result !== FAIL && !trailing) return result;
|
|
2323
|
+
if (!track) return parseTokens(tokens, source, true);
|
|
2324
|
+
if (trailing && state.maxIdx < state.idx) {
|
|
2325
|
+
state.maxIdx = state.idx;
|
|
2326
|
+
state.expected = ["end of input"];
|
|
2327
|
+
state.failureStack = state.ruleNames.slice(0, state.ruleDepth);
|
|
2328
|
+
}
|
|
2329
|
+
throw buildParseError(state);
|
|
2330
|
+
}
|
|
2331
|
+
function parse(text) {
|
|
2332
|
+
return parseTokens(lexer.tokenize(text), text);
|
|
2333
|
+
}
|
|
2334
|
+
return {
|
|
2335
|
+
parse,
|
|
2336
|
+
parseTokens,
|
|
2337
|
+
tokenize: lexer.tokenize,
|
|
2338
|
+
lexer,
|
|
2339
|
+
engine,
|
|
2340
|
+
analysis,
|
|
2341
|
+
planner,
|
|
2342
|
+
codegen,
|
|
2343
|
+
execution: mode,
|
|
2344
|
+
compiled: Boolean(compiledStart),
|
|
2345
|
+
/** Rules whose `alt`s resolved to O(1) LL(1) dispatch, for introspection. */
|
|
2346
|
+
get firsts() {
|
|
2347
|
+
return analysis.firsts;
|
|
2348
|
+
}
|
|
2349
|
+
};
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
// src/methodsblock.js
|
|
2353
|
+
var HEADER = /^[ \t]*methods[ \t]*\{/gm;
|
|
2354
|
+
var IMPORT = /^[ \t]*import\b[^\n]*?['"][^'"]+['"][ \t]*;?[ \t]*$/gm;
|
|
2355
|
+
var DECLARED_EXPORT = /^[ \t]*export[ \t]+(?:const|let|var|function|class|async[ \t]+function)[ \t]+([A-Za-z_$][\w$]*)/gm;
|
|
2356
|
+
var EXPORT_LIST = /^[ \t]*export[ \t]*\{([^}]*)\}[ \t]*;?[ \t]*$/gm;
|
|
2357
|
+
function splitMethodsBlock(source) {
|
|
2358
|
+
HEADER.lastIndex = 0;
|
|
2359
|
+
let header = null;
|
|
2360
|
+
for (let match = HEADER.exec(source); match; match = HEADER.exec(source)) header = match;
|
|
2361
|
+
if (!header) return { grammar: source, block: null };
|
|
2362
|
+
const open = header.index + header[0].length;
|
|
2363
|
+
const close = source.lastIndexOf("}");
|
|
2364
|
+
if (close < open) throw new Error("methods block is not closed: no `}` after it");
|
|
2365
|
+
const trailing = source.slice(close + 1).trim();
|
|
2366
|
+
if (trailing) throw new Error(`the methods block must be the last thing in the file; found ${JSON.stringify(trailing.slice(0, 40))} after it`);
|
|
2367
|
+
const blanked = source.slice(header.index, source.length).replace(/[^\n]/g, " ");
|
|
2368
|
+
return { grammar: source.slice(0, header.index) + blanked, block: source.slice(open, close) };
|
|
2369
|
+
}
|
|
2370
|
+
function analyzeMethodsBlock(body) {
|
|
2371
|
+
const names = [];
|
|
2372
|
+
for (const match of body.matchAll(DECLARED_EXPORT)) names.push(match[1]);
|
|
2373
|
+
for (const match of body.matchAll(EXPORT_LIST)) {
|
|
2374
|
+
for (const entry of match[1].split(",")) {
|
|
2375
|
+
const name = entry.trim().split(/\s+as\s+/).pop();
|
|
2376
|
+
if (name) names.push(name);
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
return {
|
|
2380
|
+
names,
|
|
2381
|
+
hasDefault: /^[ \t]*export[ \t]+default\b/m.test(body),
|
|
2382
|
+
imports: (body.match(IMPORT) || []).map((line) => line.trim())
|
|
2383
|
+
};
|
|
2384
|
+
}
|
|
2385
|
+
function asFunctionBody(body, info) {
|
|
2386
|
+
const stripped = body.replace(/^([ \t]*)export[ \t]+default[ \t]+/m, "$1const __default = ").replace(EXPORT_LIST, "").replace(/^([ \t]*)export[ \t]+/gm, "$1");
|
|
2387
|
+
const table = info.hasDefault ? "__default" : `{ ${info.names.join(", ")} }`;
|
|
2388
|
+
return `${stripped}
|
|
2389
|
+
return { methods: ${table}, exports: { ${info.names.join(", ")} } }`;
|
|
2390
|
+
}
|
|
2391
|
+
function evaluateMethodsBlock(body) {
|
|
2392
|
+
const info = analyzeMethodsBlock(body);
|
|
2393
|
+
if (info.imports.length > 0) {
|
|
2394
|
+
throw new Error(
|
|
2395
|
+
`this methods block imports, so it cannot be evaluated in place: ${JSON.stringify(info.imports[0])}. Generate a parser file from it instead (\`grammatik generate\`), or pass \`methods\` explicitly.`
|
|
2396
|
+
);
|
|
2397
|
+
}
|
|
2398
|
+
if (!info.hasDefault && info.names.length === 0) {
|
|
2399
|
+
throw new Error("methods block exports nothing; it needs `export default <table>` or named exports");
|
|
2400
|
+
}
|
|
2401
|
+
return new Function(asFunctionBody(body, info))();
|
|
2402
|
+
}
|
|
2403
|
+
function dedent(body) {
|
|
2404
|
+
const lines = body.replace(/^\n+|\s+$/g, "").split("\n");
|
|
2405
|
+
let common = Infinity;
|
|
2406
|
+
for (const line of lines) {
|
|
2407
|
+
if (!line.trim()) continue;
|
|
2408
|
+
common = Math.min(common, line.length - line.trimStart().length);
|
|
2409
|
+
}
|
|
2410
|
+
if (!Number.isFinite(common) || common === 0) return lines.join("\n");
|
|
2411
|
+
return lines.map((line) => line.slice(common)).join("\n");
|
|
2412
|
+
}
|
|
2413
|
+
function toRequire(line) {
|
|
2414
|
+
const namespace = line.match(/^import[ \t]*\*[ \t]*as[ \t]+([\w$]+)[ \t]+from[ \t]*(['"][^'"]+['"])/);
|
|
2415
|
+
if (namespace) return `const ${namespace[1]} = require(${namespace[2]})`;
|
|
2416
|
+
const named = line.match(/^import[ \t]*(\{[^}]*\})[ \t]*from[ \t]*(['"][^'"]+['"])/);
|
|
2417
|
+
if (named) return `const ${named[1].replace(/\bas\b/g, ":")} = require(${named[2]})`;
|
|
2418
|
+
const fallback = line.match(/^import[ \t]+([\w$]+)[ \t]+from[ \t]*(['"][^'"]+['"])/);
|
|
2419
|
+
if (fallback) return `const ${fallback[1]} = require(${fallback[2]}).default ?? require(${fallback[2]})`;
|
|
2420
|
+
const bare = line.match(/^import[ \t]*(['"][^'"]+['"])/);
|
|
2421
|
+
if (bare) return `require(${bare[1]})`;
|
|
2422
|
+
throw new Error(`cannot express this import in CommonJS, move it or emit ES modules: ${JSON.stringify(line)}`);
|
|
2423
|
+
}
|
|
2424
|
+
function emitMethodsBlock(body, format) {
|
|
2425
|
+
const info = analyzeMethodsBlock(body);
|
|
2426
|
+
if (!info.hasDefault && info.names.length === 0) {
|
|
2427
|
+
throw new Error("methods block exports nothing; it needs `export default <table>` or named exports");
|
|
2428
|
+
}
|
|
2429
|
+
let source = dedent(body).replace(/^export[ \t]+default[ \t]+/m, "const __methods = ").replace(EXPORT_LIST, "").replace(/^export[ \t]+/gm, "");
|
|
2430
|
+
if (format === "cjs") source = source.replace(IMPORT, (line) => toRequire(line.trim()));
|
|
2431
|
+
return {
|
|
2432
|
+
source,
|
|
2433
|
+
table: info.hasDefault ? "__methods" : `{ ${info.names.join(", ")} }`,
|
|
2434
|
+
names: info.names
|
|
2435
|
+
};
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
// src/definition.js
|
|
2439
|
+
var consume = (name) => ({ consume: name });
|
|
2440
|
+
var subrule = (name) => ({ subrule: name });
|
|
2441
|
+
var label = (name, parser) => ({ label: [name, parser] });
|
|
2442
|
+
var seq = (...items) => ({ seq: items });
|
|
2443
|
+
var alt = (...items) => ({ alt: items });
|
|
2444
|
+
var many = (item) => ({ many: item });
|
|
2445
|
+
var action = (logic) => ({ action: logic });
|
|
2446
|
+
var call = (name, value) => ({ [`meta.${name}`]: value });
|
|
2447
|
+
var keyword = (name, literal2) => ({ name, literal: literal2, longerAlt: "Identifier" });
|
|
2448
|
+
var definitionTokens = [
|
|
2449
|
+
{ name: "Whitespace", pattern: "[ \\t\\r\\n]+", skip: true },
|
|
2450
|
+
{ name: "LineComment", pattern: "//[^\\r\\n]*", skip: true },
|
|
2451
|
+
keyword("GrammarKeyword", "grammar"),
|
|
2452
|
+
keyword("StartKeyword", "start"),
|
|
2453
|
+
keyword("TokenKeyword", "token"),
|
|
2454
|
+
keyword("RuleKeyword", "rule"),
|
|
2455
|
+
keyword("PatternKeyword", "pattern"),
|
|
2456
|
+
keyword("LiteralKeyword", "literal"),
|
|
2457
|
+
keyword("SkipKeyword", "skip"),
|
|
2458
|
+
keyword("IgnoreCaseKeyword", "ignoreCase"),
|
|
2459
|
+
keyword("LongerAltKeyword", "longerAlt"),
|
|
2460
|
+
keyword("ModeKeyword", "mode"),
|
|
2461
|
+
keyword("PushModeKeyword", "pushMode"),
|
|
2462
|
+
keyword("PopModeKeyword", "popMode"),
|
|
2463
|
+
keyword("TrueKeyword", "true"),
|
|
2464
|
+
keyword("FalseKeyword", "false"),
|
|
2465
|
+
keyword("NullKeyword", "null"),
|
|
2466
|
+
{ name: "String", pattern: '"(?:\\\\.|[^"\\\\])*"' },
|
|
2467
|
+
{ name: "Number", pattern: "-?(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?" },
|
|
2468
|
+
{ name: "Identifier", pattern: "[A-Za-z_][A-Za-z0-9_]*" },
|
|
2469
|
+
{ name: "LParen", literal: "(" },
|
|
2470
|
+
{ name: "RParen", literal: ")" },
|
|
2471
|
+
{ name: "LBrace", literal: "{" },
|
|
2472
|
+
{ name: "RBrace", literal: "}" },
|
|
2473
|
+
{ name: "LBracket", literal: "[" },
|
|
2474
|
+
{ name: "RBracket", literal: "]" },
|
|
2475
|
+
{ name: "Comma", literal: "," },
|
|
2476
|
+
{ name: "Colon", literal: ":" },
|
|
2477
|
+
{ name: "Equals", literal: "=" },
|
|
2478
|
+
{ name: "Semicolon", literal: ";" }
|
|
2479
|
+
];
|
|
2480
|
+
var identifier = seq(label("token", consume("Identifier")), action({ val: ["token", "image"] }));
|
|
2481
|
+
var stringValue = seq(label("token", consume("String")), action(call("string", { val: ["token", "image"] })));
|
|
2482
|
+
var definitionRules = {
|
|
2483
|
+
Document: seq(
|
|
2484
|
+
consume("GrammarKeyword"),
|
|
2485
|
+
label("name", subrule("IdentifierValue")),
|
|
2486
|
+
consume("Semicolon"),
|
|
2487
|
+
consume("StartKeyword"),
|
|
2488
|
+
label("start", subrule("IdentifierValue")),
|
|
2489
|
+
consume("Semicolon"),
|
|
2490
|
+
label("declarations", many(subrule("Declaration"))),
|
|
2491
|
+
action(call("document", [{ val: "name" }, { val: "start" }, { val: "declarations" }]))
|
|
2492
|
+
),
|
|
2493
|
+
Declaration: alt(subrule("TokenDeclaration"), subrule("RuleDeclaration")),
|
|
2494
|
+
TokenDeclaration: seq(
|
|
2495
|
+
consume("TokenKeyword"),
|
|
2496
|
+
label("name", subrule("IdentifierValue")),
|
|
2497
|
+
label("kind", subrule("TokenKind")),
|
|
2498
|
+
label("flags", many(subrule("TokenFlag"))),
|
|
2499
|
+
consume("Semicolon"),
|
|
2500
|
+
action(call("token", [{ val: "name" }, { val: "kind" }, { val: "flags" }]))
|
|
2501
|
+
),
|
|
2502
|
+
TokenKind: alt(
|
|
2503
|
+
seq(consume("PatternKeyword"), label("source", subrule("StringValue")), action(["pattern", { val: "source" }])),
|
|
2504
|
+
seq(consume("LiteralKeyword"), label("source", subrule("StringValue")), action(["literal", { val: "source" }]))
|
|
2505
|
+
),
|
|
2506
|
+
TokenFlag: alt(
|
|
2507
|
+
seq(consume("SkipKeyword"), action(["skip", true])),
|
|
2508
|
+
seq(consume("IgnoreCaseKeyword"), action(["ignoreCase", true])),
|
|
2509
|
+
seq(consume("LongerAltKeyword"), label("name", subrule("IdentifierValue")), action(["longerAlt", { val: "name" }])),
|
|
2510
|
+
// `mode` may be repeated, for a token that serves several modes.
|
|
2511
|
+
seq(consume("ModeKeyword"), label("name", subrule("IdentifierValue")), action(["mode", { val: "name" }])),
|
|
2512
|
+
seq(consume("PushModeKeyword"), label("name", subrule("IdentifierValue")), action(["pushMode", { val: "name" }])),
|
|
2513
|
+
seq(consume("PopModeKeyword"), action(["popMode", true]))
|
|
2514
|
+
),
|
|
2515
|
+
RuleDeclaration: seq(
|
|
2516
|
+
consume("RuleKeyword"),
|
|
2517
|
+
label("name", subrule("IdentifierValue")),
|
|
2518
|
+
consume("Equals"),
|
|
2519
|
+
label("parser", subrule("ParserExpression")),
|
|
2520
|
+
consume("Semicolon"),
|
|
2521
|
+
action(call("rule", [{ val: "name" }, { val: "parser" }]))
|
|
2522
|
+
),
|
|
2523
|
+
ParserExpression: subrule("Call"),
|
|
2524
|
+
Call: seq(
|
|
2525
|
+
label("name", subrule("IdentifierValue")),
|
|
2526
|
+
consume("LParen"),
|
|
2527
|
+
label("args", subrule("CallArguments")),
|
|
2528
|
+
consume("RParen"),
|
|
2529
|
+
action(call("call", [{ val: "name" }, { val: "args" }]))
|
|
2530
|
+
),
|
|
2531
|
+
CallArguments: alt(
|
|
2532
|
+
seq({ lookahead: consume("RParen") }, action([])),
|
|
2533
|
+
seq(
|
|
2534
|
+
label("head", subrule("CallArgument")),
|
|
2535
|
+
label("tail", many(seq(consume("Comma"), label("value", subrule("CallArgument")), action({ val: "value" })))),
|
|
2536
|
+
action(call("list", [{ val: "head" }, { val: "tail" }]))
|
|
2537
|
+
)
|
|
2538
|
+
),
|
|
2539
|
+
CallArgument: alt(subrule("Call"), subrule("JsonValue"), subrule("IdentifierValue")),
|
|
2540
|
+
JsonValue: alt(
|
|
2541
|
+
subrule("JsonObject"),
|
|
2542
|
+
subrule("JsonArray"),
|
|
2543
|
+
subrule("StringValue"),
|
|
2544
|
+
subrule("NumberValue"),
|
|
2545
|
+
seq(consume("TrueKeyword"), action(true)),
|
|
2546
|
+
seq(consume("FalseKeyword"), action(false)),
|
|
2547
|
+
seq(consume("NullKeyword"), action(null))
|
|
2548
|
+
),
|
|
2549
|
+
JsonObject: alt(
|
|
2550
|
+
seq(consume("LBrace"), consume("RBrace"), action(call("object", []))),
|
|
2551
|
+
seq(
|
|
2552
|
+
consume("LBrace"),
|
|
2553
|
+
label("head", subrule("JsonPair")),
|
|
2554
|
+
label("tail", many(seq(consume("Comma"), label("pair", subrule("JsonPair")), action({ val: "pair" })))),
|
|
2555
|
+
consume("RBrace"),
|
|
2556
|
+
action(call("object", [{ val: "head" }, { val: "tail" }]))
|
|
2557
|
+
)
|
|
2558
|
+
),
|
|
2559
|
+
JsonPair: seq(
|
|
2560
|
+
label("key", subrule("StringValue")),
|
|
2561
|
+
consume("Colon"),
|
|
2562
|
+
label("value", subrule("JsonValue")),
|
|
2563
|
+
action([{ val: "key" }, { val: "value" }])
|
|
2564
|
+
),
|
|
2565
|
+
JsonArray: alt(
|
|
2566
|
+
seq(consume("LBracket"), consume("RBracket"), action([])),
|
|
2567
|
+
seq(
|
|
2568
|
+
consume("LBracket"),
|
|
2569
|
+
label("head", subrule("JsonValue")),
|
|
2570
|
+
label("tail", many(seq(consume("Comma"), label("value", subrule("JsonValue")), action({ val: "value" })))),
|
|
2571
|
+
consume("RBracket"),
|
|
2572
|
+
action(call("list", [{ val: "head" }, { val: "tail" }]))
|
|
2573
|
+
)
|
|
2574
|
+
),
|
|
2575
|
+
IdentifierValue: identifier,
|
|
2576
|
+
StringValue: stringValue,
|
|
2577
|
+
NumberValue: seq(label("token", consume("Number")), action(call("number", { val: ["token", "image"] })))
|
|
2578
|
+
};
|
|
2579
|
+
var definitionGrammar = { tokens: definitionTokens, rules: definitionRules, start: "Document" };
|
|
2580
|
+
function createMetaMethods() {
|
|
2581
|
+
const methods = {
|
|
2582
|
+
string: (image) => JSON.parse(image),
|
|
2583
|
+
number: (image) => Number(image),
|
|
2584
|
+
list: ([head, tail]) => {
|
|
2585
|
+
const output = new Array(tail.length + 1);
|
|
2586
|
+
output[0] = head;
|
|
2587
|
+
for (let i = 0; i < tail.length; i++) output[i + 1] = tail[i];
|
|
2588
|
+
return output;
|
|
2589
|
+
},
|
|
2590
|
+
object: (value) => {
|
|
2591
|
+
const output = {};
|
|
2592
|
+
if (value.length === 0) return output;
|
|
2593
|
+
const head = value[0];
|
|
2594
|
+
const tail = value[1];
|
|
2595
|
+
output[head[0]] = head[1];
|
|
2596
|
+
for (let i = 0; i < tail.length; i++) output[tail[i][0]] = tail[i][1];
|
|
2597
|
+
return output;
|
|
2598
|
+
},
|
|
2599
|
+
token: ([name, kind, flags]) => {
|
|
2600
|
+
const value = { name, [kind[0]]: kind[1] };
|
|
2601
|
+
const modes = [];
|
|
2602
|
+
for (let i = 0; i < flags.length; i++) {
|
|
2603
|
+
if (flags[i][0] === "mode") modes.push(flags[i][1]);
|
|
2604
|
+
else value[flags[i][0]] = flags[i][1];
|
|
2605
|
+
}
|
|
2606
|
+
if (modes.length === 1) value.mode = modes[0];
|
|
2607
|
+
else if (modes.length > 1) value.modes = modes;
|
|
2608
|
+
return ["token", value];
|
|
2609
|
+
},
|
|
2610
|
+
rule: ([name, parser]) => ["rule", name, parser],
|
|
2611
|
+
document: ([name, start, declarations]) => {
|
|
2612
|
+
const tokens = [];
|
|
2613
|
+
const rules = {};
|
|
2614
|
+
for (let i = 0; i < declarations.length; i++) {
|
|
2615
|
+
const declaration = declarations[i];
|
|
2616
|
+
if (declaration[0] === "token") tokens.push(declaration[1]);
|
|
2617
|
+
else rules[declaration[1]] = declaration[2];
|
|
2618
|
+
}
|
|
2619
|
+
return { name, tokens, rules, start };
|
|
2620
|
+
},
|
|
2621
|
+
call: ([name, args]) => {
|
|
2622
|
+
const build = CONSTRUCTORS[name];
|
|
2623
|
+
if (!build) throw new Error(`Unknown parser constructor ${name}`);
|
|
2624
|
+
if (args.length < build.arity[0] || args.length > build.arity[1]) {
|
|
2625
|
+
const [min, max] = build.arity;
|
|
2626
|
+
const expected = min === max ? String(min) : `${min}-${max}`;
|
|
2627
|
+
throw new Error(`${name} expects ${expected} argument(s), received ${args.length}`);
|
|
2628
|
+
}
|
|
2629
|
+
return build.of(args);
|
|
2630
|
+
}
|
|
2631
|
+
};
|
|
2632
|
+
const descriptors = {};
|
|
2633
|
+
for (const key of Object.keys(methods)) {
|
|
2634
|
+
descriptors[`meta.${key}`] = { method: methods[key], optimizeUnary: true };
|
|
2635
|
+
}
|
|
2636
|
+
return descriptors;
|
|
2637
|
+
}
|
|
2638
|
+
var VARIADIC = [0, Infinity];
|
|
2639
|
+
var logicOf = (node) => node && node.action !== void 0 ? node.action : node;
|
|
2640
|
+
var CONSTRUCTORS = {
|
|
2641
|
+
seq: { arity: VARIADIC, of: (args) => ({ seq: args }) },
|
|
2642
|
+
alt: { arity: VARIADIC, of: (args) => ({ alt: args }) },
|
|
2643
|
+
oneOf: { arity: VARIADIC, of: (args) => ({ oneOf: args }) },
|
|
2644
|
+
consume: { arity: [1, 1], of: ([a]) => ({ consume: a }) },
|
|
2645
|
+
subrule: { arity: [1, 1], of: ([a]) => ({ subrule: a }) },
|
|
2646
|
+
many: { arity: [1, 1], of: ([a]) => ({ many: a }) },
|
|
2647
|
+
many1: { arity: [1, 1], of: ([a]) => ({ many1: a }) },
|
|
2648
|
+
option: { arity: [1, 1], of: ([a]) => ({ option: a }) },
|
|
2649
|
+
action: { arity: [1, 1], of: ([a]) => ({ action: a }) },
|
|
2650
|
+
lookahead: { arity: [1, 1], of: ([a]) => ({ lookahead: a }) },
|
|
2651
|
+
negLookahead: { arity: [1, 1], of: ([a]) => ({ negLookahead: a }) },
|
|
2652
|
+
text: { arity: [1, 1], of: ([a]) => ({ text: a }) },
|
|
2653
|
+
eof: { arity: [0, 0], of: () => ({ eof: null }) },
|
|
2654
|
+
epsilon: { arity: [0, 0], of: () => ({ epsilon: null }) },
|
|
2655
|
+
label: { arity: [2, 2], of: ([a, b]) => ({ label: [a, b] }) },
|
|
2656
|
+
as: { arity: [2, 2], of: ([a, b]) => ({ as: [a, logicOf(b)] }) },
|
|
2657
|
+
manySep: { arity: [2, 2], of: ([a, b]) => ({ manySep: { rule: a, sep: b } }) },
|
|
2658
|
+
many1Sep: { arity: [2, 2], of: ([a, b]) => ({ many1Sep: { rule: a, sep: b } }) },
|
|
2659
|
+
between: { arity: [3, 3], of: ([a, b, c]) => ({ between: { open: a, body: b, close: c } }) },
|
|
2660
|
+
infixLeft: { arity: [3, 3], of: ([a, b, c]) => ({ infixLeft: { operand: a, operator: b, combine: logicOf(c) } }) },
|
|
2661
|
+
infixRight: { arity: [3, 3], of: ([a, b, c]) => ({ infixRight: { operand: a, operator: b, combine: logicOf(c) } }) },
|
|
2662
|
+
prefix: { arity: [3, 3], of: ([a, b, c]) => ({ prefix: { operator: a, operand: b, combine: logicOf(c) } }) },
|
|
2663
|
+
postfix: { arity: [3, 3], of: ([a, b, c]) => ({ postfix: { operand: a, suffix: b, combine: logicOf(c) } }) }
|
|
2664
|
+
};
|
|
2665
|
+
var CONSTRUCTOR_NAMES = Object.keys(CONSTRUCTORS);
|
|
2666
|
+
var createDefinitionParser = (options = {}) => createParser(definitionGrammar, { ...options, methods: createMetaMethods() });
|
|
2667
|
+
function parseDefinition(source, options = {}) {
|
|
2668
|
+
const { grammar: text, block } = splitMethodsBlock(source);
|
|
2669
|
+
const grammar = createDefinitionParser(options).parse(text);
|
|
2670
|
+
if (block !== null) grammar.methodsBlock = block;
|
|
2671
|
+
return grammar;
|
|
2672
|
+
}
|
|
2673
|
+
function createParserFromSource(source, options = {}) {
|
|
2674
|
+
const grammar = parseDefinition(source, { execution: options.execution });
|
|
2675
|
+
if (grammar.methodsBlock === void 0 || options.methods) return createParser(grammar, options);
|
|
2676
|
+
const { methods } = evaluateMethodsBlock(grammar.methodsBlock);
|
|
2677
|
+
return createParser(grammar, { ...options, methods });
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2680
|
+
// src/emit.js
|
|
2681
|
+
import { Compiler, LogicEngine as LogicEngine2 } from "json-logic-engine";
|
|
2682
|
+
var UNSUPPORTED = /\b(coerceArray|precoerceNumber|compareCheck|assertAllowedDepth|assertSize|asyncIterators|notTraversed)\b|(?<!engine\.)\bmethods\[|engine\.(?!methods\b)/;
|
|
2683
|
+
function inlineAction(logic, engine) {
|
|
2684
|
+
try {
|
|
2685
|
+
const seed = { engine };
|
|
2686
|
+
Compiler.build(logic, seed);
|
|
2687
|
+
const state = {
|
|
2688
|
+
engine,
|
|
2689
|
+
compile: seed.compile,
|
|
2690
|
+
values: [],
|
|
2691
|
+
methods: [],
|
|
2692
|
+
notTraversed: [],
|
|
2693
|
+
processing: [],
|
|
2694
|
+
state: {}
|
|
2695
|
+
};
|
|
2696
|
+
const source = Compiler.buildString(logic, state);
|
|
2697
|
+
if (state.methods.length || state.notTraversed.length || state.processing.length) return null;
|
|
2698
|
+
if (UNSUPPORTED.test(source)) return null;
|
|
2699
|
+
let values = null;
|
|
2700
|
+
if (state.values.length > 0) {
|
|
2701
|
+
values = JSON.stringify(state.values);
|
|
2702
|
+
if (JSON.parse(values) === void 0) return null;
|
|
2703
|
+
if (JSON.stringify(JSON.parse(values)) !== values) return null;
|
|
2704
|
+
}
|
|
2705
|
+
return {
|
|
2706
|
+
source,
|
|
2707
|
+
values,
|
|
2708
|
+
needsMethods: source.includes("engine.methods"),
|
|
2709
|
+
needsAbove: /\babove\b/.test(source)
|
|
2710
|
+
};
|
|
2711
|
+
} catch {
|
|
2712
|
+
return null;
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2715
|
+
function tryEvaluate(body) {
|
|
2716
|
+
try {
|
|
2717
|
+
return evaluateMethodsBlock(body).methods;
|
|
2718
|
+
} catch {
|
|
2719
|
+
return null;
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
var quote = (value) => JSON.stringify(value);
|
|
2723
|
+
var literal = (value, indent = 0) => JSON.stringify(value, null, 2).split("\n").join("\n" + " ".repeat(indent));
|
|
2724
|
+
function emitModule(spec, options = {}) {
|
|
2725
|
+
const { tokens: tokenDefs, rules, start } = spec;
|
|
2726
|
+
const {
|
|
2727
|
+
memo = false,
|
|
2728
|
+
strict = true,
|
|
2729
|
+
ll1 = true,
|
|
2730
|
+
maxSteps = 0,
|
|
2731
|
+
positions = "full",
|
|
2732
|
+
execution = "generated",
|
|
2733
|
+
format = "esm",
|
|
2734
|
+
validate = true,
|
|
2735
|
+
moduleName = spec.name || "parser",
|
|
2736
|
+
runtimeSpecifier = "grammatik/runtime",
|
|
2737
|
+
engineSpecifier = "json-logic-engine",
|
|
2738
|
+
methods: semanticMethods
|
|
2739
|
+
} = options;
|
|
2740
|
+
if (format !== "esm" && format !== "cjs") throw new Error(`emitModule: format must be 'esm' or 'cjs', got ${quote(format)}`);
|
|
2741
|
+
const methodsBlock = options.methodsBlock !== void 0 ? options.methodsBlock : spec.methodsBlock;
|
|
2742
|
+
const block = methodsBlock ? emitMethodsBlock(methodsBlock, format) : null;
|
|
2743
|
+
if (!start || !(start in rules)) throw new Error(`emitModule: start rule '${start}' is not defined in rules`);
|
|
2744
|
+
const lexer = createLexer(tokenDefs, { positions });
|
|
2745
|
+
const analysis = analyze(rules, { tokenNames: lexer.tokenNames });
|
|
2746
|
+
if (validate && analysis.errors.length > 0) throw new GrammarError(analysis.errors);
|
|
2747
|
+
const firsts = ll1 ? analysis.firsts : null;
|
|
2748
|
+
const engine = { build: () => {
|
|
2749
|
+
throw new Error("not compiled during emit");
|
|
2750
|
+
}, run: () => {
|
|
2751
|
+
} };
|
|
2752
|
+
const planner = createPlanner({ engine, rules, firsts, memo, maxSteps, unsafeEval: false });
|
|
2753
|
+
const codegen = createCodegen({ planner, rules, firsts, memo, maxSteps });
|
|
2754
|
+
codegen.compile(start);
|
|
2755
|
+
const reached = [...codegen.ruleIndex.entries()].sort((a, b) => a[1] - b[1]);
|
|
2756
|
+
const unsupported = codegen.ORIGINS.map((origin, index) => ({ origin, index })).filter(({ origin }) => origin.kind === "unknownRule");
|
|
2757
|
+
if (unsupported.length > 0) {
|
|
2758
|
+
const names = unsupported.map(({ origin }) => origin.name).join(", ");
|
|
2759
|
+
throw new Error(`emitModule: cannot emit a grammar with unknown rules (${names})`);
|
|
2760
|
+
}
|
|
2761
|
+
const blockMethods = block && !semanticMethods ? tryEvaluate(methodsBlock) : null;
|
|
2762
|
+
const compileEngine = new LogicEngine2();
|
|
2763
|
+
for (const [name, method] of Object.entries(createValueMethods())) compileEngine.addMethod(name, method);
|
|
2764
|
+
for (const [name, method] of Object.entries(semanticMethods || blockMethods || {})) compileEngine.addMethod(name, method);
|
|
2765
|
+
let needsEngine = execution === "interpreted";
|
|
2766
|
+
let needsAbove = false;
|
|
2767
|
+
const emitAction = (logic) => {
|
|
2768
|
+
if (usesOuterScope(logic) || execution === "interpreted") {
|
|
2769
|
+
needsEngine = true;
|
|
2770
|
+
return `interpretedAction(${literal(logic, 2)})`;
|
|
2771
|
+
}
|
|
2772
|
+
const inlined = inlineAction(logic, compileEngine);
|
|
2773
|
+
if (!inlined) {
|
|
2774
|
+
needsEngine = true;
|
|
2775
|
+
return `action(${literal(logic, 2)})`;
|
|
2776
|
+
}
|
|
2777
|
+
if (inlined.needsAbove) needsAbove = true;
|
|
2778
|
+
const body = `(context) => { let prev; return ${inlined.source} }`;
|
|
2779
|
+
return inlined.values ? `((values) => ${body})(${inlined.values})` : body;
|
|
2780
|
+
};
|
|
2781
|
+
const helpers = codegen.ORIGINS.map((origin) => {
|
|
2782
|
+
if (origin.kind === "constant") return `constant(${literal(origin.node, 2)})`;
|
|
2783
|
+
if (origin.kind === "action") return emitAction(origin.logic);
|
|
2784
|
+
if (origin.kind === "valueExpression") return `valueExpression(${emitAction(origin.logic)})`;
|
|
2785
|
+
throw new Error(`emitModule: unsupported helper (${origin.kind})`);
|
|
2786
|
+
});
|
|
2787
|
+
const engineFree = !needsEngine;
|
|
2788
|
+
const ruleSources = reached.map(
|
|
2789
|
+
([name, index]) => `// ${name}
|
|
2790
|
+
R[${index}] = ${codegen.SOURCES[index]}`
|
|
2791
|
+
).join("\n\n");
|
|
2792
|
+
const interpretedActions = execution === "interpreted";
|
|
2793
|
+
const cjs = format === "cjs";
|
|
2794
|
+
const runtimeImports = ["FAIL as F", "expect as E", "createState", "buildParseError", "createLexer"];
|
|
2795
|
+
if (needsEngine) runtimeImports.push("createValueMethods");
|
|
2796
|
+
const importRuntime = cjs ? `const { ${runtimeImports.map((b) => b.replace(" as ", ": ")).join(", ")} } = require(${quote(runtimeSpecifier)})` : `import { ${runtimeImports.join(", ")} } from ${quote(runtimeSpecifier)}`;
|
|
2797
|
+
const importEngine = engineFree ? "" : cjs ? `const { LogicEngine } = require(${quote(engineSpecifier)})
|
|
2798
|
+
` : `import { LogicEngine } from ${quote(engineSpecifier)}
|
|
2799
|
+
`;
|
|
2800
|
+
const PUBLIC = ["parse", "parseTokens", "tokenize", "tokens", "start", "registerMethods"];
|
|
2801
|
+
for (const name of block ? block.names : []) {
|
|
2802
|
+
if (PUBLIC.includes(name)) throw new Error(`emitModule: the methods block exports '${name}', which the generated parser already exports`);
|
|
2803
|
+
}
|
|
2804
|
+
const exported = block ? [...PUBLIC, ...block.names] : PUBLIC;
|
|
2805
|
+
const exports = cjs ? `module.exports = { ${exported.join(", ")} }
|
|
2806
|
+
// Named the same under \`default\`, so an \`import parser from\` interops.
|
|
2807
|
+
module.exports.default = module.exports` : `export { ${exported.join(", ")} }
|
|
2808
|
+
export default { ${PUBLIC.join(", ")} }`;
|
|
2809
|
+
return `// Generated by grammatik from the ${quote(moduleName)} grammar. Do not edit.
|
|
2810
|
+
//
|
|
2811
|
+
// The grammar was analyzed when this file was written: FIRST sets, left-recursion
|
|
2812
|
+
// checks and rule specialization all happened then. Importing it does no grammar
|
|
2813
|
+
// work and generates no code.
|
|
2814
|
+
${engineFree ? "// Every semantic action was compiled to source too, so this file has no\n// dependency on json-logic-engine at all." : interpretedActions ? "// Semantic actions are interpreted, so nothing here calls `eval`." : "// Semantic actions that could not be compiled to source are shipped as JSON\n// Logic and built by json-logic-engine when this module loads."}
|
|
2815
|
+
|
|
2816
|
+
${importRuntime}
|
|
2817
|
+
${importEngine}
|
|
2818
|
+
|
|
2819
|
+
const tokens = ${literal(tokenDefs)}
|
|
2820
|
+
|
|
2821
|
+
const start = ${quote(start)}
|
|
2822
|
+
|
|
2823
|
+
const lexer = createLexer(tokens, { positions: ${quote(positions)} })
|
|
2824
|
+
const tokenize = lexer.tokenize
|
|
2825
|
+
|
|
2826
|
+
${engineFree ? `// Every semantic action compiled to source, so the engine reduced to a table
|
|
2827
|
+
// of the methods below. Nothing here imports json-logic-engine.
|
|
2828
|
+
const M = Object.create(null)
|
|
2829
|
+
const engine = { methods: M }
|
|
2830
|
+
${needsAbove ? "const above = []\n" : ""}
|
|
2831
|
+
/**
|
|
2832
|
+
* Semantic methods the grammar refers to. Pass the same object you would give
|
|
2833
|
+
* \`createParser\`; anything the actions call must be registered before use.
|
|
2834
|
+
*/
|
|
2835
|
+
function registerMethods (methods) {
|
|
2836
|
+
for (const [name, method] of Object.entries(methods || {})) {
|
|
2837
|
+
M[name] = typeof method === 'function' ? { method } : method
|
|
2838
|
+
}
|
|
2839
|
+
return engine
|
|
2840
|
+
}` : `const engine = new LogicEngine()
|
|
2841
|
+
for (const [name, method] of Object.entries(createValueMethods())) engine.addMethod(name, method)
|
|
2842
|
+
${needsAbove ? "const above = []\n" : ""}
|
|
2843
|
+
/**
|
|
2844
|
+
* Semantic methods the grammar refers to. Pass the same object you would give
|
|
2845
|
+
* \`createParser\`; anything the actions call must be registered before use.
|
|
2846
|
+
*/
|
|
2847
|
+
function registerMethods (methods) {
|
|
2848
|
+
for (const [name, method] of Object.entries(methods || {})) engine.addMethod(name, method)
|
|
2849
|
+
return engine
|
|
2850
|
+
}`}
|
|
2851
|
+
|
|
2852
|
+
${block ? `// ---------------------------------------------------------------------------
|
|
2853
|
+
// The grammar's \`methods { ... }\` block, written out as source. It is registered
|
|
2854
|
+
// below, so importing this module is enough \u2014 no wiring at the call site.
|
|
2855
|
+
// ---------------------------------------------------------------------------
|
|
2856
|
+
|
|
2857
|
+
${block.source}
|
|
2858
|
+
|
|
2859
|
+
registerMethods(${block.table})
|
|
2860
|
+
` : ""}
|
|
2861
|
+
${engineFree ? "" : `const interpretedAction = (logic) => (bindings, state) => engine.run(logic, bindings, { above: [state] })
|
|
2862
|
+
`}
|
|
2863
|
+
${engineFree ? "" : interpretedActions ? "const action = interpretedAction" : `const action = (logic) => {
|
|
2864
|
+
// Compiled on first use, so \`registerMethods\` can run first.
|
|
2865
|
+
let fn
|
|
2866
|
+
return (bindings, state) => {
|
|
2867
|
+
if (fn === undefined) {
|
|
2868
|
+
try {
|
|
2869
|
+
const built = engine.build(logic)
|
|
2870
|
+
fn = typeof built === 'function' ? (b) => built(b) : () => built
|
|
2871
|
+
} catch {
|
|
2872
|
+
fn = (b, s) => engine.run(logic, b, { above: [s] })
|
|
2873
|
+
}
|
|
2874
|
+
}
|
|
2875
|
+
return fn(bindings, state)
|
|
2876
|
+
}
|
|
2877
|
+
}`}
|
|
2878
|
+
|
|
2879
|
+
/** A JSON Logic value expression sitting in parser position. */
|
|
2880
|
+
const valueExpression = (run) => (state) => run(state.bindings || {}, state)
|
|
2881
|
+
const constant = (value) => () => value
|
|
2882
|
+
|
|
2883
|
+
const P = (op, at) => {
|
|
2884
|
+
throw new Error(\`'\${op}' matched without consuming any input at token index \${at}; this would loop forever.\`)
|
|
2885
|
+
}
|
|
2886
|
+
const T = () => { throw new Error('Parser exceeded maxSteps (${maxSteps}); the grammar is likely looping.') }
|
|
2887
|
+
|
|
2888
|
+
/** Rule functions, indexed. Filled below; recursion resolves through the array. */
|
|
2889
|
+
const R = []
|
|
2890
|
+
|
|
2891
|
+
/** Values the rules close over: semantic actions and literals. */
|
|
2892
|
+
const H = [
|
|
2893
|
+
${helpers.map((source, index) => ` /* ${index} */ ${source}`).join(",\n")}
|
|
2894
|
+
]
|
|
2895
|
+
|
|
2896
|
+
${ruleSources}
|
|
2897
|
+
|
|
2898
|
+
const RULES = ${literal(Object.fromEntries(reached.map(([name, index]) => [name, index])))}
|
|
2899
|
+
|
|
2900
|
+
/**
|
|
2901
|
+
* Parses a token stream you lexed yourself.
|
|
2902
|
+
* @param {any[]} tokenList
|
|
2903
|
+
* @param {string} [source]
|
|
2904
|
+
*/
|
|
2905
|
+
function parseTokens (tokenList, source, track = false) {
|
|
2906
|
+
const state = createState(tokenList, {}, { memo: ${memo}, maxSteps: ${maxSteps}, track })
|
|
2907
|
+
state.source = source
|
|
2908
|
+
state.positions = ${quote(positions)}
|
|
2909
|
+
state.ruleNames[0] = start
|
|
2910
|
+
state.ruleDepth = 1
|
|
2911
|
+
|
|
2912
|
+
const result = R[RULES[start]](state)
|
|
2913
|
+
const trailing = ${strict ? "state.idx < tokenList.length" : "false"}
|
|
2914
|
+
if (result !== F && !trailing) return result
|
|
2915
|
+
|
|
2916
|
+
if (!track) return parseTokens(tokenList, source, true)
|
|
2917
|
+
if (trailing && state.maxIdx < state.idx) {
|
|
2918
|
+
state.maxIdx = state.idx
|
|
2919
|
+
state.expected = ['end of input']
|
|
2920
|
+
state.failureStack = state.ruleNames.slice(0, state.ruleDepth)
|
|
2921
|
+
}
|
|
2922
|
+
throw buildParseError(state)
|
|
2923
|
+
}
|
|
2924
|
+
|
|
2925
|
+
/** Lexes and parses text. @param {string} text */
|
|
2926
|
+
function parse (text) {
|
|
2927
|
+
return parseTokens(lexer.tokenize(text), text)
|
|
2928
|
+
}
|
|
2929
|
+
|
|
2930
|
+
${exports}
|
|
2931
|
+
`;
|
|
2932
|
+
}
|
|
2933
|
+
export {
|
|
2934
|
+
CONSTRUCTOR_NAMES,
|
|
2935
|
+
FAIL,
|
|
2936
|
+
GrammarError,
|
|
2937
|
+
LexError,
|
|
2938
|
+
OPERATORS,
|
|
2939
|
+
PARSER_OPS,
|
|
2940
|
+
ParseError,
|
|
2941
|
+
analyze,
|
|
2942
|
+
analyzeMethodsBlock,
|
|
2943
|
+
buildDispatch,
|
|
2944
|
+
createDefinitionParser,
|
|
2945
|
+
createLexer,
|
|
2946
|
+
createMethods,
|
|
2947
|
+
createParser,
|
|
2948
|
+
createParserFromSource,
|
|
2949
|
+
createState,
|
|
2950
|
+
createValueMethods,
|
|
2951
|
+
definitionGrammar,
|
|
2952
|
+
definitionRules,
|
|
2953
|
+
definitionTokens,
|
|
2954
|
+
emitModule,
|
|
2955
|
+
evaluateMethodsBlock,
|
|
2956
|
+
firstOf,
|
|
2957
|
+
parseDefinition,
|
|
2958
|
+
splitMethodsBlock
|
|
2959
|
+
};
|