parseman 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +167 -126
- package/dist/combinators/choice.d.ts.map +1 -1
- package/dist/combinators/completions.d.ts +17 -0
- package/dist/combinators/completions.d.ts.map +1 -0
- package/dist/combinators/grammar.d.ts +20 -4
- package/dist/combinators/grammar.d.ts.map +1 -1
- package/dist/combinators/guard.d.ts +2 -2
- package/dist/combinators/guard.d.ts.map +1 -1
- package/dist/combinators/keywords.d.ts +22 -0
- package/dist/combinators/keywords.d.ts.map +1 -0
- package/dist/combinators/literal.d.ts.map +1 -1
- package/dist/combinators/node.d.ts +24 -0
- package/dist/combinators/node.d.ts.map +1 -0
- package/dist/combinators/parser.d.ts +7 -7
- package/dist/combinators/parser.d.ts.map +1 -1
- package/dist/combinators/probe.d.ts +13 -0
- package/dist/combinators/probe.d.ts.map +1 -0
- package/dist/combinators/recover.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/combinators/scanTo.d.ts.map +1 -1
- package/dist/combinators/sequence.d.ts.map +1 -1
- package/dist/combinators/trivia-skip.d.ts +33 -0
- package/dist/combinators/trivia-skip.d.ts.map +1 -0
- package/dist/combinators/withCtx.d.ts +1 -1
- package/dist/compiler/codegen.d.ts +13 -4
- package/dist/compiler/codegen.d.ts.map +1 -1
- package/dist/cst/grammar.d.ts +33 -2
- package/dist/cst/grammar.d.ts.map +1 -1
- package/dist/cst/incremental.d.ts +4 -2
- package/dist/cst/incremental.d.ts.map +1 -1
- package/dist/cst/trivia-index.d.ts +50 -0
- package/dist/cst/trivia-index.d.ts.map +1 -0
- package/dist/cst/types.d.ts +7 -7
- package/dist/cst/types.d.ts.map +1 -1
- package/dist/functional/doc.d.ts +53 -0
- package/dist/functional/doc.d.ts.map +1 -0
- package/dist/index.cjs +953 -221
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6519 -226
- package/dist/index.js.map +4 -4
- package/dist/plugin/evaluator.d.ts +26 -6
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +1164 -215
- package/dist/plugin/index.cjs.map +4 -4
- package/dist/plugin/index.d.ts +5 -2
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1164 -215
- package/dist/plugin/index.js.map +4 -4
- package/dist/types.d.ts +51 -6
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -34,12 +34,17 @@ __export(index_exports, {
|
|
|
34
34
|
annotateSpan: () => annotateSpan,
|
|
35
35
|
balanced: () => balanced,
|
|
36
36
|
buildLineIndex: () => buildLineIndex,
|
|
37
|
+
buildTriviaIndex: () => buildTriviaIndex,
|
|
37
38
|
choice: () => choice,
|
|
38
39
|
compile: () => compile,
|
|
40
|
+
completionsAt: () => completionsAt,
|
|
39
41
|
guard: () => guard,
|
|
40
42
|
isParseError: () => isParseError,
|
|
43
|
+
keywords: () => keywords,
|
|
41
44
|
literal: () => literal,
|
|
45
|
+
makeFunctionalDoc: () => makeFunctionalDoc,
|
|
42
46
|
many: () => many,
|
|
47
|
+
node: () => node,
|
|
43
48
|
not: () => not,
|
|
44
49
|
offsetToLineCol: () => offsetToLineCol,
|
|
45
50
|
oneOrMore: () => oneOrMore,
|
|
@@ -49,6 +54,7 @@ __export(index_exports, {
|
|
|
49
54
|
recover: () => recover,
|
|
50
55
|
ref: () => ref,
|
|
51
56
|
regex: () => regex,
|
|
57
|
+
rules: () => rules,
|
|
52
58
|
scanTo: () => scanTo,
|
|
53
59
|
sepBy: () => sepBy,
|
|
54
60
|
sequence: () => sequence,
|
|
@@ -104,6 +110,21 @@ function mergeRanges(ranges) {
|
|
|
104
110
|
return out;
|
|
105
111
|
}
|
|
106
112
|
|
|
113
|
+
// src/combinators/probe.ts
|
|
114
|
+
function failAt(ctx, expected, pos) {
|
|
115
|
+
const r = { ok: false, expected, span: { start: pos, end: pos } };
|
|
116
|
+
const probe = ctx._probe;
|
|
117
|
+
if (probe !== void 0 && pos <= probe.offset) {
|
|
118
|
+
const best = probe.best;
|
|
119
|
+
if (best === null || pos > best.span.start) {
|
|
120
|
+
probe.best = r;
|
|
121
|
+
} else if (pos === best.span.start) {
|
|
122
|
+
probe.best = { ...best, expected: [...best.expected, ...expected] };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return r;
|
|
126
|
+
}
|
|
127
|
+
|
|
107
128
|
// src/combinators/literal.ts
|
|
108
129
|
var _collatorCache = null;
|
|
109
130
|
function collator() {
|
|
@@ -133,21 +154,21 @@ function literal(value, opts = {}) {
|
|
|
133
154
|
_tag: "literal",
|
|
134
155
|
_meta: meta,
|
|
135
156
|
_def: { tag: "literal", value, caseInsensitive },
|
|
136
|
-
parse(input, pos,
|
|
157
|
+
parse(input, pos, ctx) {
|
|
137
158
|
const end = pos + value.length;
|
|
138
159
|
if (end > input.length) {
|
|
139
|
-
return
|
|
160
|
+
return failAt(ctx, [JSON.stringify(value)], pos);
|
|
140
161
|
}
|
|
141
162
|
const slice = input.slice(pos, end);
|
|
142
163
|
const matched = caseInsensitive ? collator().compare(slice, value) === 0 : slice === value;
|
|
143
164
|
if (matched) {
|
|
144
165
|
const span = { start: pos, end };
|
|
145
166
|
const leaf = { _tag: "leaf", value: slice, span };
|
|
146
|
-
if (
|
|
147
|
-
if (
|
|
167
|
+
if (ctx._cstLeaves) ctx._cstLeaves.push(leaf);
|
|
168
|
+
if (ctx._cstRawChildren) ctx._cstRawChildren.push(leaf);
|
|
148
169
|
return { ok: true, value: slice, span };
|
|
149
170
|
}
|
|
150
|
-
return
|
|
171
|
+
return failAt(ctx, [JSON.stringify(value)], pos);
|
|
151
172
|
}
|
|
152
173
|
};
|
|
153
174
|
}
|
|
@@ -162,38 +183,38 @@ function firstSetFromRegex(pattern) {
|
|
|
162
183
|
return { firstSet: any(), canMatchNewline: true };
|
|
163
184
|
}
|
|
164
185
|
}
|
|
165
|
-
function canBeEmpty(
|
|
166
|
-
if (!
|
|
167
|
-
switch (
|
|
186
|
+
function canBeEmpty(node2) {
|
|
187
|
+
if (!node2) return true;
|
|
188
|
+
switch (node2.type) {
|
|
168
189
|
case "Repetition": {
|
|
169
|
-
const q =
|
|
190
|
+
const q = node2.quantifier;
|
|
170
191
|
return q.kind === "*" || q.kind === "?" || q.kind === "Range" && (q.from ?? 1) === 0;
|
|
171
192
|
}
|
|
172
193
|
case "Alternative": {
|
|
173
|
-
const exprs =
|
|
194
|
+
const exprs = node2.expressions;
|
|
174
195
|
return !exprs || exprs.length === 0 || exprs.every((e) => canBeEmpty(e));
|
|
175
196
|
}
|
|
176
197
|
case "Group":
|
|
177
|
-
return canBeEmpty(
|
|
198
|
+
return canBeEmpty(node2.expression);
|
|
178
199
|
case "Disjunction":
|
|
179
|
-
return canBeEmpty(
|
|
200
|
+
return canBeEmpty(node2.left) || canBeEmpty(node2.right);
|
|
180
201
|
default:
|
|
181
202
|
return false;
|
|
182
203
|
}
|
|
183
204
|
}
|
|
184
|
-
function extractFirstSet(
|
|
185
|
-
if (!
|
|
186
|
-
switch (
|
|
205
|
+
function extractFirstSet(node2) {
|
|
206
|
+
if (!node2) return { firstSet: empty(), canMatchNewline: false };
|
|
207
|
+
switch (node2.type) {
|
|
187
208
|
case "Disjunction": {
|
|
188
|
-
const left = extractFirstSet(
|
|
189
|
-
const right = extractFirstSet(
|
|
209
|
+
const left = extractFirstSet(node2.left);
|
|
210
|
+
const right = extractFirstSet(node2.right);
|
|
190
211
|
return {
|
|
191
212
|
firstSet: union(left.firstSet, right.firstSet),
|
|
192
213
|
canMatchNewline: left.canMatchNewline || right.canMatchNewline
|
|
193
214
|
};
|
|
194
215
|
}
|
|
195
216
|
case "Alternative": {
|
|
196
|
-
const exprs =
|
|
217
|
+
const exprs = node2.expressions;
|
|
197
218
|
if (!exprs || exprs.length === 0) return { firstSet: empty(), canMatchNewline: false };
|
|
198
219
|
let fs = empty();
|
|
199
220
|
let canNL = false;
|
|
@@ -206,13 +227,13 @@ function extractFirstSet(node) {
|
|
|
206
227
|
return { firstSet: fs, canMatchNewline: canNL };
|
|
207
228
|
}
|
|
208
229
|
case "Char": {
|
|
209
|
-
const kind =
|
|
230
|
+
const kind = node2.kind;
|
|
210
231
|
if (kind === "simple") {
|
|
211
|
-
const code =
|
|
232
|
+
const code = node2.codePoint ?? node2.value.codePointAt(0) ?? 0;
|
|
212
233
|
return { firstSet: fromRange(code, code), canMatchNewline: code === 10 };
|
|
213
234
|
}
|
|
214
235
|
if (kind === "meta") {
|
|
215
|
-
const val =
|
|
236
|
+
const val = node2.value;
|
|
216
237
|
if (val === ".") return { firstSet: any(), canMatchNewline: false };
|
|
217
238
|
if (val === "d") return { firstSet: fromRange(48, 57), canMatchNewline: false };
|
|
218
239
|
if (val === "D") return { firstSet: any(), canMatchNewline: true };
|
|
@@ -229,7 +250,7 @@ function extractFirstSet(node) {
|
|
|
229
250
|
return { firstSet: any(), canMatchNewline: true };
|
|
230
251
|
}
|
|
231
252
|
case "CharacterClass": {
|
|
232
|
-
const expressions =
|
|
253
|
+
const expressions = node2.expressions;
|
|
233
254
|
let fs = empty();
|
|
234
255
|
let canNL = false;
|
|
235
256
|
for (const expr of expressions) {
|
|
@@ -244,14 +265,14 @@ function extractFirstSet(node) {
|
|
|
244
265
|
canNL = canNL || r.canMatchNewline;
|
|
245
266
|
}
|
|
246
267
|
}
|
|
247
|
-
if (
|
|
268
|
+
if (node2.negative) return { firstSet: any(), canMatchNewline: true };
|
|
248
269
|
return { firstSet: fs, canMatchNewline: canNL };
|
|
249
270
|
}
|
|
250
271
|
case "Repetition": {
|
|
251
|
-
return extractFirstSet(
|
|
272
|
+
return extractFirstSet(node2.expression);
|
|
252
273
|
}
|
|
253
274
|
case "Group": {
|
|
254
|
-
return extractFirstSet(
|
|
275
|
+
return extractFirstSet(node2.expression);
|
|
255
276
|
}
|
|
256
277
|
default:
|
|
257
278
|
return { firstSet: any(), canMatchNewline: true };
|
|
@@ -282,7 +303,7 @@ function regex(pattern, flags = "") {
|
|
|
282
303
|
anchored.lastIndex = pos;
|
|
283
304
|
const m = anchored.exec(input);
|
|
284
305
|
if (m === null) {
|
|
285
|
-
return
|
|
306
|
+
return failAt(ctx, [`/${source}/`], pos);
|
|
286
307
|
}
|
|
287
308
|
const span = { start: pos, end: pos + m[0].length };
|
|
288
309
|
const leaf = { _tag: "leaf", value: m[0], span };
|
|
@@ -293,6 +314,98 @@ function regex(pattern, flags = "") {
|
|
|
293
314
|
};
|
|
294
315
|
}
|
|
295
316
|
|
|
317
|
+
// src/combinators/keywords.ts
|
|
318
|
+
function escapeRe(s) {
|
|
319
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
320
|
+
}
|
|
321
|
+
function keywords(words, opts = {}) {
|
|
322
|
+
const sorted = [...new Set(words)].sort((a, b) => b.length - a.length);
|
|
323
|
+
const alt = sorted.map(escapeRe).join("|");
|
|
324
|
+
const boundary = opts.boundary ? `(?![${opts.boundary}])` : "";
|
|
325
|
+
const flags = opts.caseInsensitive ? "iuy" : "uy";
|
|
326
|
+
const re = new RegExp(`(?:${alt})${boundary}`, flags);
|
|
327
|
+
let firstSet = empty();
|
|
328
|
+
for (const w of sorted) {
|
|
329
|
+
if (w.length === 0) continue;
|
|
330
|
+
const cp = w.codePointAt(0);
|
|
331
|
+
firstSet = union(firstSet, fromChar(cp));
|
|
332
|
+
if (opts.caseInsensitive) {
|
|
333
|
+
const u = String.fromCodePoint(cp).toUpperCase().codePointAt(0);
|
|
334
|
+
const l = String.fromCodePoint(cp).toLowerCase().codePointAt(0);
|
|
335
|
+
if (u !== void 0) firstSet = union(firstSet, fromChar(u));
|
|
336
|
+
if (l !== void 0) firstSet = union(firstSet, fromChar(l));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
const meta = { firstSet, canMatchNewline: false, isTrivia: false };
|
|
340
|
+
return {
|
|
341
|
+
_tag: "keywords",
|
|
342
|
+
_meta: meta,
|
|
343
|
+
_def: { tag: "keywords", words: sorted, caseInsensitive: opts.caseInsensitive ?? false, boundary: opts.boundary },
|
|
344
|
+
parse(input, pos, ctx) {
|
|
345
|
+
re.lastIndex = pos;
|
|
346
|
+
const m = re.exec(input);
|
|
347
|
+
if (m === null || m.index !== pos) {
|
|
348
|
+
return failAt(ctx, ["keyword"], pos);
|
|
349
|
+
}
|
|
350
|
+
const value = m[0];
|
|
351
|
+
const span = { start: pos, end: pos + value.length };
|
|
352
|
+
const leaf = { _tag: "leaf", value, span };
|
|
353
|
+
if (ctx._cstLeaves) ctx._cstLeaves.push(leaf);
|
|
354
|
+
if (ctx._cstRawChildren) ctx._cstRawChildren.push(leaf);
|
|
355
|
+
return { ok: true, value, span };
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// src/combinators/trivia-skip.ts
|
|
361
|
+
var NOOP_COMMIT = () => {
|
|
362
|
+
};
|
|
363
|
+
function scanTrivia(input, cur, ctx) {
|
|
364
|
+
const triviaP = ctx.trivia;
|
|
365
|
+
if (!triviaP) return { end: cur, commit: NOOP_COMMIT };
|
|
366
|
+
const log = ctx._triviaLog;
|
|
367
|
+
if (log !== void 0) {
|
|
368
|
+
const tr2 = triviaP.parse(input, cur, { trackLines: false, state: ctx.state });
|
|
369
|
+
if (!tr2.ok || tr2.span.end === cur) return { end: cur, commit: NOOP_COMMIT };
|
|
370
|
+
const end = tr2.span.end;
|
|
371
|
+
return {
|
|
372
|
+
end,
|
|
373
|
+
commit: () => {
|
|
374
|
+
log.push(cur, end);
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
if (ctx.captureTrivia && ctx._cstRawChildren) {
|
|
379
|
+
const sink = [];
|
|
380
|
+
const tr2 = triviaP.parse(input, cur, {
|
|
381
|
+
trackLines: ctx.trackLines,
|
|
382
|
+
state: ctx.state,
|
|
383
|
+
_cstLeaves: sink
|
|
384
|
+
});
|
|
385
|
+
if (tr2.ok && tr2.span.end > cur) {
|
|
386
|
+
const out = ctx._cstRawChildren;
|
|
387
|
+
return {
|
|
388
|
+
end: tr2.span.end,
|
|
389
|
+
commit: () => {
|
|
390
|
+
for (const tok of sink) {
|
|
391
|
+
if (tok._tag === "leaf" && tok.span.end > tok.span.start) {
|
|
392
|
+
out.push({ _tag: "trivia", value: tok.value, span: tok.span });
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
return { end: cur, commit: NOOP_COMMIT };
|
|
399
|
+
}
|
|
400
|
+
const tr = triviaP.parse(input, cur, { trackLines: ctx.trackLines, state: ctx.state });
|
|
401
|
+
return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT };
|
|
402
|
+
}
|
|
403
|
+
function consumeTrivia(input, cur, ctx) {
|
|
404
|
+
const scan = scanTrivia(input, cur, ctx);
|
|
405
|
+
scan.commit();
|
|
406
|
+
return scan.end;
|
|
407
|
+
}
|
|
408
|
+
|
|
296
409
|
// src/combinators/sequence.ts
|
|
297
410
|
function sequence(...parsers) {
|
|
298
411
|
const meta = {
|
|
@@ -309,12 +422,19 @@ function sequence(...parsers) {
|
|
|
309
422
|
let cur = pos;
|
|
310
423
|
for (let i = 0; i < parsers.length; i++) {
|
|
311
424
|
if (ctx.trivia && i > 0) {
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
425
|
+
const scan = scanTrivia(input, cur, ctx);
|
|
426
|
+
const raw = ctx._cstRawChildren;
|
|
427
|
+
const mark = raw ? raw.length : 0;
|
|
428
|
+
scan.commit();
|
|
429
|
+
const result2 = parsers[i].parse(input, scan.end, ctx);
|
|
430
|
+
if (!result2.ok) return result2;
|
|
431
|
+
if (result2.span.end > scan.end) {
|
|
432
|
+
cur = result2.span.end;
|
|
433
|
+
} else if (raw) {
|
|
434
|
+
raw.length = mark;
|
|
317
435
|
}
|
|
436
|
+
values.push(result2.value);
|
|
437
|
+
continue;
|
|
318
438
|
}
|
|
319
439
|
const result = parsers[i].parse(input, cur, ctx);
|
|
320
440
|
if (!result.ok) return result;
|
|
@@ -415,14 +535,22 @@ function choice(...args) {
|
|
|
415
535
|
return { ok: false, expected, span: { start: pos, end: pos } };
|
|
416
536
|
}
|
|
417
537
|
for (let i = 0; i < parsers.length; i++) {
|
|
418
|
-
if (gates[i] && !gates[i](ctx.
|
|
538
|
+
if (gates[i] && !gates[i](ctx.state)) continue;
|
|
539
|
+
const leavesLen = ctx._cstLeaves?.length;
|
|
540
|
+
const rawLen = ctx._cstRawChildren?.length;
|
|
419
541
|
const result = parsers[i].parse(input, pos, ctx);
|
|
420
542
|
if (!result.ok) {
|
|
543
|
+
if (leavesLen !== void 0 && ctx._cstLeaves) ctx._cstLeaves.length = leavesLen;
|
|
544
|
+
if (rawLen !== void 0 && ctx._cstRawChildren) ctx._cstRawChildren.length = rawLen;
|
|
421
545
|
expected.push(...result.expected);
|
|
422
546
|
continue;
|
|
423
547
|
}
|
|
424
548
|
const checks = autoNot[i];
|
|
425
|
-
if (checks && autoNotFires(input, result.span.end, checks))
|
|
549
|
+
if (checks && autoNotFires(input, result.span.end, checks)) {
|
|
550
|
+
if (leavesLen !== void 0 && ctx._cstLeaves) ctx._cstLeaves.length = leavesLen;
|
|
551
|
+
if (rawLen !== void 0 && ctx._cstRawChildren) ctx._cstRawChildren.length = rawLen;
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
426
554
|
return result;
|
|
427
555
|
}
|
|
428
556
|
return { ok: false, expected, span: { start: pos, end: pos } };
|
|
@@ -553,6 +681,30 @@ function areDisjoint(sets) {
|
|
|
553
681
|
}
|
|
554
682
|
|
|
555
683
|
// src/combinators/repeat.ts
|
|
684
|
+
function repItem(parser2, input, cur, ctx) {
|
|
685
|
+
const raw = ctx._cstRawChildren;
|
|
686
|
+
const mark = raw ? raw.length : 0;
|
|
687
|
+
let pos = cur;
|
|
688
|
+
if (ctx.trivia) {
|
|
689
|
+
const scan = scanTrivia(input, cur, ctx);
|
|
690
|
+
scan.commit();
|
|
691
|
+
pos = scan.end;
|
|
692
|
+
}
|
|
693
|
+
if (pos >= input.length) {
|
|
694
|
+
if (raw) raw.length = mark;
|
|
695
|
+
return "stop";
|
|
696
|
+
}
|
|
697
|
+
const result = parser2.parse(input, pos, ctx);
|
|
698
|
+
if (!result.ok) {
|
|
699
|
+
if (raw) raw.length = mark;
|
|
700
|
+
return { fail: result };
|
|
701
|
+
}
|
|
702
|
+
if (result.span.end === pos) {
|
|
703
|
+
if (raw) raw.length = mark;
|
|
704
|
+
return "stop";
|
|
705
|
+
}
|
|
706
|
+
return { value: result.value, end: result.span.end };
|
|
707
|
+
}
|
|
556
708
|
function many(parser2) {
|
|
557
709
|
const meta = {
|
|
558
710
|
firstSet: parser2._meta.firstSet,
|
|
@@ -567,11 +719,10 @@ function many(parser2) {
|
|
|
567
719
|
const values = [];
|
|
568
720
|
let cur = pos;
|
|
569
721
|
while (cur < input.length) {
|
|
570
|
-
const
|
|
571
|
-
if (
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
cur = result.span.end;
|
|
722
|
+
const item = repItem(parser2, input, cur, ctx);
|
|
723
|
+
if (item === "stop" || "fail" in item) break;
|
|
724
|
+
values.push(item.value);
|
|
725
|
+
cur = item.end;
|
|
575
726
|
}
|
|
576
727
|
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
577
728
|
}
|
|
@@ -593,11 +744,10 @@ function oneOrMore(parser2) {
|
|
|
593
744
|
const values = [first.value];
|
|
594
745
|
let cur = first.span.end;
|
|
595
746
|
while (cur < input.length) {
|
|
596
|
-
const
|
|
597
|
-
if (
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
cur = result.span.end;
|
|
747
|
+
const item = repItem(parser2, input, cur, ctx);
|
|
748
|
+
if (item === "stop" || "fail" in item) break;
|
|
749
|
+
values.push(item.value);
|
|
750
|
+
cur = item.end;
|
|
601
751
|
}
|
|
602
752
|
return { ok: true, value: values, span: { start: pos, end: cur } };
|
|
603
753
|
}
|
|
@@ -636,26 +786,10 @@ function sepBy(parser2, separator) {
|
|
|
636
786
|
const values = [first.value];
|
|
637
787
|
let cur = first.span.end;
|
|
638
788
|
while (cur < input.length) {
|
|
639
|
-
|
|
640
|
-
if (ctx.trivia) {
|
|
641
|
-
const triviaCtx = { trivia: ctx.trivia, trackLines: ctx.trackLines, user: ctx.user };
|
|
642
|
-
const tr = ctx.trivia.parse(input, sepPos, triviaCtx);
|
|
643
|
-
if (tr.ok) {
|
|
644
|
-
if (ctx._cstRawChildren && tr.span.end > tr.span.start) ctx._cstRawChildren.push({ _tag: "trivia", value: input.slice(tr.span.start, tr.span.end), span: tr.span });
|
|
645
|
-
sepPos = tr.span.end;
|
|
646
|
-
}
|
|
647
|
-
}
|
|
789
|
+
const sepPos = ctx.trivia ? consumeTrivia(input, cur, ctx) : cur;
|
|
648
790
|
const sep = separator.parse(input, sepPos, ctx);
|
|
649
791
|
if (!sep.ok) break;
|
|
650
|
-
|
|
651
|
-
if (ctx.trivia) {
|
|
652
|
-
const triviaCtx = { trivia: ctx.trivia, trackLines: ctx.trackLines, user: ctx.user };
|
|
653
|
-
const tr = ctx.trivia.parse(input, nextPos, triviaCtx);
|
|
654
|
-
if (tr.ok) {
|
|
655
|
-
if (ctx._cstRawChildren && tr.span.end > tr.span.start) ctx._cstRawChildren.push({ _tag: "trivia", value: input.slice(tr.span.start, tr.span.end), span: tr.span });
|
|
656
|
-
nextPos = tr.span.end;
|
|
657
|
-
}
|
|
658
|
-
}
|
|
792
|
+
const nextPos = ctx.trivia ? consumeTrivia(input, sep.span.end, ctx) : sep.span.end;
|
|
659
793
|
const next = parser2.parse(input, nextPos, ctx);
|
|
660
794
|
if (!next.ok) break;
|
|
661
795
|
values.push(next.value);
|
|
@@ -697,7 +831,7 @@ function ref() {
|
|
|
697
831
|
}
|
|
698
832
|
|
|
699
833
|
// src/combinators/parser.ts
|
|
700
|
-
function
|
|
834
|
+
function rules(factory) {
|
|
701
835
|
const cache = {};
|
|
702
836
|
const proxy = new Proxy(cache, {
|
|
703
837
|
get(target, key) {
|
|
@@ -735,9 +869,9 @@ function not(parser2) {
|
|
|
735
869
|
isTrivia: false
|
|
736
870
|
};
|
|
737
871
|
return {
|
|
738
|
-
_tag: "
|
|
872
|
+
_tag: "not",
|
|
739
873
|
_meta: meta,
|
|
740
|
-
_def: { tag: "
|
|
874
|
+
_def: { tag: "not", parser: parser2 },
|
|
741
875
|
parse(input, pos, ctx) {
|
|
742
876
|
const result = parser2.parse(input, pos, ctx);
|
|
743
877
|
if (result.ok) {
|
|
@@ -748,6 +882,44 @@ function not(parser2) {
|
|
|
748
882
|
};
|
|
749
883
|
}
|
|
750
884
|
|
|
885
|
+
// src/combinators/node.ts
|
|
886
|
+
function node(type, parser2, build) {
|
|
887
|
+
const meta = {
|
|
888
|
+
firstSet: parser2._meta.firstSet,
|
|
889
|
+
canMatchNewline: parser2._meta.canMatchNewline,
|
|
890
|
+
isTrivia: false
|
|
891
|
+
};
|
|
892
|
+
return {
|
|
893
|
+
_tag: "node",
|
|
894
|
+
_meta: meta,
|
|
895
|
+
_def: { tag: "node", type, parser: parser2, build },
|
|
896
|
+
parse(input, pos, ctx) {
|
|
897
|
+
const children = [];
|
|
898
|
+
const rawChildren = [];
|
|
899
|
+
const innerCtx = {
|
|
900
|
+
...ctx,
|
|
901
|
+
// A node() always records the trivia between its terms — the library owns
|
|
902
|
+
// capture so grammar authors don't reconstruct it. (Matches the compiler.)
|
|
903
|
+
captureTrivia: true,
|
|
904
|
+
_cstChildren: children,
|
|
905
|
+
_cstLeaves: children,
|
|
906
|
+
_cstRawChildren: rawChildren
|
|
907
|
+
};
|
|
908
|
+
const r = parser2.parse(input, pos, innerCtx);
|
|
909
|
+
if (!r.ok) return r;
|
|
910
|
+
const built = build(children, rawChildren, r.span);
|
|
911
|
+
const isNodeLike = typeof built === "object" && built !== null && built._tag === "node";
|
|
912
|
+
if (ctx._cstChildren) ctx._cstChildren.push(built);
|
|
913
|
+
if (ctx._cstRawChildren) {
|
|
914
|
+
ctx._cstRawChildren.push(
|
|
915
|
+
isNodeLike ? built : { _tag: "leaf", value: typeof built === "string" ? built : "", span: r.span }
|
|
916
|
+
);
|
|
917
|
+
}
|
|
918
|
+
return { ok: true, value: built, span: r.span };
|
|
919
|
+
}
|
|
920
|
+
};
|
|
921
|
+
}
|
|
922
|
+
|
|
751
923
|
// src/combinators/map.ts
|
|
752
924
|
function transform(parser2, fn) {
|
|
753
925
|
return {
|
|
@@ -816,15 +988,48 @@ function annotateSpan(span, index) {
|
|
|
816
988
|
}
|
|
817
989
|
|
|
818
990
|
// src/combinators/grammar.ts
|
|
819
|
-
function
|
|
991
|
+
function parser(opts, root) {
|
|
992
|
+
return {
|
|
993
|
+
_tag: "grammar",
|
|
994
|
+
_meta: root._meta,
|
|
995
|
+
_def: {
|
|
996
|
+
tag: "grammar",
|
|
997
|
+
parser: root,
|
|
998
|
+
triviaParser: opts.trivia,
|
|
999
|
+
trackLines: opts.trackLines ?? false
|
|
1000
|
+
},
|
|
1001
|
+
parse(input, pos, _ctx) {
|
|
1002
|
+
const trackLines = opts.trackLines ?? _ctx?.trackLines ?? false;
|
|
1003
|
+
const ctx = {
|
|
1004
|
+
..._ctx,
|
|
1005
|
+
trackLines,
|
|
1006
|
+
...opts.trivia !== void 0 ? { trivia: opts.trivia } : {},
|
|
1007
|
+
...opts.captureTrivia || _ctx?.captureTrivia ? { captureTrivia: true } : {}
|
|
1008
|
+
};
|
|
1009
|
+
const result = root.parse(input, pos ?? 0, ctx);
|
|
1010
|
+
if (trackLines) {
|
|
1011
|
+
const idx = buildLineIndex(input);
|
|
1012
|
+
return { ...result, span: annotateSpan(result.span, idx) };
|
|
1013
|
+
}
|
|
1014
|
+
return result;
|
|
1015
|
+
}
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
function parse(combinator, input, opts = {}) {
|
|
820
1019
|
const trackLines = opts.trackLines ?? false;
|
|
821
|
-
const
|
|
822
|
-
const
|
|
1020
|
+
const _errors = opts.recover ? [] : void 0;
|
|
1021
|
+
const ctx = {
|
|
1022
|
+
trackLines,
|
|
1023
|
+
..._errors !== void 0 ? { _errors } : {}
|
|
1024
|
+
};
|
|
1025
|
+
const result = combinator.parse(input, 0, ctx);
|
|
1026
|
+
if (!result.ok) return result;
|
|
1027
|
+
const withErrors = _errors !== void 0 ? { ...result, errors: _errors } : result;
|
|
823
1028
|
if (trackLines) {
|
|
824
1029
|
const idx = buildLineIndex(input);
|
|
825
|
-
return { ...
|
|
1030
|
+
return { ...withErrors, span: annotateSpan(withErrors.span, idx) };
|
|
826
1031
|
}
|
|
827
|
-
return
|
|
1032
|
+
return withErrors;
|
|
828
1033
|
}
|
|
829
1034
|
|
|
830
1035
|
// src/compiler/codegen.ts
|
|
@@ -834,9 +1039,58 @@ function v(ctx, prefix = "_v") {
|
|
|
834
1039
|
function ind(ctx) {
|
|
835
1040
|
return " ".repeat(ctx.indent);
|
|
836
1041
|
}
|
|
1042
|
+
function emitLeafCapture(ctx, valExpr, startExpr, endExpr) {
|
|
1043
|
+
if (!ctx.capturing) return [];
|
|
1044
|
+
const i = ind(ctx);
|
|
1045
|
+
if (ctx.capAsTrivia) {
|
|
1046
|
+
return [`${i}if (_ctx._cstRawChildren) _ctx._cstRawChildren.push({ _tag: 'trivia', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } })`];
|
|
1047
|
+
}
|
|
1048
|
+
const lf = v(ctx, "_lf");
|
|
1049
|
+
return [
|
|
1050
|
+
`${i}if (_ctx._cstLeaves) {`,
|
|
1051
|
+
`${i} const ${lf} = { _tag: 'leaf', value: ${valExpr}, span: { start: ${startExpr}, end: ${endExpr} } }`,
|
|
1052
|
+
`${i} _ctx._cstLeaves.push(${lf})`,
|
|
1053
|
+
`${i} if (_ctx._cstRawChildren) _ctx._cstRawChildren.push(${lf})`,
|
|
1054
|
+
`${i}}`
|
|
1055
|
+
];
|
|
1056
|
+
}
|
|
1057
|
+
function ensureTriviaCaptureFn(ctx) {
|
|
1058
|
+
const trivia2 = ctx.activeTrivia;
|
|
1059
|
+
const existing = ctx.triviaCaptureNames.get(trivia2);
|
|
1060
|
+
if (existing) return existing;
|
|
1061
|
+
const fnName = `_tc${ctx.triviaCaptureNames.size}`;
|
|
1062
|
+
ctx.triviaCaptureNames.set(trivia2, fnName);
|
|
1063
|
+
const saved = { indent: ctx.indent, failLabel: ctx.failLabel, asTrivia: ctx.capAsTrivia, trivia: ctx.activeTrivia };
|
|
1064
|
+
ctx.indent = 2;
|
|
1065
|
+
ctx.failLabel = "_cap";
|
|
1066
|
+
ctx.capAsTrivia = true;
|
|
1067
|
+
delete ctx.activeTrivia;
|
|
1068
|
+
const r = emit(trivia2, ctx, "_pos");
|
|
1069
|
+
ctx.indent = saved.indent;
|
|
1070
|
+
ctx.failLabel = saved.failLabel;
|
|
1071
|
+
ctx.capAsTrivia = saved.asTrivia;
|
|
1072
|
+
if (saved.trivia) ctx.activeTrivia = saved.trivia;
|
|
1073
|
+
ctx.namedFnDecls.push([
|
|
1074
|
+
`function ${fnName}(input, _pos, _ctx) {`,
|
|
1075
|
+
` let _e = _pos`,
|
|
1076
|
+
` _cap: {`,
|
|
1077
|
+
...r.stmts,
|
|
1078
|
+
` _e = ${r.endVar}`,
|
|
1079
|
+
` }`,
|
|
1080
|
+
` if (_ctx._triviaLog !== undefined && _e > _pos) _ctx._triviaLog.push(_pos, _e)`,
|
|
1081
|
+
` return _e`,
|
|
1082
|
+
`}`
|
|
1083
|
+
].join("\n"));
|
|
1084
|
+
return fnName;
|
|
1085
|
+
}
|
|
837
1086
|
function failStmt(ctx, expected, posExpr) {
|
|
1087
|
+
if (ctx.failLabel) return `${ind(ctx)}break ${ctx.failLabel}`;
|
|
838
1088
|
return `${ind(ctx)}return { ok: false, expected: [${expected}], span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
839
1089
|
}
|
|
1090
|
+
function failArr(ctx, expectedArr, posExpr) {
|
|
1091
|
+
if (ctx.failLabel) return `${ind(ctx)}break ${ctx.failLabel}`;
|
|
1092
|
+
return `${ind(ctx)}return { ok: false, expected: ${expectedArr}, span: { start: ${posExpr}, end: ${posExpr} } }`;
|
|
1093
|
+
}
|
|
840
1094
|
function firstSetCond(codeVar, fs) {
|
|
841
1095
|
if (fs.kind === "any") return "true";
|
|
842
1096
|
if (fs.kind === "empty") return "false";
|
|
@@ -852,6 +1106,30 @@ function asIIFE(stmts, valueVar, endVar, startPos, indent) {
|
|
|
852
1106
|
`${indent}})()`
|
|
853
1107
|
].join("\n");
|
|
854
1108
|
}
|
|
1109
|
+
function emitFallible(inner, ctx, pos) {
|
|
1110
|
+
const lbl = v(ctx, "_lbl");
|
|
1111
|
+
const okV = `${lbl}ok`;
|
|
1112
|
+
const valV = `${lbl}v`;
|
|
1113
|
+
const endV = `${lbl}e`;
|
|
1114
|
+
const savedLabel = ctx.failLabel;
|
|
1115
|
+
const savedIndent = ctx.indent;
|
|
1116
|
+
ctx.failLabel = lbl;
|
|
1117
|
+
ctx.indent = 0;
|
|
1118
|
+
const r = emit(inner, ctx, pos);
|
|
1119
|
+
ctx.failLabel = savedLabel;
|
|
1120
|
+
ctx.indent = savedIndent;
|
|
1121
|
+
const ind0 = ind(ctx);
|
|
1122
|
+
const stmts = [
|
|
1123
|
+
`${ind0}let ${okV} = false, ${valV}, ${endV} = ${pos}`,
|
|
1124
|
+
`${ind0}${lbl}: {`,
|
|
1125
|
+
...r.stmts,
|
|
1126
|
+
`${ind0} ${valV} = ${r.valueVar}`,
|
|
1127
|
+
`${ind0} ${endV} = ${r.endVar}`,
|
|
1128
|
+
`${ind0} ${okV} = true`,
|
|
1129
|
+
`${ind0}}`
|
|
1130
|
+
];
|
|
1131
|
+
return { stmts, okVar: okV, valVar: valV, endVar: endV };
|
|
1132
|
+
}
|
|
855
1133
|
function emitLit(def, ctx, pos) {
|
|
856
1134
|
const { value, caseInsensitive } = def;
|
|
857
1135
|
const len = value.length;
|
|
@@ -889,12 +1167,19 @@ function emitLit(def, ctx, pos) {
|
|
|
889
1167
|
`${ind(ctx)}const ${vv} = ${JSON.stringify(value)}`
|
|
890
1168
|
);
|
|
891
1169
|
}
|
|
892
|
-
|
|
1170
|
+
const endVar = len === 0 ? pos : `${pos} + ${len}`;
|
|
1171
|
+
stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
|
|
1172
|
+
return { stmts, valueVar: vv, endVar };
|
|
893
1173
|
}
|
|
894
1174
|
function emitRegex(def, ctx, pos) {
|
|
895
1175
|
const flags = "y" + def.flags.replace(/[gy]/g, "");
|
|
896
|
-
const
|
|
897
|
-
|
|
1176
|
+
const key = `${def.optimizedSource}/${flags}`;
|
|
1177
|
+
let rName = ctx.regexMap.get(key);
|
|
1178
|
+
if (rName === void 0) {
|
|
1179
|
+
rName = `_re${ctx.regexDecls.length}`;
|
|
1180
|
+
ctx.regexDecls.push(`const ${rName} = /${def.optimizedSource}/${flags}`);
|
|
1181
|
+
ctx.regexMap.set(key, rName);
|
|
1182
|
+
}
|
|
898
1183
|
const mv = v(ctx, "_m");
|
|
899
1184
|
const vv = v(ctx);
|
|
900
1185
|
const expectedStr = JSON.stringify(`/${def.source}/`);
|
|
@@ -904,7 +1189,9 @@ function emitRegex(def, ctx, pos) {
|
|
|
904
1189
|
`${ind(ctx)}if (${mv} === null) ${failStmt({ ...ctx, indent: 0 }, expectedStr, pos).trim()}`,
|
|
905
1190
|
`${ind(ctx)}const ${vv} = ${mv}[0]`
|
|
906
1191
|
];
|
|
907
|
-
|
|
1192
|
+
const endVar = `${pos} + ${vv}.length`;
|
|
1193
|
+
stmts.push(...emitLeafCapture(ctx, vv, pos, endVar));
|
|
1194
|
+
return { stmts, valueVar: vv, endVar };
|
|
908
1195
|
}
|
|
909
1196
|
function ensureTriviaFn(ctx) {
|
|
910
1197
|
const trivia2 = ctx.activeTrivia;
|
|
@@ -912,9 +1199,15 @@ function ensureTriviaFn(ctx) {
|
|
|
912
1199
|
const fnName = `_pf${ctx.namedParsers.size}`;
|
|
913
1200
|
ctx.namedParsers.set(trivia2, fnName);
|
|
914
1201
|
const savedIndent = ctx.indent;
|
|
1202
|
+
const savedFailLabel = ctx.failLabel;
|
|
1203
|
+
const savedTrivia = ctx.activeTrivia;
|
|
915
1204
|
ctx.indent = 1;
|
|
1205
|
+
ctx.failLabel = void 0;
|
|
1206
|
+
delete ctx.activeTrivia;
|
|
916
1207
|
const r = emit(trivia2, ctx, "_pos");
|
|
917
1208
|
ctx.indent = savedIndent;
|
|
1209
|
+
ctx.failLabel = savedFailLabel;
|
|
1210
|
+
if (savedTrivia) ctx.activeTrivia = savedTrivia;
|
|
918
1211
|
ctx.namedFnDecls.push([
|
|
919
1212
|
`function ${fnName}(input, _pos, _ctx) {`,
|
|
920
1213
|
...r.stmts,
|
|
@@ -934,12 +1227,17 @@ function emitSeq(def, ctx, pos) {
|
|
|
934
1227
|
const valueVars = [];
|
|
935
1228
|
for (let i = 0; i < def.parsers.length; i++) {
|
|
936
1229
|
if (i > 0 && ctx.activeTrivia) {
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1230
|
+
if (ctx.capturing) {
|
|
1231
|
+
const capFn = ensureTriviaCaptureFn(ctx);
|
|
1232
|
+
stmts.push(`${ind(ctx)}${curV} = ${capFn}(input, ${curV}, _ctx)`);
|
|
1233
|
+
} else {
|
|
1234
|
+
const trivFn = ensureTriviaFn(ctx);
|
|
1235
|
+
const tmp = v(ctx, "_trv");
|
|
1236
|
+
stmts.push(
|
|
1237
|
+
`${ind(ctx)}const ${tmp} = ${trivFn}(input, ${curV}, _ctx)`,
|
|
1238
|
+
`${ind(ctx)}if (${tmp}.ok) ${curV} = ${tmp}.span.end`
|
|
1239
|
+
);
|
|
1240
|
+
}
|
|
943
1241
|
}
|
|
944
1242
|
const r = emit(def.parsers[i], ctx, curV);
|
|
945
1243
|
stmts.push(...r.stmts, `${ind(ctx)}${curV} = ${r.endVar}`);
|
|
@@ -979,9 +1277,7 @@ function emitChoice(def, ctx, pos) {
|
|
|
979
1277
|
ctx.indent--;
|
|
980
1278
|
stmts.push(`${ind(ctx)}}`);
|
|
981
1279
|
}
|
|
982
|
-
stmts.push(
|
|
983
|
-
`${ind(ctx)}else return { ok: false, expected: ${allExpected}, span: { start: ${pos}, end: ${pos} } }`
|
|
984
|
-
);
|
|
1280
|
+
stmts.push(`${ind(ctx)}else ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`);
|
|
985
1281
|
return { stmts, valueVar: valV, endVar: endV };
|
|
986
1282
|
}
|
|
987
1283
|
return emitNonDisjoint(def, def.strategy, allExpected, ctx, pos);
|
|
@@ -989,17 +1285,21 @@ function emitChoice(def, ctx, pos) {
|
|
|
989
1285
|
function emitGreedyClassify(def, superIndex, allExpected, ctx, pos) {
|
|
990
1286
|
const superParser = def.parsers[superIndex];
|
|
991
1287
|
const regexDef = getCoreRegexDef(superParser);
|
|
992
|
-
const reIdx = ctx.regexDecls.length;
|
|
993
|
-
const reVar = `_re${reIdx}`;
|
|
994
1288
|
const cleanFlags = "y" + regexDef.flags.replace(/[gy]/g, "");
|
|
995
|
-
|
|
1289
|
+
const reKey = `${regexDef.source}/${cleanFlags}`;
|
|
1290
|
+
let reVar = ctx.regexMap.get(reKey);
|
|
1291
|
+
if (reVar === void 0) {
|
|
1292
|
+
reVar = `_re${ctx.regexDecls.length}`;
|
|
1293
|
+
ctx.regexDecls.push(`const ${reVar} = /${regexDef.source}/${cleanFlags}`);
|
|
1294
|
+
ctx.regexMap.set(reKey, reVar);
|
|
1295
|
+
}
|
|
996
1296
|
const matchV = v(ctx, "_gm");
|
|
997
1297
|
const wordV = v(ctx, "_gw");
|
|
998
1298
|
const endV = v(ctx, "_ge");
|
|
999
1299
|
const stmts = [
|
|
1000
1300
|
`${ind(ctx)}${reVar}.lastIndex = ${pos}`,
|
|
1001
1301
|
`${ind(ctx)}const ${matchV} = ${reVar}.exec(input)`,
|
|
1002
|
-
`${ind(ctx)}if (${matchV} === null)
|
|
1302
|
+
`${ind(ctx)}if (${matchV} === null) ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`,
|
|
1003
1303
|
`${ind(ctx)}const ${wordV} = ${matchV}[0]`,
|
|
1004
1304
|
`${ind(ctx)}const ${endV} = ${pos} + ${wordV}.length`
|
|
1005
1305
|
];
|
|
@@ -1039,7 +1339,7 @@ function emitLiteralsLongestFirst(def, sortedIndices, allExpected, ctx, pos) {
|
|
|
1039
1339
|
ctx.indent--;
|
|
1040
1340
|
stmts.push(`${ind(ctx)}}`);
|
|
1041
1341
|
}
|
|
1042
|
-
stmts.push(`${ind(ctx)}else
|
|
1342
|
+
stmts.push(`${ind(ctx)}else ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`);
|
|
1043
1343
|
return { stmts, valueVar: valV, endVar: endV };
|
|
1044
1344
|
}
|
|
1045
1345
|
function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
@@ -1050,40 +1350,60 @@ function emitFirstMatch(def, allExpected, ctx, pos) {
|
|
|
1050
1350
|
const gate = def.gates[i];
|
|
1051
1351
|
const autoNot = def.autoNot[i];
|
|
1052
1352
|
const savedIndent = ctx.indent;
|
|
1353
|
+
const savedFailLabel = ctx.failLabel;
|
|
1053
1354
|
ctx.indent = 0;
|
|
1355
|
+
ctx.failLabel = void 0;
|
|
1054
1356
|
const r = emit(p, ctx, pos);
|
|
1055
1357
|
ctx.indent = savedIndent;
|
|
1358
|
+
ctx.failLabel = savedFailLabel;
|
|
1056
1359
|
const iife = asIIFE(r.stmts, r.valueVar, r.endVar, pos, ind(ctx));
|
|
1057
1360
|
let gateCond = null;
|
|
1058
1361
|
if (gate) {
|
|
1059
1362
|
const gateIdx = ctx.mapFns.length;
|
|
1060
1363
|
ctx.mapFns.push(gate);
|
|
1061
|
-
gateCond = `_mf[${gateIdx}](_ctx.
|
|
1364
|
+
gateCond = `_mf[${gateIdx}](_ctx.state)`;
|
|
1062
1365
|
}
|
|
1063
1366
|
const skipCond = gateCond ? `!${resV}?.ok && ${gateCond}` : `!${resV}?.ok`;
|
|
1367
|
+
const markLeaves = ctx.capturing ? v(ctx, "_cml") : null;
|
|
1368
|
+
const markRaw = ctx.capturing ? v(ctx, "_cmr") : null;
|
|
1369
|
+
const rollback = markLeaves ? `if (_ctx._cstLeaves) _ctx._cstLeaves.length = ${markLeaves}; if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markRaw}` : "";
|
|
1370
|
+
const markStmts = markLeaves ? [
|
|
1371
|
+
`${ind(ctx)} const ${markLeaves} = _ctx._cstLeaves?.length ?? 0`,
|
|
1372
|
+
`${ind(ctx)} const ${markRaw} = _ctx._cstRawChildren?.length ?? 0`
|
|
1373
|
+
] : [];
|
|
1374
|
+
const rollbackStmt = rollback ? `${ind(ctx)} if (!${resV}?.ok) { ${rollback} }` : "";
|
|
1064
1375
|
if (autoNot && autoNot.length > 0) {
|
|
1065
1376
|
const tmp = v(ctx, "_ct");
|
|
1066
1377
|
const anCode = v(ctx, "_anc");
|
|
1067
1378
|
const rejectCond = autoNot.map(
|
|
1068
1379
|
(check) => check.kind === "firstSet" ? firstSetCond(anCode, check.set) : `input.startsWith(${JSON.stringify(check.value)}, ${tmp}.span.end)`
|
|
1069
1380
|
).join(" || ");
|
|
1381
|
+
stmts.push(`${ind(ctx)}if (${skipCond}) {`);
|
|
1382
|
+
stmts.push(...markStmts);
|
|
1070
1383
|
stmts.push(
|
|
1071
|
-
`${ind(ctx)}
|
|
1384
|
+
`${ind(ctx)} const ${tmp} = (() => { try { return ${iife} } catch {} })()`,
|
|
1385
|
+
`${ind(ctx)} if (${tmp}?.ok) { const ${anCode} = ${tmp}.span.end < input.length ? input.charCodeAt(${tmp}.span.end) : -1; if (!(${rejectCond})) ${resV} = ${tmp} }`
|
|
1072
1386
|
);
|
|
1387
|
+
if (rollbackStmt) stmts.push(rollbackStmt);
|
|
1388
|
+
stmts.push(`${ind(ctx)}}`);
|
|
1073
1389
|
} else {
|
|
1074
|
-
stmts.push(`${ind(ctx)}if (${skipCond}) {
|
|
1390
|
+
stmts.push(`${ind(ctx)}if (${skipCond}) {`);
|
|
1391
|
+
stmts.push(...markStmts);
|
|
1392
|
+
stmts.push(`${ind(ctx)} try { ${resV} = ${iife} } catch {}`);
|
|
1393
|
+
if (rollbackStmt) stmts.push(rollbackStmt);
|
|
1394
|
+
stmts.push(`${ind(ctx)}}`);
|
|
1075
1395
|
}
|
|
1076
1396
|
}
|
|
1077
|
-
stmts.push(
|
|
1078
|
-
`${ind(ctx)}if (!${resV}?.ok) return { ok: false, expected: ${allExpected}, span: { start: ${pos}, end: ${pos} } }`
|
|
1079
|
-
);
|
|
1397
|
+
stmts.push(`${ind(ctx)}if (!${resV}?.ok) ${failArr({ ...ctx, indent: 0 }, allExpected, pos)}`);
|
|
1080
1398
|
return { stmts, valueVar: `${resV}.value`, endVar: `${resV}.span.end` };
|
|
1081
1399
|
}
|
|
1082
1400
|
function emitNonDisjoint(def, strategy, allExpected, ctx, pos) {
|
|
1083
|
-
if (
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1401
|
+
if (!ctx.capturing) {
|
|
1402
|
+
if (strategy.tag === "greedyClassify")
|
|
1403
|
+
return emitGreedyClassify(def, strategy.superIndex, allExpected, ctx, pos);
|
|
1404
|
+
if (strategy.tag === "literalsLongestFirst")
|
|
1405
|
+
return emitLiteralsLongestFirst(def, strategy.sortedIndices, allExpected, ctx, pos);
|
|
1406
|
+
}
|
|
1087
1407
|
return emitFirstMatch(def, allExpected, ctx, pos);
|
|
1088
1408
|
}
|
|
1089
1409
|
function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
@@ -1092,6 +1412,7 @@ function emitTransformChain(p, baseValue, endV, startPos, ctx) {
|
|
|
1092
1412
|
const innerR = emitTransformChain(def.parser, baseValue, endV, startPos, ctx);
|
|
1093
1413
|
const fnIdx = ctx.mapFns.length;
|
|
1094
1414
|
ctx.mapFns.push(def.fn);
|
|
1415
|
+
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
1095
1416
|
const vv = v(ctx);
|
|
1096
1417
|
return {
|
|
1097
1418
|
stmts: [...innerR.stmts, `${ind(ctx)}const ${vv} = _mf[${fnIdx}](${innerR.valueVar}, { start: ${startPos}, end: ${endV} })`],
|
|
@@ -1119,13 +1440,6 @@ function emitMany(def, ctx, pos) {
|
|
|
1119
1440
|
`${ind(ctx)}const ${arrV} = []`,
|
|
1120
1441
|
`${ind(ctx)}let ${curV} = ${pos}`
|
|
1121
1442
|
];
|
|
1122
|
-
const emitInnerIIFE = () => {
|
|
1123
|
-
const savedIndent = ctx.indent;
|
|
1124
|
-
ctx.indent = 0;
|
|
1125
|
-
const r = emit(def.parser, ctx, curV);
|
|
1126
|
-
ctx.indent = savedIndent;
|
|
1127
|
-
return asIIFE(r.stmts, r.valueVar, r.endVar, curV, ind(ctx) + " ");
|
|
1128
|
-
};
|
|
1129
1443
|
if (def.min === 1) {
|
|
1130
1444
|
const firstR = emit(def.parser, ctx, curV);
|
|
1131
1445
|
stmts.push(...firstR.stmts);
|
|
@@ -1136,11 +1450,35 @@ function emitMany(def, ctx, pos) {
|
|
|
1136
1450
|
}
|
|
1137
1451
|
stmts.push(`${ind(ctx)}while (${curV} < input.length) {`);
|
|
1138
1452
|
ctx.indent++;
|
|
1453
|
+
let itemPos = curV;
|
|
1454
|
+
let rollback = "";
|
|
1455
|
+
if (ctx.activeTrivia) {
|
|
1456
|
+
if (ctx.capturing) {
|
|
1457
|
+
const capFn = ensureTriviaCaptureFn(ctx);
|
|
1458
|
+
const markV = v(ctx, "_mk");
|
|
1459
|
+
const npV = v(ctx, "_np");
|
|
1460
|
+
stmts.push(
|
|
1461
|
+
`${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`,
|
|
1462
|
+
`${ind(ctx)}const ${npV} = ${capFn}(input, ${curV}, _ctx)`
|
|
1463
|
+
);
|
|
1464
|
+
itemPos = npV;
|
|
1465
|
+
rollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; `;
|
|
1466
|
+
} else {
|
|
1467
|
+
const trivFn = ensureTriviaFn(ctx);
|
|
1468
|
+
const npV = v(ctx, "_np");
|
|
1469
|
+
stmts.push(
|
|
1470
|
+
`${ind(ctx)}let ${npV} = ${curV}`,
|
|
1471
|
+
`${ind(ctx)}{ const _trv = ${trivFn}(input, ${npV}, _ctx); if (_trv.ok) ${npV} = _trv.span.end }`
|
|
1472
|
+
);
|
|
1473
|
+
itemPos = npV;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
const { stmts: iterStmts, okVar: iterOk, valVar: iterVal, endVar: iterEnd } = emitFallible(def.parser, ctx, itemPos);
|
|
1139
1477
|
stmts.push(
|
|
1140
|
-
|
|
1141
|
-
`${ind(ctx)}if (
|
|
1142
|
-
`${ind(ctx)}${arrV}.push(
|
|
1143
|
-
`${ind(ctx)}${curV} =
|
|
1478
|
+
...iterStmts,
|
|
1479
|
+
`${ind(ctx)}if (!${iterOk} || ${iterEnd} === ${curV}) { ${rollback}break }`,
|
|
1480
|
+
`${ind(ctx)}${arrV}.push(${iterVal})`,
|
|
1481
|
+
`${ind(ctx)}${curV} = ${iterEnd}`
|
|
1144
1482
|
);
|
|
1145
1483
|
ctx.indent--;
|
|
1146
1484
|
stmts.push(`${ind(ctx)}}`);
|
|
@@ -1149,56 +1487,80 @@ function emitMany(def, ctx, pos) {
|
|
|
1149
1487
|
function emitOptional(def, ctx, pos) {
|
|
1150
1488
|
const valV = v(ctx, "_opt");
|
|
1151
1489
|
const endV = v(ctx, "_opte");
|
|
1152
|
-
const
|
|
1153
|
-
|
|
1154
|
-
const r = emit(def.parser, ctx, pos);
|
|
1155
|
-
ctx.indent = savedIndent;
|
|
1156
|
-
const iife = asIIFE(r.stmts, r.valueVar, r.endVar, pos, ind(ctx));
|
|
1157
|
-
const resV = v(ctx, "_optr");
|
|
1490
|
+
const { stmts: lblStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
1491
|
+
const ind0 = ind(ctx);
|
|
1158
1492
|
const stmts = [
|
|
1159
|
-
|
|
1160
|
-
`${
|
|
1161
|
-
`${
|
|
1493
|
+
...lblStmts,
|
|
1494
|
+
`${ind0}const ${valV} = ${okVar} ? ${valVar} : null`,
|
|
1495
|
+
`${ind0}const ${endV} = ${okVar} ? ${endVar} : ${pos}`
|
|
1162
1496
|
];
|
|
1163
1497
|
return { stmts, valueVar: valV, endVar: endV };
|
|
1164
1498
|
}
|
|
1165
1499
|
function emitSepBy(_p, def, ctx, pos) {
|
|
1166
1500
|
const arrV = v(ctx, "_arr");
|
|
1167
1501
|
const curV = v(ctx, "_cur");
|
|
1168
|
-
const
|
|
1169
|
-
const saved = ctx.indent;
|
|
1170
|
-
ctx.indent = 0;
|
|
1171
|
-
const r = emit(inner, ctx, posExpr);
|
|
1172
|
-
ctx.indent = saved;
|
|
1173
|
-
return asIIFE(r.stmts, r.valueVar, r.endVar, posExpr, ind(ctx));
|
|
1174
|
-
};
|
|
1175
|
-
const firstR_saved = ctx.indent;
|
|
1176
|
-
ctx.indent = 0;
|
|
1177
|
-
const firstR = emit(def.parser, ctx, pos);
|
|
1178
|
-
ctx.indent = firstR_saved;
|
|
1179
|
-
const firstV = v(ctx, "_sb0");
|
|
1180
|
-
const sepV = v(ctx, "_sbs");
|
|
1181
|
-
const nextV = v(ctx, "_sbn");
|
|
1502
|
+
const { stmts: firstStmts, okVar: firstOk, valVar: firstVal, endVar: firstEnd } = emitFallible(def.parser, ctx, pos);
|
|
1182
1503
|
const stmts = [
|
|
1183
1504
|
`${ind(ctx)}const ${arrV} = []`,
|
|
1184
1505
|
`${ind(ctx)}let ${curV} = ${pos}`,
|
|
1185
|
-
|
|
1186
|
-
`${ind(ctx)}if (${
|
|
1506
|
+
...firstStmts,
|
|
1507
|
+
`${ind(ctx)}if (${firstOk}) {`
|
|
1187
1508
|
];
|
|
1188
1509
|
ctx.indent++;
|
|
1189
1510
|
stmts.push(
|
|
1190
|
-
`${ind(ctx)}${arrV}.push(${
|
|
1191
|
-
`${ind(ctx)}${curV} = ${
|
|
1511
|
+
`${ind(ctx)}${arrV}.push(${firstVal})`,
|
|
1512
|
+
`${ind(ctx)}${curV} = ${firstEnd}`,
|
|
1192
1513
|
`${ind(ctx)}while (${curV} < input.length) {`
|
|
1193
1514
|
);
|
|
1194
1515
|
ctx.indent++;
|
|
1516
|
+
let rollback = "";
|
|
1517
|
+
if (ctx.capturing) {
|
|
1518
|
+
const markV = v(ctx, "_mk");
|
|
1519
|
+
stmts.push(`${ind(ctx)}const ${markV} = _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0`);
|
|
1520
|
+
rollback = `if (_ctx._cstRawChildren) _ctx._cstRawChildren.length = ${markV}; `;
|
|
1521
|
+
}
|
|
1522
|
+
let sepAtPos = curV;
|
|
1523
|
+
if (ctx.activeTrivia) {
|
|
1524
|
+
if (ctx.capturing) {
|
|
1525
|
+
const capFn = ensureTriviaCaptureFn(ctx);
|
|
1526
|
+
const spV = v(ctx, "_sp");
|
|
1527
|
+
stmts.push(`${ind(ctx)}const ${spV} = ${capFn}(input, ${curV}, _ctx)`);
|
|
1528
|
+
sepAtPos = spV;
|
|
1529
|
+
} else {
|
|
1530
|
+
const trivFn = ensureTriviaFn(ctx);
|
|
1531
|
+
const spV = v(ctx, "_sp");
|
|
1532
|
+
stmts.push(
|
|
1533
|
+
`${ind(ctx)}let ${spV} = ${curV}`,
|
|
1534
|
+
`${ind(ctx)}{ const _trv = ${trivFn}(input, ${spV}, _ctx); if (_trv.ok) ${spV} = _trv.span.end }`
|
|
1535
|
+
);
|
|
1536
|
+
sepAtPos = spV;
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
const { stmts: sepStmts, okVar: sepOk, endVar: sepEnd } = emitFallible(def.separator, ctx, sepAtPos);
|
|
1540
|
+
stmts.push(...sepStmts, `${ind(ctx)}if (!${sepOk}) { ${rollback}break }`);
|
|
1541
|
+
let nextAtPos = sepEnd;
|
|
1542
|
+
if (ctx.activeTrivia) {
|
|
1543
|
+
if (ctx.capturing) {
|
|
1544
|
+
const capFn = ensureTriviaCaptureFn(ctx);
|
|
1545
|
+
const npV = v(ctx, "_np");
|
|
1546
|
+
stmts.push(`${ind(ctx)}const ${npV} = ${capFn}(input, ${sepEnd}, _ctx)`);
|
|
1547
|
+
nextAtPos = npV;
|
|
1548
|
+
} else {
|
|
1549
|
+
const trivFn = ensureTriviaFn(ctx);
|
|
1550
|
+
const npV = v(ctx, "_np");
|
|
1551
|
+
stmts.push(
|
|
1552
|
+
`${ind(ctx)}let ${npV} = ${sepEnd}`,
|
|
1553
|
+
`${ind(ctx)}{ const _trv = ${trivFn}(input, ${npV}, _ctx); if (_trv.ok) ${npV} = _trv.span.end }`
|
|
1554
|
+
);
|
|
1555
|
+
nextAtPos = npV;
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
const { stmts: nextStmts, okVar: nextOk, valVar: nextVal, endVar: nextEnd } = emitFallible(def.parser, ctx, nextAtPos);
|
|
1195
1559
|
stmts.push(
|
|
1196
|
-
|
|
1197
|
-
`${ind(ctx)}if (!${
|
|
1198
|
-
`${ind(ctx)}
|
|
1199
|
-
`${ind(ctx)}
|
|
1200
|
-
`${ind(ctx)}${arrV}.push(${nextV}.value)`,
|
|
1201
|
-
`${ind(ctx)}${curV} = ${nextV}.span.end`
|
|
1560
|
+
...nextStmts,
|
|
1561
|
+
`${ind(ctx)}if (!${nextOk}) { ${rollback}break }`,
|
|
1562
|
+
`${ind(ctx)}${arrV}.push(${nextVal})`,
|
|
1563
|
+
`${ind(ctx)}${curV} = ${nextEnd}`
|
|
1202
1564
|
);
|
|
1203
1565
|
ctx.indent--;
|
|
1204
1566
|
stmts.push(`${ind(ctx)}}`);
|
|
@@ -1207,13 +1569,6 @@ function emitSepBy(_p, def, ctx, pos) {
|
|
|
1207
1569
|
return { stmts, valueVar: arrV, endVar: curV };
|
|
1208
1570
|
}
|
|
1209
1571
|
function emitScanTo(def, ctx, pos) {
|
|
1210
|
-
const safeIIFE = (inner, posExpr) => {
|
|
1211
|
-
const saved = ctx.indent;
|
|
1212
|
-
ctx.indent = 0;
|
|
1213
|
-
const r = emit(inner, ctx, posExpr);
|
|
1214
|
-
ctx.indent = saved;
|
|
1215
|
-
return `(() => { try { return ${asIIFE(r.stmts, r.valueVar, r.endVar, posExpr, ind(ctx))} } catch { return null } })()`;
|
|
1216
|
-
};
|
|
1217
1572
|
const curV = v(ctx, "_stcur");
|
|
1218
1573
|
const foundV = v(ctx, "_stfnd");
|
|
1219
1574
|
const stmts = [
|
|
@@ -1222,18 +1577,19 @@ function emitScanTo(def, ctx, pos) {
|
|
|
1222
1577
|
`${ind(ctx)}while (${curV} < input.length) {`
|
|
1223
1578
|
];
|
|
1224
1579
|
ctx.indent++;
|
|
1225
|
-
const
|
|
1226
|
-
stmts.
|
|
1227
|
-
|
|
1228
|
-
`${ind(ctx)}if (${sentV}?.ok) { ${foundV} = true; break }`
|
|
1229
|
-
);
|
|
1580
|
+
const probeCtx = { ...ctx, capturing: false };
|
|
1581
|
+
const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, probeCtx, curV);
|
|
1582
|
+
stmts.push(...sentStmts, `${ind(ctx)}if (${sentOk}) { ${foundV} = true; break }`);
|
|
1230
1583
|
if (def.skip.length > 0) {
|
|
1231
1584
|
const advV = v(ctx, "_stadv");
|
|
1232
1585
|
stmts.push(`${ind(ctx)}let ${advV} = false`);
|
|
1233
1586
|
for (const skipper of def.skip) {
|
|
1234
|
-
const
|
|
1587
|
+
const { stmts: skStmts, okVar: skOk, endVar: skEnd } = emitFallible(skipper, probeCtx, curV);
|
|
1235
1588
|
stmts.push(
|
|
1236
|
-
`${ind(ctx)}if (!${advV}) {
|
|
1589
|
+
`${ind(ctx)}if (!${advV}) {`,
|
|
1590
|
+
...skStmts,
|
|
1591
|
+
`${ind(ctx)} if (${skOk} && ${skEnd} > ${curV}) { ${curV} = ${skEnd}; ${advV} = true }`,
|
|
1592
|
+
`${ind(ctx)}}`
|
|
1237
1593
|
);
|
|
1238
1594
|
}
|
|
1239
1595
|
stmts.push(`${ind(ctx)}if (!${advV}) ${curV}++`);
|
|
@@ -1245,14 +1601,54 @@ function emitScanTo(def, ctx, pos) {
|
|
|
1245
1601
|
if (!def.orEOF) {
|
|
1246
1602
|
const sentDef = def.sentinel._def;
|
|
1247
1603
|
const expectedStr = sentDef.tag === "literal" ? JSON.stringify([JSON.stringify(sentDef.value)]) : `["sentinel"]`;
|
|
1248
|
-
stmts.push(
|
|
1249
|
-
`${ind(ctx)}if (!${foundV}) return { ok: false, expected: ${expectedStr}, span: { start: ${pos}, end: ${curV} } }`
|
|
1250
|
-
);
|
|
1604
|
+
stmts.push(`${ind(ctx)}if (!${foundV}) ${failArr({ ...ctx, indent: 0 }, expectedStr, pos)}`);
|
|
1251
1605
|
}
|
|
1252
1606
|
const valV = v(ctx);
|
|
1253
1607
|
stmts.push(`${ind(ctx)}const ${valV} = input.slice(${pos}, ${curV})`);
|
|
1608
|
+
if (ctx.capturing) {
|
|
1609
|
+
const cap = emitLeafCapture(ctx, valV, pos, curV).map((s) => " " + s);
|
|
1610
|
+
stmts.push(`${ind(ctx)}if (${curV} > ${pos}) {`, ...cap, `${ind(ctx)}}`);
|
|
1611
|
+
}
|
|
1254
1612
|
return { stmts, valueVar: valV, endVar: curV };
|
|
1255
1613
|
}
|
|
1614
|
+
function emitNot(def, ctx, pos) {
|
|
1615
|
+
const { stmts, okVar } = emitFallible(def.parser, ctx, pos);
|
|
1616
|
+
return {
|
|
1617
|
+
stmts: [
|
|
1618
|
+
...stmts,
|
|
1619
|
+
`${ind(ctx)}if (${okVar}) ${failStmt({ ...ctx, indent: 0 }, '"not"', pos).trim()}`
|
|
1620
|
+
],
|
|
1621
|
+
valueVar: "null",
|
|
1622
|
+
endVar: pos
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1625
|
+
function emitNode(def, ctx, pos) {
|
|
1626
|
+
const fnIdx = ctx.buildFns.length;
|
|
1627
|
+
ctx.buildFns.push(def.build);
|
|
1628
|
+
ctx.buildSrcs.push(def.buildSrc ?? null);
|
|
1629
|
+
const i = ind(ctx);
|
|
1630
|
+
const chV = v(ctx, "_ch");
|
|
1631
|
+
const rawV = v(ctx, "_raw");
|
|
1632
|
+
const sc = v(ctx, "_sc"), sl = v(ctx, "_sl"), sr = v(ctx, "_sr"), st = v(ctx, "_st");
|
|
1633
|
+
const stmts = [
|
|
1634
|
+
`${i}const ${chV} = [], ${rawV} = []`,
|
|
1635
|
+
`${i}const ${sc} = _ctx._cstChildren, ${sl} = _ctx._cstLeaves, ${sr} = _ctx._cstRawChildren, ${st} = _ctx.captureTrivia`,
|
|
1636
|
+
`${i}_ctx._cstChildren = ${chV}; _ctx._cstLeaves = ${chV}; _ctx._cstRawChildren = ${rawV}; _ctx.captureTrivia = true`
|
|
1637
|
+
];
|
|
1638
|
+
const { stmts: innerStmts, okVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
1639
|
+
stmts.push(...innerStmts);
|
|
1640
|
+
stmts.push(`${i}_ctx._cstChildren = ${sc}; _ctx._cstLeaves = ${sl}; _ctx._cstRawChildren = ${sr}; _ctx.captureTrivia = ${st}`);
|
|
1641
|
+
stmts.push(`${i}if (!${okVar}) ${failStmt({ ...ctx, indent: 0 }, '"node"', pos).trim()}`);
|
|
1642
|
+
const ndV = v(ctx, "_nd");
|
|
1643
|
+
stmts.push(
|
|
1644
|
+
`${i}const ${ndV} = _build[${fnIdx}](${chV}, ${rawV}, { start: ${pos}, end: ${endVar} })`,
|
|
1645
|
+
// Record into the caller's collectors (the node in children; the node, or a
|
|
1646
|
+
// spanned leaf if build collapsed to a non-node value, in rawChildren).
|
|
1647
|
+
`${i}if (${sc}) ${sc}.push(${ndV})`,
|
|
1648
|
+
`${i}if (${sr}) ${sr}.push((typeof ${ndV} === 'object' && ${ndV} !== null && ${ndV}._tag === 'node') ? ${ndV} : { _tag: 'leaf', value: typeof ${ndV} === 'string' ? ${ndV} : '', span: { start: ${pos}, end: ${endVar} } })`
|
|
1649
|
+
);
|
|
1650
|
+
return { stmts, valueVar: ndV, endVar };
|
|
1651
|
+
}
|
|
1256
1652
|
function emitRuntimeFallback(parser2, ctx, pos) {
|
|
1257
1653
|
const idx = ctx.runtimeParsers.length;
|
|
1258
1654
|
ctx.runtimeParsers.push(parser2);
|
|
@@ -1261,7 +1657,7 @@ function emitRuntimeFallback(parser2, ctx, pos) {
|
|
|
1261
1657
|
const ev = v(ctx, "_rte");
|
|
1262
1658
|
const stmts = [
|
|
1263
1659
|
`${ind(ctx)}const ${rv} = _rp[${idx}].parse(input, ${pos}, _ctx)`,
|
|
1264
|
-
`${ind(ctx)}if (!${rv}.ok)
|
|
1660
|
+
`${ind(ctx)}if (!${rv}.ok) ${failStmt({ ...ctx, indent: 0 }, '"runtime"', pos).trim()}`,
|
|
1265
1661
|
`${ind(ctx)}const ${vv} = ${rv}.value`,
|
|
1266
1662
|
`${ind(ctx)}const ${ev} = ${rv}.span.end`
|
|
1267
1663
|
];
|
|
@@ -1279,9 +1675,12 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
1279
1675
|
return emitRuntimeFallback(p, ctx, pos);
|
|
1280
1676
|
}
|
|
1281
1677
|
const savedIndent = ctx.indent;
|
|
1678
|
+
const savedFailLabel = ctx.failLabel;
|
|
1282
1679
|
ctx.indent = 1;
|
|
1680
|
+
ctx.failLabel = void 0;
|
|
1283
1681
|
const r = emit(resolved, ctx, "_pos");
|
|
1284
1682
|
ctx.indent = savedIndent;
|
|
1683
|
+
ctx.failLabel = savedFailLabel;
|
|
1285
1684
|
ctx.namedFnDecls.push([
|
|
1286
1685
|
`function ${fnName2}(input, _pos, _ctx) {`,
|
|
1287
1686
|
...r.stmts,
|
|
@@ -1296,7 +1695,7 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
1296
1695
|
return {
|
|
1297
1696
|
stmts: [
|
|
1298
1697
|
`${ind(ctx)}const ${rv} = ${fnName}(input, ${pos}, _ctx)`,
|
|
1299
|
-
`${ind(ctx)}if (!${rv}.ok)
|
|
1698
|
+
`${ind(ctx)}if (!${rv}.ok) ${failStmt({ ...ctx, indent: 0 }, '"parser"', pos).trim()}`,
|
|
1300
1699
|
`${ind(ctx)}const ${vv} = ${rv}.value`,
|
|
1301
1700
|
`${ind(ctx)}const ${ev} = ${rv}.span.end`
|
|
1302
1701
|
],
|
|
@@ -1304,6 +1703,33 @@ function emitLazy(p, def, ctx, pos) {
|
|
|
1304
1703
|
endVar: ev
|
|
1305
1704
|
};
|
|
1306
1705
|
}
|
|
1706
|
+
function emitRecover(def, ctx, pos) {
|
|
1707
|
+
const { stmts: innerStmts, okVar, valVar, endVar } = emitFallible(def.parser, ctx, pos);
|
|
1708
|
+
const ind0 = ind(ctx);
|
|
1709
|
+
const scanV = v(ctx, "_sc");
|
|
1710
|
+
const errV = v(ctx, "_err");
|
|
1711
|
+
const savedIndent = ctx.indent;
|
|
1712
|
+
ctx.indent += 2;
|
|
1713
|
+
const { stmts: sentStmts, okVar: sentOk } = emitFallible(def.sentinel, ctx, scanV);
|
|
1714
|
+
ctx.indent = savedIndent;
|
|
1715
|
+
const stmts = [
|
|
1716
|
+
...innerStmts,
|
|
1717
|
+
`${ind0}if (!${okVar}) {`,
|
|
1718
|
+
`${ind0} let ${scanV} = ${pos}`,
|
|
1719
|
+
`${ind0} while (${scanV} < input.length) {`,
|
|
1720
|
+
...sentStmts,
|
|
1721
|
+
`${ind0} if (${sentOk}) break`,
|
|
1722
|
+
`${ind0} ${scanV}++`,
|
|
1723
|
+
`${ind0} }`,
|
|
1724
|
+
`${ind0} const ${errV} = { _tag: 'parseError', span: { start: ${pos}, end: ${scanV} }, expected: [] }`,
|
|
1725
|
+
`${ind0} if (_ctx._errors) _ctx._errors.push(${errV})`,
|
|
1726
|
+
`${ind0} ${valVar} = ${errV}`,
|
|
1727
|
+
`${ind0} ${endVar} = ${scanV}`,
|
|
1728
|
+
`${ind0} ${okVar} = true`,
|
|
1729
|
+
`${ind0}}`
|
|
1730
|
+
];
|
|
1731
|
+
return { stmts, valueVar: valVar, endVar };
|
|
1732
|
+
}
|
|
1307
1733
|
function emit(p, ctx, pos) {
|
|
1308
1734
|
const def = p._def;
|
|
1309
1735
|
switch (def.tag) {
|
|
@@ -1326,6 +1752,7 @@ function emit(p, ctx, pos) {
|
|
|
1326
1752
|
const inner = emit(def.parser, ctx, pos);
|
|
1327
1753
|
const fnIdx = ctx.mapFns.length;
|
|
1328
1754
|
ctx.mapFns.push(def.fn);
|
|
1755
|
+
ctx.mapFnSrcs.push(def.fnSrc ?? null);
|
|
1329
1756
|
const mv = v(ctx, "_mapped");
|
|
1330
1757
|
return {
|
|
1331
1758
|
stmts: [
|
|
@@ -1366,15 +1793,21 @@ function emit(p, ctx, pos) {
|
|
|
1366
1793
|
else ctx.activeTrivia = savedTrivia;
|
|
1367
1794
|
return r;
|
|
1368
1795
|
}
|
|
1796
|
+
case "not":
|
|
1797
|
+
return emitNot(def, ctx, pos);
|
|
1798
|
+
case "node":
|
|
1799
|
+
return emitNode(def, ctx, pos);
|
|
1369
1800
|
case "scanTo":
|
|
1370
1801
|
return emitScanTo(def, ctx, pos);
|
|
1802
|
+
case "recover":
|
|
1803
|
+
return emitRecover(def, ctx, pos);
|
|
1371
1804
|
case "guard": {
|
|
1372
1805
|
const fnIdx = ctx.mapFns.length;
|
|
1373
1806
|
ctx.mapFns.push(def.predicate);
|
|
1374
1807
|
const vv = v(ctx);
|
|
1375
1808
|
return {
|
|
1376
1809
|
stmts: [
|
|
1377
|
-
`${ind(ctx)}if (!_mf[${fnIdx}](_ctx.
|
|
1810
|
+
`${ind(ctx)}if (!_mf[${fnIdx}](_ctx.state)) ${failStmt({ ...ctx, indent: 0 }, '"gate"', pos).trim()}`,
|
|
1378
1811
|
`${ind(ctx)}const ${vv} = null`
|
|
1379
1812
|
],
|
|
1380
1813
|
valueVar: vv,
|
|
@@ -1390,9 +1823,12 @@ function emit(p, ctx, pos) {
|
|
|
1390
1823
|
const fnName = `_wcf${ctx.namedParsers.size}`;
|
|
1391
1824
|
ctx.namedParsers.set(innerParser, fnName);
|
|
1392
1825
|
const savedIndent = ctx.indent;
|
|
1826
|
+
const savedFailLabel = ctx.failLabel;
|
|
1393
1827
|
ctx.indent = 1;
|
|
1828
|
+
ctx.failLabel = void 0;
|
|
1394
1829
|
const innerR = emit(innerParser, ctx, "_pos");
|
|
1395
1830
|
ctx.indent = savedIndent;
|
|
1831
|
+
ctx.failLabel = savedFailLabel;
|
|
1396
1832
|
ctx.namedFnDecls.push([
|
|
1397
1833
|
`function ${fnName}(input, _pos, _ctx) {`,
|
|
1398
1834
|
...innerR.stmts,
|
|
@@ -1406,8 +1842,8 @@ function emit(p, ctx, pos) {
|
|
|
1406
1842
|
const ev = v(ctx, "_wce");
|
|
1407
1843
|
return {
|
|
1408
1844
|
stmts: [
|
|
1409
|
-
`${ind(ctx)}const ${rv} = ${fn}(input, ${pos}, { ..._ctx,
|
|
1410
|
-
`${ind(ctx)}if (!${rv}.ok)
|
|
1845
|
+
`${ind(ctx)}const ${rv} = ${fn}(input, ${pos}, { ..._ctx, state: _mf[${evIdx}]() })`,
|
|
1846
|
+
`${ind(ctx)}if (!${rv}.ok) ${failStmt({ ...ctx, indent: 0 }, '"withCtx"', pos).trim()}`,
|
|
1411
1847
|
`${ind(ctx)}const ${vv} = ${rv}.value`,
|
|
1412
1848
|
`${ind(ctx)}const ${ev} = ${rv}.span.end`
|
|
1413
1849
|
],
|
|
@@ -1419,16 +1855,61 @@ function emit(p, ctx, pos) {
|
|
|
1419
1855
|
return emitRuntimeFallback(p, ctx, pos);
|
|
1420
1856
|
}
|
|
1421
1857
|
}
|
|
1422
|
-
function
|
|
1858
|
+
function hasNodeDef(p, seen = /* @__PURE__ */ new Set()) {
|
|
1859
|
+
if (seen.has(p)) return false;
|
|
1860
|
+
seen.add(p);
|
|
1861
|
+
const d = p._def;
|
|
1862
|
+
switch (d.tag) {
|
|
1863
|
+
case "node":
|
|
1864
|
+
return true;
|
|
1865
|
+
case "lazy": {
|
|
1866
|
+
try {
|
|
1867
|
+
return hasNodeDef(d.thunk(), seen);
|
|
1868
|
+
} catch {
|
|
1869
|
+
return false;
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
case "grammar":
|
|
1873
|
+
case "trivia":
|
|
1874
|
+
case "optional":
|
|
1875
|
+
case "many":
|
|
1876
|
+
case "oneOrMore":
|
|
1877
|
+
case "not":
|
|
1878
|
+
case "transform":
|
|
1879
|
+
return hasNodeDef(d.parser, seen);
|
|
1880
|
+
case "skip":
|
|
1881
|
+
return hasNodeDef(d.main, seen) || hasNodeDef(d.skipped, seen);
|
|
1882
|
+
case "sequence":
|
|
1883
|
+
case "choice":
|
|
1884
|
+
return d.parsers.some((x) => hasNodeDef(x, seen));
|
|
1885
|
+
case "sepBy":
|
|
1886
|
+
return hasNodeDef(d.parser, seen) || hasNodeDef(d.separator, seen);
|
|
1887
|
+
case "scanTo":
|
|
1888
|
+
return hasNodeDef(d.sentinel, seen) || d.skip.some((x) => hasNodeDef(x, seen));
|
|
1889
|
+
case "recover":
|
|
1890
|
+
return hasNodeDef(d.parser, seen) || hasNodeDef(d.sentinel, seen);
|
|
1891
|
+
case "withCtx":
|
|
1892
|
+
return hasNodeDef(d.parser, seen);
|
|
1893
|
+
default:
|
|
1894
|
+
return false;
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
function compile(parser2, mapFnSources) {
|
|
1423
1898
|
const ctx = {
|
|
1424
1899
|
vars: 0,
|
|
1425
1900
|
indent: 1,
|
|
1426
1901
|
regexDecls: [],
|
|
1902
|
+
regexMap: /* @__PURE__ */ new Map(),
|
|
1427
1903
|
mapFns: [],
|
|
1904
|
+
mapFnSrcs: [],
|
|
1905
|
+
buildFns: [],
|
|
1906
|
+
buildSrcs: [],
|
|
1428
1907
|
runtimeParsers: [],
|
|
1429
1908
|
needsCollator: false,
|
|
1430
1909
|
namedParsers: /* @__PURE__ */ new Map(),
|
|
1431
|
-
|
|
1910
|
+
triviaCaptureNames: /* @__PURE__ */ new Map(),
|
|
1911
|
+
namedFnDecls: [],
|
|
1912
|
+
capturing: hasNodeDef(parser2)
|
|
1432
1913
|
};
|
|
1433
1914
|
const r = emit(parser2, ctx, "_pos");
|
|
1434
1915
|
const collatorDecl = ctx.needsCollator ? `const _collator = new Intl.Collator(undefined, { sensitivity: 'accent' })
|
|
@@ -1437,13 +1918,13 @@ function compile(parser2) {
|
|
|
1437
1918
|
...ctx.regexDecls,
|
|
1438
1919
|
"",
|
|
1439
1920
|
...ctx.namedFnDecls,
|
|
1440
|
-
`${collatorDecl}function _parse(input, _pos, _rp, _mf, _ctx) {`,
|
|
1921
|
+
`${collatorDecl}function _parse(input, _pos, _rp, _mf, _build, _ctx) {`,
|
|
1441
1922
|
` let pos = _pos`,
|
|
1442
1923
|
...r.stmts,
|
|
1443
1924
|
` return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`,
|
|
1444
1925
|
`}`
|
|
1445
1926
|
].join("\n");
|
|
1446
|
-
const fn = new Function("input", "_pos", "_rp", "_mf", "_ctx", [
|
|
1927
|
+
const fn = new Function("input", "_pos", "_rp", "_mf", "_build", "_ctx", [
|
|
1447
1928
|
...ctx.regexDecls,
|
|
1448
1929
|
collatorDecl,
|
|
1449
1930
|
...ctx.namedFnDecls,
|
|
@@ -1452,17 +1933,27 @@ function compile(parser2) {
|
|
|
1452
1933
|
`return { ok: true, value: ${r.valueVar}, span: { start: _pos, end: ${r.endVar} } }`
|
|
1453
1934
|
].join("\n"));
|
|
1454
1935
|
const defaultCtx = { trackLines: false };
|
|
1455
|
-
const
|
|
1456
|
-
const
|
|
1936
|
+
const derivedSrcs = ctx.mapFnSrcs.length === ctx.mapFns.length && ctx.mapFnSrcs.every((s) => s !== null) ? ctx.mapFnSrcs : void 0;
|
|
1937
|
+
const effectiveSources = mapFnSources ?? derivedSrcs;
|
|
1938
|
+
const buildCovered = ctx.buildFns.length === 0 || ctx.buildSrcs.every((s) => s !== null);
|
|
1939
|
+
const buildSources = ctx.buildFns.length === 0 ? void 0 : ctx.buildSrcs;
|
|
1940
|
+
const mfCovered = ctx.mapFns.length === 0 || effectiveSources !== void 0 && effectiveSources.length === ctx.mapFns.length;
|
|
1941
|
+
const canInline = ctx.runtimeParsers.length === 0 && mfCovered && buildCovered;
|
|
1942
|
+
const inlineExpression = canInline ? buildInlineExpression(ctx, r, collatorDecl, effectiveSources, buildSources) : null;
|
|
1457
1943
|
return {
|
|
1458
1944
|
source,
|
|
1459
1945
|
inlineExpression,
|
|
1460
1946
|
parse(input, pos = 0) {
|
|
1461
|
-
return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, defaultCtx);
|
|
1947
|
+
return fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, defaultCtx);
|
|
1948
|
+
},
|
|
1949
|
+
parseWithErrors(input, pos = 0) {
|
|
1950
|
+
const errors = [];
|
|
1951
|
+
const result = fn(input, pos, ctx.runtimeParsers, ctx.mapFns, ctx.buildFns, { ...defaultCtx, _errors: errors });
|
|
1952
|
+
return { ...result, errors };
|
|
1462
1953
|
}
|
|
1463
1954
|
};
|
|
1464
1955
|
}
|
|
1465
|
-
function buildInlineExpression(ctx, r, collatorDecl) {
|
|
1956
|
+
function buildInlineExpression(ctx, r, collatorDecl, mapFnSources, buildSources) {
|
|
1466
1957
|
const bodyLines = [
|
|
1467
1958
|
` let pos = _pos`,
|
|
1468
1959
|
...r.stmts.map((s) => ` ${s}`),
|
|
@@ -1473,14 +1964,16 @@ function buildInlineExpression(ctx, r, collatorDecl) {
|
|
|
1473
1964
|
...bodyLines,
|
|
1474
1965
|
`}`
|
|
1475
1966
|
].join("\n");
|
|
1476
|
-
const
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1967
|
+
const mfDecl = mapFnSources?.length ? ` const _mf = [${mapFnSources.join(", ")}]` : "";
|
|
1968
|
+
const buildDecl = buildSources?.length ? ` const _build = [${buildSources.join(", ")}]` : "";
|
|
1969
|
+
const needsWrapper = ctx.regexDecls.length > 0 || !!collatorDecl || ctx.namedFnDecls.length > 0 || !!mfDecl || !!buildDecl;
|
|
1970
|
+
if (!needsWrapper) return innerFn;
|
|
1480
1971
|
return [
|
|
1481
1972
|
`/* @__PURE__ */ (() => {`,
|
|
1482
1973
|
...ctx.regexDecls.map((d) => ` ${d}`),
|
|
1483
1974
|
collatorDecl ? ` ${collatorDecl.trim()}` : "",
|
|
1975
|
+
mfDecl,
|
|
1976
|
+
buildDecl,
|
|
1484
1977
|
...ctx.namedFnDecls.flatMap((f) => f.split("\n").map((l) => ` ${l}`)),
|
|
1485
1978
|
` return ${innerFn}`,
|
|
1486
1979
|
`})()`
|
|
@@ -1499,7 +1992,7 @@ function guard(predicate) {
|
|
|
1499
1992
|
_meta: meta,
|
|
1500
1993
|
_def: { tag: "guard", predicate },
|
|
1501
1994
|
parse(_input, pos, ctx) {
|
|
1502
|
-
if (predicate(ctx.
|
|
1995
|
+
if (predicate(ctx.state))
|
|
1503
1996
|
return { ok: true, value: null, span: { start: pos, end: pos } };
|
|
1504
1997
|
return { ok: false, expected: ["guard"], span: { start: pos, end: pos } };
|
|
1505
1998
|
}
|
|
@@ -1518,7 +2011,7 @@ function withCtx(extra, parser2) {
|
|
|
1518
2011
|
_meta: meta,
|
|
1519
2012
|
_def: { tag: "withCtx", extra, parser: parser2 },
|
|
1520
2013
|
parse(input, pos, ctx) {
|
|
1521
|
-
return parser2.parse(input, pos, { ...ctx,
|
|
2014
|
+
return parser2.parse(input, pos, { ...ctx, state: extra });
|
|
1522
2015
|
}
|
|
1523
2016
|
};
|
|
1524
2017
|
}
|
|
@@ -1547,6 +2040,7 @@ function recover(parser2, sentinel) {
|
|
|
1547
2040
|
span: { start: pos, end: scanPos },
|
|
1548
2041
|
expected: result.expected
|
|
1549
2042
|
};
|
|
2043
|
+
ctx._errors?.push(error);
|
|
1550
2044
|
return { ok: true, value: error, span: { start: pos, end: scanPos } };
|
|
1551
2045
|
}
|
|
1552
2046
|
};
|
|
@@ -1555,6 +2049,21 @@ function isParseError(value) {
|
|
|
1555
2049
|
return typeof value === "object" && value !== null && value._tag === "parseError";
|
|
1556
2050
|
}
|
|
1557
2051
|
|
|
2052
|
+
// src/combinators/completions.ts
|
|
2053
|
+
function completionsAt(parser2, input, offset) {
|
|
2054
|
+
const probe = { offset, best: null };
|
|
2055
|
+
const ctx = { trackLines: false, _probe: probe };
|
|
2056
|
+
const result = parser2.parse(input.slice(0, offset), 0, ctx);
|
|
2057
|
+
if (result.ok) return [];
|
|
2058
|
+
const best = deeperFail(probe.best, result);
|
|
2059
|
+
return best?.expected ?? [];
|
|
2060
|
+
}
|
|
2061
|
+
function deeperFail(a, b) {
|
|
2062
|
+
if (!a) return b;
|
|
2063
|
+
if (!b) return a;
|
|
2064
|
+
return a.span.start >= b.span.start ? a : b;
|
|
2065
|
+
}
|
|
2066
|
+
|
|
1558
2067
|
// src/combinators/scanTo.ts
|
|
1559
2068
|
function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
1560
2069
|
const meta = {
|
|
@@ -1568,14 +2077,23 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
|
1568
2077
|
_def: { tag: "scanTo", sentinel, skip: skip2, orEOF },
|
|
1569
2078
|
parse(input, pos, ctx) {
|
|
1570
2079
|
let cur = pos;
|
|
2080
|
+
const probeCtx = { trackLines: ctx.trackLines, state: ctx.state };
|
|
2081
|
+
const emit2 = (end) => {
|
|
2082
|
+
if (end > pos && (ctx._cstLeaves || ctx._cstRawChildren)) {
|
|
2083
|
+
const leaf = { _tag: "leaf", value: input.slice(pos, end), span: { start: pos, end } };
|
|
2084
|
+
if (ctx._cstLeaves) ctx._cstLeaves.push(leaf);
|
|
2085
|
+
if (ctx._cstRawChildren) ctx._cstRawChildren.push(leaf);
|
|
2086
|
+
}
|
|
2087
|
+
};
|
|
1571
2088
|
while (cur < input.length) {
|
|
1572
|
-
const s = sentinel.parse(input, cur,
|
|
2089
|
+
const s = sentinel.parse(input, cur, probeCtx);
|
|
1573
2090
|
if (s.ok) {
|
|
2091
|
+
emit2(cur);
|
|
1574
2092
|
return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
|
|
1575
2093
|
}
|
|
1576
2094
|
let advanced = false;
|
|
1577
2095
|
for (const skipper of skip2) {
|
|
1578
|
-
const r = skipper.parse(input, cur,
|
|
2096
|
+
const r = skipper.parse(input, cur, probeCtx);
|
|
1579
2097
|
if (r.ok && r.span.end > cur) {
|
|
1580
2098
|
cur = r.span.end;
|
|
1581
2099
|
advanced = true;
|
|
@@ -1585,6 +2103,7 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
|
1585
2103
|
if (!advanced) cur++;
|
|
1586
2104
|
}
|
|
1587
2105
|
if (orEOF) {
|
|
2106
|
+
emit2(cur);
|
|
1588
2107
|
return { ok: true, value: input.slice(pos, cur), span: { start: pos, end: cur } };
|
|
1589
2108
|
}
|
|
1590
2109
|
const sentDef = sentinel._def;
|
|
@@ -1595,19 +2114,23 @@ function scanTo(sentinel, { skip: skip2 = [], orEOF = false } = {}) {
|
|
|
1595
2114
|
}
|
|
1596
2115
|
function balanced(open, close, options = {}) {
|
|
1597
2116
|
const inner = scanTo(literal(close), options);
|
|
1598
|
-
|
|
2117
|
+
const combi = transform(
|
|
1599
2118
|
sequence(literal(open), inner, literal(close)),
|
|
1600
2119
|
([o, content, c]) => o + content + c
|
|
1601
2120
|
);
|
|
2121
|
+
if (combi._def.tag === "transform") {
|
|
2122
|
+
combi._def.fnSrc = "([o, content, c]) => o + content + c";
|
|
2123
|
+
}
|
|
2124
|
+
return combi;
|
|
1602
2125
|
}
|
|
1603
2126
|
|
|
1604
2127
|
// src/cst/incremental.ts
|
|
1605
2128
|
function isNode(x) {
|
|
1606
2129
|
return typeof x === "object" && x !== null && x._tag === "node";
|
|
1607
2130
|
}
|
|
1608
|
-
function findContaining(
|
|
1609
|
-
for (let i = 0; i <
|
|
1610
|
-
const child =
|
|
2131
|
+
function findContaining(node2, pos, path = []) {
|
|
2132
|
+
for (let i = 0; i < node2.children.length; i++) {
|
|
2133
|
+
const child = node2.children[i];
|
|
1611
2134
|
if (!isNode(child)) continue;
|
|
1612
2135
|
if (child.span.start <= pos && pos < child.span.end) {
|
|
1613
2136
|
return findContaining(child, pos, [...path, i]) ?? { node: child, path: [...path, i] };
|
|
@@ -1636,22 +2159,29 @@ function replaceAtPath(grammar, root, path, newNode) {
|
|
|
1636
2159
|
var ParseDocImpl = class _ParseDocImpl {
|
|
1637
2160
|
_parser;
|
|
1638
2161
|
_ruleName;
|
|
2162
|
+
_trivia;
|
|
2163
|
+
_captureTrivia;
|
|
1639
2164
|
tree;
|
|
1640
2165
|
errors;
|
|
1641
2166
|
input;
|
|
1642
|
-
|
|
2167
|
+
/** Offset reached by the top-level rule. < input.length means input was left unconsumed. */
|
|
2168
|
+
consumedEnd;
|
|
2169
|
+
constructor(parser2, ruleName, tree, errors, input, trivia2, captureTrivia, consumedEnd = 0) {
|
|
1643
2170
|
this._parser = parser2;
|
|
1644
2171
|
this._ruleName = ruleName;
|
|
2172
|
+
this._trivia = trivia2;
|
|
2173
|
+
this._captureTrivia = captureTrivia;
|
|
1645
2174
|
this.tree = tree;
|
|
1646
2175
|
this.errors = errors;
|
|
1647
2176
|
this.input = input;
|
|
2177
|
+
this.consumedEnd = consumedEnd;
|
|
1648
2178
|
}
|
|
1649
2179
|
edit(from, to, replacement) {
|
|
1650
2180
|
const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
|
|
1651
|
-
if (!this.tree) return makeParseDoc(this._parser, this._ruleName, newInput);
|
|
2181
|
+
if (!this.tree) return makeParseDoc(this._parser, this._ruleName, newInput, this._trivia, this._captureTrivia);
|
|
1652
2182
|
const delta = replacement.length - (to - from);
|
|
1653
2183
|
const found = findContaining(this.tree, from);
|
|
1654
|
-
if (!found) return makeParseDoc(this._parser, this._ruleName, newInput);
|
|
2184
|
+
if (!found) return makeParseDoc(this._parser, this._ruleName, newInput, this._trivia, this._captureTrivia);
|
|
1655
2185
|
const ancestors = ancestorsAt(this.tree, found.path);
|
|
1656
2186
|
const candidates = [found];
|
|
1657
2187
|
const pathCopy = [...found.path];
|
|
@@ -1660,32 +2190,66 @@ var ParseDocImpl = class _ParseDocImpl {
|
|
|
1660
2190
|
candidates.push({ node: ancestors[i + 1], path: [...pathCopy] });
|
|
1661
2191
|
}
|
|
1662
2192
|
for (const candidate of candidates) {
|
|
1663
|
-
const { node, path } = candidate;
|
|
1664
|
-
const expectedEnd =
|
|
1665
|
-
const parser2 = this._parser.rule(
|
|
1666
|
-
const ctx = { trackLines: false,
|
|
1667
|
-
const r = parser2.parse(newInput,
|
|
2193
|
+
const { node: node2, path } = candidate;
|
|
2194
|
+
const expectedEnd = node2.span.end + delta;
|
|
2195
|
+
const parser2 = this._parser.rule(node2.type);
|
|
2196
|
+
const ctx = { trackLines: false, state: node2.state, ...this._trivia !== void 0 ? { trivia: this._trivia } : {}, ...this._captureTrivia ? { captureTrivia: true } : {} };
|
|
2197
|
+
const r = parser2.parse(newInput, node2.span.start, ctx);
|
|
1668
2198
|
if (!r.ok) continue;
|
|
1669
2199
|
if (r.span.end === expectedEnd) {
|
|
1670
2200
|
const newTree = replaceAtPath(this._parser, this.tree, path, r.value);
|
|
1671
|
-
return new _ParseDocImpl(this._parser, this._ruleName, newTree, [], newInput);
|
|
2201
|
+
return new _ParseDocImpl(this._parser, this._ruleName, newTree, [], newInput, this._trivia, this._captureTrivia);
|
|
1672
2202
|
}
|
|
1673
2203
|
}
|
|
1674
|
-
return makeParseDoc(this._parser, this._ruleName, newInput);
|
|
2204
|
+
return makeParseDoc(this._parser, this._ruleName, newInput, this._trivia, this._captureTrivia);
|
|
1675
2205
|
}
|
|
1676
2206
|
};
|
|
1677
|
-
function makeParseDoc(parser2, ruleName, input) {
|
|
1678
|
-
const ctx = {
|
|
2207
|
+
function makeParseDoc(parser2, ruleName, input, trivia2, captureTrivia, triviaLog) {
|
|
2208
|
+
const ctx = {
|
|
2209
|
+
trackLines: false,
|
|
2210
|
+
...trivia2 !== void 0 ? { trivia: trivia2 } : {},
|
|
2211
|
+
...captureTrivia ? { captureTrivia: true } : {},
|
|
2212
|
+
...triviaLog !== void 0 ? { _triviaLog: triviaLog } : {}
|
|
2213
|
+
};
|
|
1679
2214
|
const r = parser2.rule(ruleName).parse(input, 0, ctx);
|
|
1680
2215
|
if (r.ok) {
|
|
1681
|
-
return new ParseDocImpl(parser2, ruleName, r.value, [], input);
|
|
2216
|
+
return new ParseDocImpl(parser2, ruleName, r.value, [], input, trivia2, captureTrivia, r.span.end);
|
|
1682
2217
|
}
|
|
1683
|
-
return new ParseDocImpl(parser2, ruleName, null, [{ ok: false, expected: r.expected, span: r.span }], input);
|
|
2218
|
+
return new ParseDocImpl(parser2, ruleName, null, [{ ok: false, expected: r.expected, span: r.span }], input, trivia2, captureTrivia, r.span.start);
|
|
1684
2219
|
}
|
|
1685
2220
|
|
|
1686
2221
|
// src/cst/grammar.ts
|
|
1687
2222
|
var Parser = class {
|
|
1688
2223
|
_built = false;
|
|
2224
|
+
/** Set in a subclass to enable automatic trivia skipping between sequence terms. */
|
|
2225
|
+
_trivia;
|
|
2226
|
+
/**
|
|
2227
|
+
* Set in a subclass to make the rule name optional in `parse()`.
|
|
2228
|
+
* When defined, `parse(input)` is equivalent to `parse(defaultRule, input)`.
|
|
2229
|
+
*/
|
|
2230
|
+
_defaultRule;
|
|
2231
|
+
/**
|
|
2232
|
+
* Set `true` in a subclass to record trivia (whitespace/comments) consumed
|
|
2233
|
+
* between terms into each node's rawChildren as separate CSTTrivia tokens.
|
|
2234
|
+
* Requires `_trivia` to be set. Default: trivia is skipped without recording.
|
|
2235
|
+
*/
|
|
2236
|
+
_captureTrivia;
|
|
2237
|
+
/**
|
|
2238
|
+
* When set, makeParseDoc injects this array as ctx._triviaLog so each
|
|
2239
|
+
* scanTrivia.commit() pushes [runStart, runEnd] pairs into it.
|
|
2240
|
+
* Reset to [] before each parse() call in any subclass that uses it.
|
|
2241
|
+
*/
|
|
2242
|
+
_triviaLog;
|
|
2243
|
+
/**
|
|
2244
|
+
* Optional map of public entry-point aliases → actual rule names. Lets callers
|
|
2245
|
+
* `parse('value', src)` when the grammar's rule is `ValueList`, removing
|
|
2246
|
+
* per-adapter name-translation tables.
|
|
2247
|
+
*/
|
|
2248
|
+
_aliases;
|
|
2249
|
+
/** Resolve an alias to its target rule name (identity if not aliased). */
|
|
2250
|
+
_resolveRule(name) {
|
|
2251
|
+
return this._aliases?.[name] ?? name;
|
|
2252
|
+
}
|
|
1689
2253
|
_build() {
|
|
1690
2254
|
if (this._built) return;
|
|
1691
2255
|
this._built = true;
|
|
@@ -1725,8 +2289,8 @@ var Parser = class {
|
|
|
1725
2289
|
* grammar is whitespace-sensitive (e.g. CSS descendant vs adjacent combinators).
|
|
1726
2290
|
* The default implementation ignores `rawChildren`.
|
|
1727
2291
|
*/
|
|
1728
|
-
buildNode(type, span, children,
|
|
1729
|
-
return { _tag: "node", type, span, children,
|
|
2292
|
+
buildNode(type, span, children, state, _rawChildren) {
|
|
2293
|
+
return { _tag: "node", type, span, children, state };
|
|
1730
2294
|
}
|
|
1731
2295
|
/** Wrap an inner combinator so it produces a CSTNode on each match. */
|
|
1732
2296
|
_makeNodeParser(type, inner) {
|
|
@@ -1741,7 +2305,7 @@ var Parser = class {
|
|
|
1741
2305
|
_meta: meta,
|
|
1742
2306
|
_def: { tag: "unknown" },
|
|
1743
2307
|
parse(input, pos, ctx) {
|
|
1744
|
-
const
|
|
2308
|
+
const state = ctx.state !== void 0 ? Object.assign({}, ctx.state) : void 0;
|
|
1745
2309
|
const children = [];
|
|
1746
2310
|
const rawChildren = [];
|
|
1747
2311
|
const innerCtx = {
|
|
@@ -1752,31 +2316,34 @@ var Parser = class {
|
|
|
1752
2316
|
};
|
|
1753
2317
|
const r = inner.parse(input, pos, innerCtx);
|
|
1754
2318
|
if (!r.ok) return r;
|
|
1755
|
-
const
|
|
1756
|
-
|
|
1757
|
-
if (ctx.
|
|
1758
|
-
|
|
2319
|
+
const node2 = self.buildNode(type, r.span, children, state, rawChildren);
|
|
2320
|
+
const isNodeLike = typeof node2 === "object" && node2 !== null && node2._tag === "node";
|
|
2321
|
+
if (ctx._cstChildren) ctx._cstChildren.push(node2);
|
|
2322
|
+
if (ctx._cstRawChildren) ctx._cstRawChildren.push(
|
|
2323
|
+
isNodeLike ? node2 : { _tag: "leaf", value: typeof node2 === "string" ? node2 : "", span: r.span }
|
|
2324
|
+
);
|
|
2325
|
+
return { ok: true, value: node2, span: r.span };
|
|
1759
2326
|
}
|
|
1760
2327
|
};
|
|
1761
2328
|
}
|
|
1762
2329
|
/** Reconstruct a node with a new children array (used by IncrementalParser). */
|
|
1763
|
-
rebuild(
|
|
1764
|
-
return this.buildNode(
|
|
2330
|
+
rebuild(node2, newChildren) {
|
|
2331
|
+
return this.buildNode(node2.type, node2.span, newChildren, node2.state, []);
|
|
1765
2332
|
}
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
return makeParseDoc(this, ruleName,
|
|
2333
|
+
parse(ruleNameOrInput, input) {
|
|
2334
|
+
let ruleName;
|
|
2335
|
+
let src;
|
|
2336
|
+
if (input === void 0) {
|
|
2337
|
+
if (this._defaultRule === void 0) {
|
|
2338
|
+
throw new Error("parse(input) requires a _defaultRule to be set on the grammar");
|
|
2339
|
+
}
|
|
2340
|
+
ruleName = this._defaultRule;
|
|
2341
|
+
src = ruleNameOrInput;
|
|
2342
|
+
} else {
|
|
2343
|
+
ruleName = ruleNameOrInput;
|
|
2344
|
+
src = input;
|
|
2345
|
+
}
|
|
2346
|
+
return makeParseDoc(this, this._resolveRule(ruleName), src, this._trivia, this._captureTrivia, this._triviaLog);
|
|
1780
2347
|
}
|
|
1781
2348
|
/**
|
|
1782
2349
|
* Get the compiled Combinator for a named rule.
|
|
@@ -1784,7 +2351,8 @@ var Parser = class {
|
|
|
1784
2351
|
*/
|
|
1785
2352
|
rule(name) {
|
|
1786
2353
|
this._build();
|
|
1787
|
-
const
|
|
2354
|
+
const resolved = this._resolveRule(name);
|
|
2355
|
+
const p = this[resolved];
|
|
1788
2356
|
if (!p) throw new Error(`No rule '${String(name)}' on this parser`);
|
|
1789
2357
|
return p;
|
|
1790
2358
|
}
|
|
@@ -1792,4 +2360,168 @@ var Parser = class {
|
|
|
1792
2360
|
function isCombinator(val) {
|
|
1793
2361
|
return val !== null && typeof val === "object" && "_tag" in val;
|
|
1794
2362
|
}
|
|
2363
|
+
|
|
2364
|
+
// src/functional/doc.ts
|
|
2365
|
+
function isNode2(x) {
|
|
2366
|
+
return typeof x === "object" && x !== null && x._tag === "node";
|
|
2367
|
+
}
|
|
2368
|
+
function findContaining2(node2, pos, path = []) {
|
|
2369
|
+
for (let i = 0; i < node2.children.length; i++) {
|
|
2370
|
+
const child = node2.children[i];
|
|
2371
|
+
if (!isNode2(child)) continue;
|
|
2372
|
+
if (child.span.start <= pos && pos < child.span.end) {
|
|
2373
|
+
const inner = findContaining2(child, pos, [...path, i]);
|
|
2374
|
+
return inner ?? { node: child, path: [...path, i] };
|
|
2375
|
+
}
|
|
2376
|
+
}
|
|
2377
|
+
return null;
|
|
2378
|
+
}
|
|
2379
|
+
function ancestorsAt2(root, path) {
|
|
2380
|
+
const ancestors = [root];
|
|
2381
|
+
let cur = root;
|
|
2382
|
+
for (const idx of path.slice(0, -1)) {
|
|
2383
|
+
const child = cur.children[idx];
|
|
2384
|
+
if (!child || !isNode2(child)) break;
|
|
2385
|
+
ancestors.push(child);
|
|
2386
|
+
cur = child;
|
|
2387
|
+
}
|
|
2388
|
+
return ancestors;
|
|
2389
|
+
}
|
|
2390
|
+
function replaceAtPath2(rebuild, root, path, newNode) {
|
|
2391
|
+
if (path.length === 0) return newNode;
|
|
2392
|
+
const [idx, ...rest] = path;
|
|
2393
|
+
const newChildren = [...root.children];
|
|
2394
|
+
newChildren[idx] = rest.length === 0 ? newNode : replaceAtPath2(rebuild, root.children[idx], rest, newNode);
|
|
2395
|
+
return rebuild(root, newChildren);
|
|
2396
|
+
}
|
|
2397
|
+
function defaultRebuild(node2, children) {
|
|
2398
|
+
return { ...node2, children };
|
|
2399
|
+
}
|
|
2400
|
+
var FunctionalDocImpl = class _FunctionalDocImpl {
|
|
2401
|
+
_registry;
|
|
2402
|
+
_rootRule;
|
|
2403
|
+
_opts;
|
|
2404
|
+
tree;
|
|
2405
|
+
errors;
|
|
2406
|
+
input;
|
|
2407
|
+
constructor(registry, rootRule, opts, tree, errors, input) {
|
|
2408
|
+
this._registry = registry;
|
|
2409
|
+
this._rootRule = rootRule;
|
|
2410
|
+
this._opts = opts;
|
|
2411
|
+
this.tree = tree;
|
|
2412
|
+
this.errors = errors;
|
|
2413
|
+
this.input = input;
|
|
2414
|
+
}
|
|
2415
|
+
edit(from, to, replacement) {
|
|
2416
|
+
const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
|
|
2417
|
+
const reparse = () => makeFunctionalDoc(this._registry, this._rootRule, newInput, this._opts);
|
|
2418
|
+
if (!this.tree) return reparse();
|
|
2419
|
+
const delta = replacement.length - (to - from);
|
|
2420
|
+
const found = findContaining2(this.tree, from);
|
|
2421
|
+
if (!found) return reparse();
|
|
2422
|
+
const ancestors = ancestorsAt2(this.tree, found.path);
|
|
2423
|
+
const candidates = [found];
|
|
2424
|
+
const pathCopy = [...found.path];
|
|
2425
|
+
for (let i = ancestors.length - 2; i >= 0; i--) {
|
|
2426
|
+
pathCopy.pop();
|
|
2427
|
+
candidates.push({ node: ancestors[i + 1], path: [...pathCopy] });
|
|
2428
|
+
}
|
|
2429
|
+
const rebuild = this._opts.rebuild ?? defaultRebuild;
|
|
2430
|
+
for (const { node: node2, path } of candidates) {
|
|
2431
|
+
const ruleFn = this._registry[node2.type];
|
|
2432
|
+
if (!ruleFn) continue;
|
|
2433
|
+
const ctx = { trackLines: false, state: node2.state };
|
|
2434
|
+
const r = ruleFn(newInput, node2.span.start, ctx);
|
|
2435
|
+
if (!r.ok) continue;
|
|
2436
|
+
if (r.span.end === node2.span.end + delta) {
|
|
2437
|
+
const newTree = replaceAtPath2(rebuild, this.tree, path, r.value);
|
|
2438
|
+
return new _FunctionalDocImpl(this._registry, this._rootRule, this._opts, newTree, [], newInput);
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
return reparse();
|
|
2442
|
+
}
|
|
2443
|
+
};
|
|
2444
|
+
function makeFunctionalDoc(registry, rootRule, input, opts = {}) {
|
|
2445
|
+
const ruleFn = registry[rootRule];
|
|
2446
|
+
if (!ruleFn) throw new Error(`No rule '${rootRule}' in registry`);
|
|
2447
|
+
const ctx = { trackLines: false, state: opts.state };
|
|
2448
|
+
const r = ruleFn(input, 0, ctx);
|
|
2449
|
+
if (r.ok) {
|
|
2450
|
+
return new FunctionalDocImpl(registry, rootRule, opts, r.value, [], input);
|
|
2451
|
+
}
|
|
2452
|
+
return new FunctionalDocImpl(registry, rootRule, opts, null, [{ ok: false, expected: r.expected, span: r.span }], input);
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2455
|
+
// src/cst/trivia-index.ts
|
|
2456
|
+
function spanOf(c) {
|
|
2457
|
+
const s = c.span;
|
|
2458
|
+
return s && typeof s.start === "number" ? s : void 0;
|
|
2459
|
+
}
|
|
2460
|
+
function merge(map, key, run) {
|
|
2461
|
+
if (run.length === 0) return;
|
|
2462
|
+
const existing = map.get(key);
|
|
2463
|
+
if (existing) existing.push(...run);
|
|
2464
|
+
else map.set(key, [...run]);
|
|
2465
|
+
}
|
|
2466
|
+
function buildTriviaIndex(root, opts) {
|
|
2467
|
+
const before = /* @__PURE__ */ new Map();
|
|
2468
|
+
const after = /* @__PURE__ */ new Map();
|
|
2469
|
+
const visit = (node2) => {
|
|
2470
|
+
const raw = node2.children;
|
|
2471
|
+
if (!raw) return;
|
|
2472
|
+
let prevEnd;
|
|
2473
|
+
let i = 0;
|
|
2474
|
+
while (i < raw.length) {
|
|
2475
|
+
const c = raw[i];
|
|
2476
|
+
if (c._tag === "trivia") {
|
|
2477
|
+
const run = [];
|
|
2478
|
+
while (i < raw.length && raw[i]._tag === "trivia") {
|
|
2479
|
+
const t = raw[i];
|
|
2480
|
+
const sp2 = spanOf(t);
|
|
2481
|
+
if (sp2 && sp2.end > sp2.start) run.push({ value: t.value ?? "", span: sp2 });
|
|
2482
|
+
i++;
|
|
2483
|
+
}
|
|
2484
|
+
const next = i < raw.length ? spanOf(raw[i]) : void 0;
|
|
2485
|
+
if (next) merge(before, next.start, run);
|
|
2486
|
+
if (prevEnd !== void 0) merge(after, prevEnd, run);
|
|
2487
|
+
continue;
|
|
2488
|
+
}
|
|
2489
|
+
const sp = spanOf(c);
|
|
2490
|
+
if (sp) prevEnd = sp.end;
|
|
2491
|
+
if (c._tag === "node") visit(c);
|
|
2492
|
+
i++;
|
|
2493
|
+
}
|
|
2494
|
+
};
|
|
2495
|
+
if (root && typeof root === "object") visit(root);
|
|
2496
|
+
if (opts) {
|
|
2497
|
+
const rootSpan = spanOf(root);
|
|
2498
|
+
const re = new RegExp(opts.trivia.source, opts.trivia.flags.replace(/[gy]/g, "") + "y");
|
|
2499
|
+
const scanFrom = (from) => {
|
|
2500
|
+
const run = [];
|
|
2501
|
+
let pos = from;
|
|
2502
|
+
re.lastIndex = pos;
|
|
2503
|
+
let m = re.exec(opts.input);
|
|
2504
|
+
while (m && m.index === pos && m[0].length > 0) {
|
|
2505
|
+
run.push({ value: m[0], span: { start: pos, end: pos + m[0].length } });
|
|
2506
|
+
pos += m[0].length;
|
|
2507
|
+
re.lastIndex = pos;
|
|
2508
|
+
m = re.exec(opts.input);
|
|
2509
|
+
}
|
|
2510
|
+
return run;
|
|
2511
|
+
};
|
|
2512
|
+
if (rootSpan && rootSpan.start > 0) {
|
|
2513
|
+
const lead = scanFrom(0);
|
|
2514
|
+
if (lead.length) merge(before, rootSpan.start, lead);
|
|
2515
|
+
}
|
|
2516
|
+
const end = rootSpan ? rootSpan.end : 0;
|
|
2517
|
+
if (end < opts.input.length) {
|
|
2518
|
+
const trail = scanFrom(end);
|
|
2519
|
+
if (trail.length) {
|
|
2520
|
+
merge(after, end, trail);
|
|
2521
|
+
merge(before, end + trail.reduce((n, t) => n + (t.span.end - t.span.start), 0), trail);
|
|
2522
|
+
}
|
|
2523
|
+
}
|
|
2524
|
+
}
|
|
2525
|
+
return { before, after };
|
|
2526
|
+
}
|
|
1795
2527
|
//# sourceMappingURL=index.cjs.map
|