parseman 0.25.0 → 0.26.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.
Files changed (54) hide show
  1. package/dist/combinators/completions.d.ts +14 -4
  2. package/dist/combinators/completions.d.ts.map +1 -1
  3. package/dist/combinators/expect.d.ts +2 -0
  4. package/dist/combinators/expect.d.ts.map +1 -1
  5. package/dist/combinators/grammar.d.ts +5 -5
  6. package/dist/combinators/not.d.ts.map +1 -1
  7. package/dist/combinators/parser.d.ts +3 -2
  8. package/dist/combinators/parser.d.ts.map +1 -1
  9. package/dist/combinators/repeat.d.ts.map +1 -1
  10. package/dist/combinators/sequence.d.ts.map +1 -1
  11. package/dist/combinators/trivia-skip.d.ts +1 -0
  12. package/dist/combinators/trivia-skip.d.ts.map +1 -1
  13. package/dist/compiler/codegen.d.ts +9 -4
  14. package/dist/compiler/codegen.d.ts.map +1 -1
  15. package/dist/compiler/scannable-run.d.ts +2 -2
  16. package/dist/compiler/scannable-run.d.ts.map +1 -1
  17. package/dist/cst/capture-buffer.d.ts +1 -0
  18. package/dist/cst/capture-buffer.d.ts.map +1 -1
  19. package/dist/cst/types.d.ts +7 -7
  20. package/dist/cst/types.d.ts.map +1 -1
  21. package/dist/functional/doc.d.ts +37 -2
  22. package/dist/functional/doc.d.ts.map +1 -1
  23. package/dist/functional/run.d.ts +12 -1
  24. package/dist/functional/run.d.ts.map +1 -1
  25. package/dist/index.cjs +505 -195
  26. package/dist/index.cjs.map +4 -4
  27. package/dist/index.d.ts +1 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +505 -195
  30. package/dist/index.js.map +4 -4
  31. package/dist/language-service/index.cjs +776 -0
  32. package/dist/language-service/index.cjs.map +7 -0
  33. package/dist/language-service/index.d.ts +94 -0
  34. package/dist/language-service/index.d.ts.map +1 -0
  35. package/dist/language-service/index.js +753 -0
  36. package/dist/language-service/index.js.map +7 -0
  37. package/dist/plugin/evaluator.d.ts.map +1 -1
  38. package/dist/plugin/index.cjs +568 -129
  39. package/dist/plugin/index.cjs.map +4 -4
  40. package/dist/plugin/index.d.ts +11 -1
  41. package/dist/plugin/index.d.ts.map +1 -1
  42. package/dist/plugin/index.js +568 -129
  43. package/dist/plugin/index.js.map +4 -4
  44. package/dist/recovery/scan.d.ts +54 -0
  45. package/dist/recovery/scan.d.ts.map +1 -0
  46. package/dist/spec/index.cjs.map +1 -1
  47. package/dist/spec/index.js.map +1 -1
  48. package/dist/types.d.ts +60 -6
  49. package/dist/types.d.ts.map +1 -1
  50. package/package.json +6 -1
  51. package/dist/combinators/recover-list.d.ts +0 -41
  52. package/dist/combinators/recover-list.d.ts.map +0 -1
  53. package/dist/combinators/recover.d.ts +0 -20
  54. package/dist/combinators/recover.d.ts.map +0 -1
@@ -0,0 +1,776 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/language-service/index.ts
21
+ var language_service_exports = {};
22
+ __export(language_service_exports, {
23
+ languageService: () => languageService
24
+ });
25
+ module.exports = __toCommonJS(language_service_exports);
26
+
27
+ // src/cst/capture-buffer.ts
28
+ function cstCaptureActive(ctx) {
29
+ return ctx._cstBuf !== void 0 || ctx._cstLeaves !== void 0;
30
+ }
31
+ function pushCstChild(ctx, built, rawEntry) {
32
+ const b = ctx._cstBuf;
33
+ if (b) {
34
+ if (b.ch) b.ch.push(built);
35
+ else if (b.single !== void 0) {
36
+ b.ch = [b.single, built];
37
+ b.single = void 0;
38
+ } else b.single = built;
39
+ if (b.raw) b.raw.push(rawEntry);
40
+ else if (b.rawSingle !== void 0) {
41
+ b.raw = [b.rawSingle, rawEntry];
42
+ b.rawSingle = void 0;
43
+ } else b.rawSingle = rawEntry;
44
+ return;
45
+ }
46
+ if (ctx._cstChildren) ctx._cstChildren.push(built);
47
+ else if (ctx._cstLeaves) ctx._cstLeaves.push(built);
48
+ if (ctx._cstRawChildren) ctx._cstRawChildren.push(rawEntry);
49
+ }
50
+
51
+ // src/recovery/scan.ts
52
+ function probeAt(sentinel, input, pos, ctx) {
53
+ const s = {
54
+ _probe: ctx._probe,
55
+ _errors: ctx._errors,
56
+ _cstLeaves: ctx._cstLeaves,
57
+ _cstChildren: ctx._cstChildren,
58
+ _cstRawChildren: ctx._cstRawChildren,
59
+ _cstTriviaLog: ctx._cstTriviaLog,
60
+ _triviaLog: ctx._triviaLog,
61
+ _fields: ctx._fields,
62
+ _cstBuf: ctx._cstBuf,
63
+ _tolerant: ctx._tolerant,
64
+ _sync: ctx._sync
65
+ };
66
+ ctx._probe = void 0;
67
+ ctx._errors = void 0;
68
+ ctx._cstLeaves = void 0;
69
+ ctx._cstChildren = void 0;
70
+ ctx._cstRawChildren = void 0;
71
+ ctx._cstTriviaLog = void 0;
72
+ ctx._triviaLog = void 0;
73
+ ctx._fields = void 0;
74
+ ctx._cstBuf = void 0;
75
+ ctx._tolerant = void 0;
76
+ ctx._sync = void 0;
77
+ try {
78
+ return sentinel.parse(input, pos, ctx).ok;
79
+ } finally {
80
+ ctx._probe = s._probe;
81
+ ctx._errors = s._errors;
82
+ ctx._cstLeaves = s._cstLeaves;
83
+ ctx._cstChildren = s._cstChildren;
84
+ ctx._cstRawChildren = s._cstRawChildren;
85
+ ctx._cstTriviaLog = s._cstTriviaLog;
86
+ ctx._triviaLog = s._triviaLog;
87
+ ctx._fields = s._fields;
88
+ ctx._cstBuf = s._cstBuf;
89
+ ctx._tolerant = s._tolerant;
90
+ ctx._sync = s._sync;
91
+ }
92
+ }
93
+ function orSentinel(a, b) {
94
+ if (b === void 0) return a;
95
+ return {
96
+ _tag: "orSentinel",
97
+ _meta: { firstSet: { kind: "any" }, canMatchNewline: false, isTrivia: false },
98
+ _def: { tag: "unknown" },
99
+ parse(input, pos, ctx) {
100
+ const ra = a.parse(input, pos, ctx);
101
+ if (ra.ok) return ra;
102
+ return b.parse(input, pos, ctx);
103
+ }
104
+ };
105
+ }
106
+ function firstSetSentinel(fs) {
107
+ if (fs.kind !== "ranges" || fs.ranges.length === 0) return null;
108
+ const ranges = fs.ranges;
109
+ return {
110
+ _tag: "firstSetSentinel",
111
+ _meta: { firstSet: fs, canMatchNewline: false, isTrivia: false },
112
+ _def: { tag: "unknown" },
113
+ parse(input, pos) {
114
+ const code = pos < input.length ? input.codePointAt(pos) : void 0;
115
+ if (code !== void 0) {
116
+ for (const r of ranges) {
117
+ if (code >= r.lo && code <= r.hi) return { ok: true, value: null, span: { start: pos, end: pos } };
118
+ }
119
+ }
120
+ return { ok: false, expected: [], span: { start: pos, end: pos } };
121
+ }
122
+ };
123
+ }
124
+ function recoverScan(input, from, ctx, sync, expected) {
125
+ let scanPos = from;
126
+ while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++;
127
+ const error = { _tag: "parseError", span: { start: from, end: scanPos }, expected };
128
+ ctx._errors?.push(error);
129
+ return { error, end: scanPos };
130
+ }
131
+ function matchesAt(sentinel, input, pos, ctx) {
132
+ return probeAt(sentinel, input, pos, ctx);
133
+ }
134
+ function captureError(ctx, error) {
135
+ if (cstCaptureActive(ctx)) pushCstChild(ctx, error, error);
136
+ }
137
+ var REC = { scan: recoverScan, at: matchesAt, or: orSentinel, sentinel: firstSetSentinel, capture: captureError };
138
+
139
+ // src/functional/run.ts
140
+ var invoke = (r, input, pos, ctx) => typeof r === "function" ? r(input, pos, ctx) : r.parse(input, pos, ctx);
141
+ function run(entry, input, options = {}) {
142
+ if (typeof entry !== "function" && typeof entry?.parse !== "function") {
143
+ throw new TypeError(
144
+ `run(): start production is ${entry === null ? "null" : typeof entry}, not a rule \u2014 the requested grammar rule does not exist (check the rule name).`
145
+ );
146
+ }
147
+ const triviaLog = [];
148
+ const errors = [];
149
+ const grammarTrivia = typeof entry !== "function" ? entry._meta.grammarTrivia : void 0;
150
+ const ctx = {
151
+ trackLines: false,
152
+ _triviaLog: triviaLog,
153
+ _errors: errors,
154
+ build: options.build,
155
+ state: options.state,
156
+ ...grammarTrivia !== void 0 ? { trivia: grammarTrivia, ...grammarTrivia._meta.triviaKindLabels ? { triviaKindLabels: grammarTrivia._meta.triviaKindLabels } : {} } : {},
157
+ ...options.triviaCaptureMask !== void 0 ? { _triviaCaptureMask: options.triviaCaptureMask } : {},
158
+ ...options.tolerant ? { _tolerant: true, _rec: REC } : {}
159
+ };
160
+ const r = invoke(entry, input, 0, ctx);
161
+ let unconsumedFrom = null;
162
+ if (r.ok) {
163
+ let pos = r.span?.end ?? 0;
164
+ if (options.trivia && pos < input.length) {
165
+ const t = invoke(options.trivia, input, pos, { trackLines: false });
166
+ if (t.ok && t.span.end > pos) pos = t.span.end;
167
+ }
168
+ unconsumedFrom = pos < input.length ? pos : null;
169
+ }
170
+ return {
171
+ ok: r.ok,
172
+ value: r.ok ? r.value : void 0,
173
+ span: r.span ?? { start: 0, end: 0 },
174
+ expected: r.ok ? [] : r.expected ?? [],
175
+ errors,
176
+ triviaLog,
177
+ unconsumedFrom
178
+ };
179
+ }
180
+
181
+ // src/combinators/completions.ts
182
+ function completionsAt(target, input, offset, options = {}) {
183
+ const probe = { offset, best: null };
184
+ const ctx = { trackLines: false, _probe: probe, ...options.tolerant ? { _tolerant: true, _rec: REC } : {} };
185
+ const sliced = input.slice(0, offset);
186
+ const result = "parseWithContext" in target ? target.parseWithContext(sliced, ctx, 0) : target.parse(sliced, 0, ctx);
187
+ if (result.ok) return options.tolerant ? probe.best?.expected ?? [] : [];
188
+ const best = deeperFail(probe.best, result);
189
+ return best?.expected ?? [];
190
+ }
191
+ function deeperFail(a, b) {
192
+ if (!a) return b;
193
+ if (!b) return a;
194
+ return a.span.start >= b.span.start ? a : b;
195
+ }
196
+
197
+ // src/compiler/scannable-run.ts
198
+ var META = new Set("()[]{}*+?|^$.".split(""));
199
+
200
+ // src/compiler/trivia-fast-path.ts
201
+ var CAP_RECORD = [
202
+ ` if (_cap && _e > _pos) {`,
203
+ ` if (_ctx._triviaLog !== undefined) _ctx._triviaLog.push(_pos, _e)`,
204
+ ` if (_ctx._cstTriviaLog !== undefined) _ctx._cstTriviaLog.push(_pos, _e, _ctx._cstRawChildren ? _ctx._cstRawChildren.length : 0)`,
205
+ ` }`
206
+ ].join("\n");
207
+
208
+ // src/compiler/linker.ts
209
+ function normalizeCstCollapse(collapse) {
210
+ if (collapse === true) return () => true;
211
+ if (Array.isArray(collapse)) {
212
+ const types = new Set(collapse);
213
+ return (type) => types.has(type);
214
+ }
215
+ return typeof collapse === "function" ? collapse : void 0;
216
+ }
217
+ function buildCstNode(type, children, _fields, span, _rawChildren, _triviaLog, state) {
218
+ return { _tag: "node", type, span: { start: span.start, end: span.end }, state: state ?? null, children: [...children] };
219
+ }
220
+ function cstBuildHost(typeOrOptions, children, _fields, span, rawChildren, triviaLog, state) {
221
+ if (typeof typeOrOptions === "string") {
222
+ return buildCstNode(typeOrOptions, children ?? [], _fields, span ?? { start: 0, end: 0 }, rawChildren, triviaLog, state);
223
+ }
224
+ const collapse = normalizeCstCollapse(typeOrOptions?.collapse);
225
+ const host = (type, children2, fields, span2, rawChildren2, triviaLog2, state2) => buildCstNode(type, children2, fields, span2, rawChildren2, triviaLog2, state2);
226
+ if (collapse) host._parsemanCstCollapse = collapse;
227
+ return host;
228
+ }
229
+
230
+ // src/cst/walk.ts
231
+ function walk(root, visitor, ctx = void 0) {
232
+ const go = (node2, parent) => {
233
+ const descend = visitor.enter ? visitor.enter(node2, parent, ctx) : void 0;
234
+ const children = node2.children;
235
+ if (descend !== false && Array.isArray(children)) {
236
+ for (const child of children) go(child, node2);
237
+ }
238
+ visitor.leave?.(node2, parent, ctx);
239
+ };
240
+ go(root, null);
241
+ }
242
+
243
+ // src/cst/relative-spans.ts
244
+ function relativizeCST(node2, parentBase = 0) {
245
+ const span = { start: node2.span.start - parentBase, end: node2.span.end - parentBase };
246
+ if (node2.children) {
247
+ const base = node2.span.start;
248
+ return { ...node2, span, children: node2.children.map((c) => relativizeCST(c, base)) };
249
+ }
250
+ return { ...node2, span };
251
+ }
252
+ function absolutizeCST(node2, parentBase = 0) {
253
+ const start = parentBase + node2.span.start;
254
+ const span = { start, end: parentBase + node2.span.end };
255
+ if (node2.children) {
256
+ return { ...node2, span, children: node2.children.map((c) => absolutizeCST(c, start)) };
257
+ }
258
+ return { ...node2, span };
259
+ }
260
+ function absoluteSpanCST(root, path) {
261
+ let base = 0;
262
+ let node2 = root;
263
+ for (const idx of path) {
264
+ base += node2.span.start;
265
+ const child = node2.children?.[idx];
266
+ if (!child) throw new RangeError(`absoluteSpanCST: no child ${idx} on path ${path.join(".")}`);
267
+ node2 = child;
268
+ }
269
+ return { start: base + node2.span.start, end: base + node2.span.end };
270
+ }
271
+
272
+ // src/functional/doc.ts
273
+ function mkCtx(state, build, tolerant) {
274
+ return tolerant ? { trackLines: false, state, build, _tolerant: true, _rec: REC, _errors: [] } : { trackLines: false, state, build };
275
+ }
276
+ function asRuleFn(entry) {
277
+ return typeof entry === "function" ? entry : (input, pos, ctx) => entry.parse(input, pos, ctx);
278
+ }
279
+ function defOf(entry) {
280
+ return typeof entry === "object" && entry !== null && "_def" in entry ? entry._def : void 0;
281
+ }
282
+ function producesRepetition(def, depth = 0) {
283
+ if (!def || depth > 24) return false;
284
+ const d = def;
285
+ switch (d.tag) {
286
+ case "sepBy":
287
+ case "many":
288
+ case "oneOrMore":
289
+ return true;
290
+ // Rule entries and `ref`s wrap their body in a `lazy` thunk — resolve it. The
291
+ // `depth` cap bounds any self-referential cycle (a repetition, if present, is
292
+ // found shallowly before recursion goes deep).
293
+ case "lazy": {
294
+ const thunk = d.thunk;
295
+ let inner;
296
+ try {
297
+ inner = typeof thunk === "function" ? thunk() : void 0;
298
+ } catch {
299
+ return false;
300
+ }
301
+ return producesRepetition(inner?._def, depth + 1);
302
+ }
303
+ // Transparent wrappers — look through to the inner parser.
304
+ case "node":
305
+ case "transform":
306
+ case "token":
307
+ case "label":
308
+ case "field":
309
+ case "expect":
310
+ case "withCtx":
311
+ case "grammar":
312
+ case "optional":
313
+ return producesRepetition(defOf(d.parser), depth + 1);
314
+ case "skip":
315
+ return producesRepetition(defOf(d.main), depth + 1);
316
+ // A bracketed/anchored list is a sequence whose element run is a repetition.
317
+ case "sequence":
318
+ return d.parsers.some((p) => producesRepetition(defOf(p), depth + 1));
319
+ default:
320
+ return false;
321
+ }
322
+ }
323
+ var REENTRY_MAX_SPAN_FRACTION = 0.5;
324
+ function isNode(x) {
325
+ return typeof x === "object" && x !== null && x._tag === "node";
326
+ }
327
+ function findContaining(node2, nodeAbsStart, pos, path = []) {
328
+ for (let i = 0; i < node2.children.length; i++) {
329
+ const child = node2.children[i];
330
+ if (!isNode(child)) continue;
331
+ const childAbsStart = nodeAbsStart + child.span.start;
332
+ const childAbsEnd = nodeAbsStart + child.span.end;
333
+ if (childAbsStart <= pos && pos < childAbsEnd) {
334
+ const inner = findContaining(child, childAbsStart, pos, [...path, i]);
335
+ return inner ?? { node: child, path: [...path, i] };
336
+ }
337
+ }
338
+ return null;
339
+ }
340
+ function ancestorsAt(root, path) {
341
+ const ancestors = [root];
342
+ let cur = root;
343
+ for (const idx of path.slice(0, -1)) {
344
+ const child = cur.children[idx];
345
+ if (!child || !isNode(child)) break;
346
+ ancestors.push(child);
347
+ cur = child;
348
+ }
349
+ return ancestors;
350
+ }
351
+ function replaceAtPath(rebuild, root, path, newNode) {
352
+ if (path.length === 0) return newNode;
353
+ const [idx, ...rest] = path;
354
+ const newChildren = [...root.children];
355
+ newChildren[idx] = rest.length === 0 ? newNode : replaceAtPath(rebuild, root.children[idx], rest, newNode);
356
+ return rebuild(root, newChildren);
357
+ }
358
+ function defaultRebuild(node2, children) {
359
+ return { ...node2, children };
360
+ }
361
+ function shiftRelStart(child, delta) {
362
+ const c = child;
363
+ return { ...c, span: { start: c.span.start + delta, end: c.span.end + delta } };
364
+ }
365
+ function graftRelative(root, path, newNode, delta) {
366
+ if (path.length === 0) return newNode;
367
+ const [idx, ...rest] = path;
368
+ const oldChildren = root.children;
369
+ const newChildren = oldChildren.map((child, i) => {
370
+ if (i < idx) return child;
371
+ if (i === idx) {
372
+ return rest.length === 0 ? newNode : graftRelative(child, rest, newNode, delta);
373
+ }
374
+ return shiftRelStart(child, delta);
375
+ });
376
+ return {
377
+ ...root,
378
+ span: { start: root.span.start, end: root.span.end + delta },
379
+ children: newChildren
380
+ };
381
+ }
382
+ function parseMiddle(input, reStart, reEnd, cStart, ruleFn, separator, build, tolerant) {
383
+ const ctx = mkCtx(null, build, tolerant);
384
+ const out = [];
385
+ let pos = reStart;
386
+ let guard = 0;
387
+ while (pos < reEnd) {
388
+ if (++guard > reEnd - reStart + 2) return null;
389
+ if (separator && out.length > 0 && out[out.length - 1]._tag === "node") {
390
+ if (pos + separator.length > reEnd || !input.startsWith(separator, pos)) return null;
391
+ out.push({ _tag: "leaf", value: separator, span: { start: pos - cStart, end: pos + separator.length - cStart } });
392
+ pos += separator.length;
393
+ continue;
394
+ }
395
+ let r;
396
+ try {
397
+ r = ruleFn(input, pos, ctx);
398
+ } catch {
399
+ return null;
400
+ }
401
+ if (!r.ok || r.span.end <= pos || r.span.end > reEnd) return null;
402
+ out.push(relativizeCST(r.value, cStart));
403
+ pos = r.span.end;
404
+ }
405
+ return pos === reEnd ? out : null;
406
+ }
407
+ function tryListSplice(root, path, C, cStart, newInput, from, to, delta, registry, build, tolerant) {
408
+ const kids = C.children;
409
+ if (kids.length === 0) return null;
410
+ let h = 0;
411
+ while (h < kids.length && cStart + kids[h].span.end <= from) h++;
412
+ while (h > 0 && kids[h - 1]._tag === "node") h--;
413
+ let t = kids.length;
414
+ while (t > h && cStart + kids[t - 1].span.start >= to) t--;
415
+ while (t < kids.length && kids[t]._tag !== "node") t++;
416
+ if (t >= kids.length) return null;
417
+ for (let i = t; i < kids.length; i++) {
418
+ if (kids[i]._tag === "node" && kids[i].state != null) return null;
419
+ }
420
+ let elemType;
421
+ let elemCount = 0;
422
+ for (const k of kids) {
423
+ if (k._tag !== "node") continue;
424
+ elemCount++;
425
+ if (elemType === void 0) elemType = k.type;
426
+ else if (k.type !== elemType) return null;
427
+ }
428
+ if (elemType === void 0 || elemCount < 2) return null;
429
+ const ruleFn = registry[elemType];
430
+ if (!ruleFn) return null;
431
+ let separator = null;
432
+ for (let i = 1; i < kids.length - 1; i++) {
433
+ if (kids[i - 1]._tag === "node" && kids[i + 1]._tag === "node") {
434
+ if (kids[i]._tag !== "leaf") return null;
435
+ const sep = kids[i].value ?? null;
436
+ if (separator === null) separator = sep;
437
+ else if (sep !== separator) return null;
438
+ }
439
+ }
440
+ if (separator === null) return null;
441
+ const first = kids[0];
442
+ const last = kids[kids.length - 1];
443
+ if (first._tag === "leaf" && first.value !== separator && from < cStart + first.span.end) return null;
444
+ if (last._tag === "leaf" && last.value !== separator && to > cStart + last.span.start) return null;
445
+ const reStart = h > 0 ? cStart + kids[h - 1].span.end : cStart;
446
+ const reEnd = cStart + kids[t].span.start + delta;
447
+ if (reEnd < reStart) return null;
448
+ const middle = parseMiddle(newInput, reStart, reEnd, cStart, ruleFn, separator, build, tolerant);
449
+ if (!middle) return null;
450
+ if (middle.length > 0) {
451
+ const last2 = middle[middle.length - 1];
452
+ if (!(last2._tag === "leaf" && last2.value === separator)) return null;
453
+ }
454
+ if (reEnd < newInput.length) {
455
+ for (const sentinel of [" ", "\uFFFF"]) {
456
+ if (newInput[reEnd] === sentinel) continue;
457
+ const probed = newInput.slice(0, reEnd) + sentinel.repeat(newInput.length - reEnd);
458
+ const probe = parseMiddle(probed, reStart, reEnd, cStart, ruleFn, separator, build, tolerant);
459
+ if (!probe || probe.length !== middle.length) return null;
460
+ for (let i = 0; i < middle.length; i++) if (!structurallyEqual(probe[i], middle[i])) return null;
461
+ }
462
+ }
463
+ const head = kids.slice(0, h);
464
+ const tail = kids.slice(t).map((k) => shiftRelStart(k, delta));
465
+ const newC = {
466
+ ...C,
467
+ span: { start: C.span.start, end: C.span.end + delta },
468
+ children: [...head, ...middle, ...tail]
469
+ };
470
+ return graftRelative(root, path, newC, delta);
471
+ }
472
+ function structurallyEqual(a, b) {
473
+ if (a === b) return true;
474
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
475
+ const at = a._tag;
476
+ const bt = b._tag;
477
+ if (at !== bt) return false;
478
+ const as = a.span;
479
+ const bs = b.span;
480
+ if (as || bs) {
481
+ if (!as || !bs || as.start !== bs.start || as.end !== bs.end) return false;
482
+ }
483
+ if (at === "leaf" || at === "trivia") {
484
+ return a.value === b.value;
485
+ }
486
+ if (at === "parseError") {
487
+ const ae = a.expected ?? [];
488
+ const be = b.expected ?? [];
489
+ return ae.length === be.length && ae.every((x, i) => x === be[i]);
490
+ }
491
+ if (a.type !== b.type) return false;
492
+ const ac = a.children;
493
+ const bc = b.children;
494
+ if (ac || bc) {
495
+ if (!ac || !bc || ac.length !== bc.length) return false;
496
+ for (let i = 0; i < ac.length; i++) if (!structurallyEqual(ac[i], bc[i])) return false;
497
+ }
498
+ return true;
499
+ }
500
+ function boundaryIsSafe(ruleFn, newInput, start, boundary, state, build, produced, tolerant) {
501
+ if (!produced.ok) return false;
502
+ if (boundary >= newInput.length) return true;
503
+ for (const sentinel of [" ", "\uFFFF"]) {
504
+ if (newInput[boundary] === sentinel) continue;
505
+ const probed = newInput.slice(0, boundary) + sentinel.repeat(newInput.length - boundary);
506
+ const ctx = mkCtx(state, build, tolerant);
507
+ let r;
508
+ try {
509
+ r = ruleFn(probed, start, ctx);
510
+ } catch {
511
+ return false;
512
+ }
513
+ if (!r.ok || r.span.end !== produced.span.end) return false;
514
+ if (!structurallyEqual(r.value, produced.value)) return false;
515
+ }
516
+ return true;
517
+ }
518
+ var ParseDocImpl = class _ParseDocImpl {
519
+ _registry;
520
+ /** Registry entries normalized to callable parse functions (memoized once). */
521
+ _fns;
522
+ /** Rule names the grammar proves are genuine repetitions — the ONLY splice-safe types. */
523
+ _reps;
524
+ _rootRule;
525
+ _opts;
526
+ /**
527
+ * The tree, held in whichever coordinate system it arrived in. A fresh parse
528
+ * arrives ABSOLUTE (`_abs` set); an incremental graft arrives PARENT-RELATIVE
529
+ * (`_rel` set). The public `tree` is always the RELATIVE form (so untouched
530
+ * subtrees stay shared by identity across edits and reads are O(1)); it's
531
+ * materialized from `_abs` once, on demand, and memoized. `undefined` = not yet
532
+ * computed; `null` = failed parse. A fresh parse that's never edited nor
533
+ * tree-read pays no conversion — important for the full-reparse fallback path.
534
+ */
535
+ _abs;
536
+ _rel;
537
+ errors;
538
+ unconsumedFrom;
539
+ input;
540
+ constructor(registry, rootRule, opts, trees, errors, unconsumedFrom, input) {
541
+ this._registry = registry;
542
+ this._fns = {};
543
+ this._reps = /* @__PURE__ */ new Set();
544
+ for (const [name, entry] of Object.entries(registry)) {
545
+ this._fns[name] = asRuleFn(entry);
546
+ if (producesRepetition(defOf(entry))) this._reps.add(name);
547
+ }
548
+ this._rootRule = rootRule;
549
+ this._opts = opts;
550
+ this._abs = trees.abs;
551
+ this._rel = trees.rel;
552
+ this.errors = errors;
553
+ this.unconsumedFrom = unconsumedFrom;
554
+ this.input = input;
555
+ }
556
+ /**
557
+ * The parse tree with PARENT-RELATIVE spans — a node's `span` is relative to
558
+ * its parent's start (root base 0). This is the shareable representation:
559
+ * untouched subtrees keep the same identity across `.edit()`s, and reading the
560
+ * tree after a length-changing edit is O(1) (no offset rewrite). For absolute
561
+ * positions use the O(depth) cursor `spanAt(path)`, or `absolutizeCST(tree)` to
562
+ * materialize the whole absolute tree. (A fresh non-incremental `node().parse()`
563
+ * result is unchanged — still absolute.)
564
+ */
565
+ get tree() {
566
+ if (this._rel === void 0) {
567
+ this._rel = this._abs ? relativizeCST(this._abs, 0) : null;
568
+ }
569
+ return this._rel;
570
+ }
571
+ spanAt(path) {
572
+ const rel = this.tree;
573
+ if (!rel) throw new Error("spanAt on a failed parse (tree is null)");
574
+ return absoluteSpanCST(rel, path);
575
+ }
576
+ /**
577
+ * Wrap a reused (grafted/spliced) RELATIVE tree, recomputing `errors` and
578
+ * `unconsumedFrom` from it so both stay consistent with the tree across the
579
+ * edit (the flat errors are recovered errors embedded in the reused subtrees;
580
+ * unconsumedFrom re-derives trailing junk over the new input).
581
+ */
582
+ wrapReuse(newTree, newInput) {
583
+ const trivia2 = triviaOf(this._registry[this._rootRule], this._opts);
584
+ const end = newTree.span.end;
585
+ const errors = collectEmbeddedErrors(newTree, 0, []);
586
+ const unconsumedFrom = unconsumedAfter(end, newInput, trivia2);
587
+ return new _ParseDocImpl(this._registry, this._rootRule, this._opts, { rel: newTree }, errors, unconsumedFrom, newInput);
588
+ }
589
+ edit(from, to, replacement) {
590
+ const newInput = this.input.slice(0, from) + replacement + this.input.slice(to);
591
+ const reparse = () => parseDoc(this._registry, this._rootRule, newInput, this._opts);
592
+ const root = this.tree;
593
+ if (!root) return reparse();
594
+ const delta = replacement.length - (to - from);
595
+ const found = findContaining(root, 0, from) ?? { node: root, path: [] };
596
+ const ancestors = ancestorsAt(root, found.path);
597
+ const candidates = [found];
598
+ const pathCopy = [...found.path];
599
+ for (let i = ancestors.length - 2; i >= 0; i--) {
600
+ pathCopy.pop();
601
+ candidates.push({ node: ancestors[i + 1], path: [...pathCopy] });
602
+ }
603
+ const rebuild = this._opts.rebuild ?? defaultRebuild;
604
+ for (const { node: node2, path } of candidates) {
605
+ const { start: absStart, end: absEnd } = absoluteSpanCST(root, path);
606
+ if (absEnd - absStart > this.input.length * REENTRY_MAX_SPAN_FRACTION) break;
607
+ const ruleFn = this._fns[node2.type];
608
+ if (!ruleFn) continue;
609
+ if (!(absStart <= from && to <= absEnd)) continue;
610
+ const ctx = mkCtx(node2.state, this._opts.build, !!this._opts.tolerant);
611
+ const r = ruleFn(newInput, absStart, ctx);
612
+ if (!r.ok) continue;
613
+ if (r.span.end !== absEnd + delta) continue;
614
+ if (!boundaryIsSafe(ruleFn, newInput, absStart, absEnd + delta, node2.state, this._opts.build, r, !!this._opts.tolerant)) {
615
+ continue;
616
+ }
617
+ const parentBase = absStart - node2.span.start;
618
+ const newRel = relativizeCST(r.value, parentBase);
619
+ if (delta === 0) {
620
+ const newTree2 = replaceAtPath(rebuild, root, path, newRel);
621
+ return this.wrapReuse(newTree2, newInput);
622
+ }
623
+ if (this._opts.rebuild) return reparse();
624
+ const newTree = graftRelative(root, path, newRel, delta);
625
+ return this.wrapReuse(newTree, newInput);
626
+ }
627
+ if (this._opts.structuralReuse && delta !== 0 && !this._opts.rebuild) {
628
+ const spliceCandidates = [...candidates, { node: root, path: [] }];
629
+ for (const { node: node2, path } of spliceCandidates) {
630
+ if (!this._reps.has(node2.type)) continue;
631
+ const { start: cStart, end: cEnd } = absoluteSpanCST(root, path);
632
+ if (!(cStart <= from && to <= cEnd)) continue;
633
+ const spliced = tryListSplice(root, path, node2, cStart, newInput, from, to, delta, this._fns, this._opts.build, !!this._opts.tolerant);
634
+ if (spliced) return this.wrapReuse(spliced, newInput);
635
+ }
636
+ }
637
+ return reparse();
638
+ }
639
+ };
640
+ function triviaOf(entry, opts) {
641
+ if (opts.trivia) return opts.trivia;
642
+ if (entry === void 0 || typeof entry === "function") return void 0;
643
+ const meta = entry._meta;
644
+ return meta ? meta.grammarTrivia : void 0;
645
+ }
646
+ function unconsumedAfter(end, input, trivia2) {
647
+ let pos = end;
648
+ if (trivia2 && pos < input.length) {
649
+ const t = trivia2.parse(input, pos, { trackLines: false });
650
+ if (t.ok && t.span.end > pos) pos = t.span.end;
651
+ }
652
+ return pos < input.length ? pos : null;
653
+ }
654
+ function collectEmbeddedErrors(node2, base, out) {
655
+ const c = node2;
656
+ if (!c || typeof c !== "object" || !c.span) return out;
657
+ const start = base + c.span.start;
658
+ if (c._tag === "parseError") {
659
+ out.push({ _tag: "parseError", span: { start, end: base + c.span.end }, expected: c.expected ?? [] });
660
+ return out;
661
+ }
662
+ if (Array.isArray(c.children)) for (const k of c.children) collectEmbeddedErrors(k, start, out);
663
+ return out;
664
+ }
665
+ function parseDoc(registry, rootRule, input, opts = {}) {
666
+ const entry = registry[rootRule];
667
+ if (!entry) throw new Error(`No rule '${rootRule}' in registry`);
668
+ const ctx = mkCtx(opts.state, opts.build, !!opts.tolerant);
669
+ const r = asRuleFn(entry)(input, 0, ctx);
670
+ if (r.ok) {
671
+ const errors = ctx._errors ? [...ctx._errors] : [];
672
+ const unconsumedFrom = unconsumedAfter(r.span.end, input, triviaOf(entry, opts));
673
+ return new ParseDocImpl(registry, rootRule, opts, { abs: r.value }, errors, unconsumedFrom, input);
674
+ }
675
+ const fail = { _tag: "parseError", span: r.span, expected: r.expected };
676
+ return new ParseDocImpl(registry, rootRule, opts, { abs: null }, [fail], null, input);
677
+ }
678
+
679
+ // src/language-service/index.ts
680
+ var errorMessage = (expected) => expected.length ? `Unexpected input; expected ${expected.join(" or ")}` : "Unexpected input";
681
+ function mergeDiagnostics(tree, flat, unconsumedFrom, inputLength, config) {
682
+ const out = [];
683
+ const seen = /* @__PURE__ */ new Set();
684
+ const add = (d) => {
685
+ const key = `${d.severity}|${d.span.start}|${d.span.end}|${d.message}`;
686
+ if (seen.has(key)) return;
687
+ seen.add(key);
688
+ out.push(d);
689
+ };
690
+ if (tree) for (const d of diagnoseTree(tree, config)) add(d);
691
+ for (const e of flat) add({ severity: "error", message: errorMessage(e.expected), span: e.span });
692
+ if (unconsumedFrom !== null) {
693
+ add({ severity: "error", message: "Unexpected input", span: { start: unconsumedFrom, end: inputLength } });
694
+ }
695
+ return out;
696
+ }
697
+ function diagnoseTree(root, config) {
698
+ const out = [];
699
+ const rules2 = config.diagnostics;
700
+ walk(root, {
701
+ enter(node2) {
702
+ if (node2._tag === "parseError") {
703
+ out.push({ severity: "error", message: errorMessage(node2.expected ?? []), span: node2.span });
704
+ return;
705
+ }
706
+ const h = rules2 && node2.type !== void 0 ? rules2[node2.type] : void 0;
707
+ if (h) {
708
+ const d = h(node2);
709
+ if (d) out.push(...Array.isArray(d) ? d : [d]);
710
+ }
711
+ }
712
+ });
713
+ return out;
714
+ }
715
+ function languageService(grammar, config = {}) {
716
+ const build = cstBuildHost();
717
+ const asReg = typeof grammar === "object" && grammar !== null && "rules" in grammar;
718
+ const registry = asReg ? grammar.rules : void 0;
719
+ const root = asReg ? grammar.root : void 0;
720
+ const entry = asReg ? registry[root] : grammar;
721
+ const trivia2 = typeof entry !== "function" ? entry._meta.grammarTrivia : void 0;
722
+ const parseTolerant = (src) => run(entry, src, { tolerant: true, build, ...trivia2 !== void 0 ? { trivia: trivia2 } : {} });
723
+ const complete = (input, offset, treeForCursor) => {
724
+ const expected = completionsAt(entry, input, offset, { tolerant: true });
725
+ const rule = ruleAtCursor(treeForCursor, offset);
726
+ const handler = rule !== null ? config.complete?.[rule] : void 0;
727
+ if (handler) return handler({ rule, expected, offset });
728
+ return expected.map((label2) => ({ label: label2 }));
729
+ };
730
+ return {
731
+ parse(src, opts) {
732
+ return run(entry, src, { tolerant: opts?.tolerant ?? true, build });
733
+ },
734
+ diagnostics(src) {
735
+ const r = parseTolerant(src);
736
+ const tree = r.value && typeof r.value === "object" ? r.value : null;
737
+ return mergeDiagnostics(tree, r.errors, r.unconsumedFrom, src.length, config);
738
+ },
739
+ completionsAt(src, offset) {
740
+ return complete(src, offset, parseTolerant(src.slice(0, offset)).value);
741
+ },
742
+ openDocument(src) {
743
+ if (!registry || root === void 0) {
744
+ throw new Error("openDocument requires languageService({ rules, root }, config) \u2014 a rules() registry, not a single entry");
745
+ }
746
+ const host = cstBuildHost();
747
+ const make = (doc) => {
748
+ const abs = doc.tree ? absolutizeCST(doc.tree) : null;
749
+ return {
750
+ get tree() {
751
+ return abs;
752
+ },
753
+ edit: (from, to, replacement) => make(doc.edit(from, to, replacement)),
754
+ // Same unified merge as the stateless path: embedded parseError nodes +
755
+ // lint, the flat recovery errors the doc collected, and a trailing-junk
756
+ // sentinel — so a live editor document surfaces missing-closers and junk.
757
+ diagnostics: () => mergeDiagnostics(abs, doc.errors, doc.unconsumedFrom, doc.input.length, config),
758
+ completionsAt: (offset) => complete(doc.input, offset, abs)
759
+ };
760
+ };
761
+ return make(parseDoc(registry, root, src, { tolerant: true, structuralReuse: true, build: host }));
762
+ }
763
+ };
764
+ }
765
+ function ruleAtCursor(root, offset) {
766
+ if (!root || typeof root !== "object") return null;
767
+ let best = null;
768
+ walk(root, {
769
+ enter(node2) {
770
+ const s = node2.span;
771
+ if (s && s.start <= offset && offset <= s.end && node2.type !== void 0) best = node2.type;
772
+ }
773
+ });
774
+ return best;
775
+ }
776
+ //# sourceMappingURL=index.cjs.map