parseman 0.58.3 → 0.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analysis/diagnostics.js +141 -93
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +143 -94
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/dispatch.d.ts.map +1 -1
- package/dist/combinators/field-trivia.d.ts.map +1 -1
- package/dist/combinators/keywords.d.ts.map +1 -1
- package/dist/combinators/literal.d.ts.map +1 -1
- package/dist/combinators/node.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/scanTo.d.ts.map +1 -1
- package/dist/combinators/token.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/compiler/token-capability.js.map +1 -1
- package/dist/cst/capture-buffer.d.ts.map +1 -1
- package/dist/cst/plain.js +69 -0
- package/dist/cst/plain.js.map +6 -0
- package/dist/index.js +144 -98
- package/dist/index.js.map +3 -3
- package/dist/language-service/index.js +19 -2
- package/dist/language-service/index.js.map +2 -2
- package/dist/line-index.d.ts +13 -0
- package/dist/line-index.d.ts.map +1 -1
- package/dist/plugin/entry-supercompiler.d.ts.map +1 -1
- package/dist/plugin/index.js +146 -100
- package/dist/plugin/index.js.map +3 -3
- package/dist/recovery/scan.d.ts.map +1 -1
- package/dist/run/index.js +4 -1
- package/dist/run/index.js.map +2 -2
- package/dist/spec/index.js +18 -1
- package/dist/spec/index.js.map +2 -2
- package/dist/table/assemble.d.ts.map +1 -1
- package/dist/table/emit-assembly.d.ts +1 -1
- package/dist/table/emit-assembly.d.ts.map +1 -1
- package/dist/table/emit.d.ts +6 -0
- package/dist/table/emit.d.ts.map +1 -1
- package/dist/table/index.js +124 -89
- package/dist/table/index.js.map +3 -3
- package/dist/table/run-support.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +2 -1
- package/src/combinators/dispatch.ts +2 -1
- package/src/combinators/field-trivia.ts +6 -5
- package/src/combinators/keywords.ts +3 -2
- package/src/combinators/literal.ts +5 -4
- package/src/combinators/node.ts +3 -1
- package/src/combinators/regex.ts +5 -4
- package/src/combinators/scanTo.ts +2 -1
- package/src/combinators/token.ts +3 -6
- package/src/compiler/linker.ts +4 -2
- package/src/cst/capture-buffer.ts +13 -5
- package/src/cst/plain.d.ts +35 -0
- package/src/cst/plain.js +74 -0
- package/src/line-index.ts +37 -0
- package/src/plugin/entry-supercompiler.ts +3 -2
- package/src/recovery/scan.ts +2 -1
- package/src/table/assemble.ts +33 -35
- package/src/table/emit-assembly.ts +27 -24
- package/src/table/emit.ts +9 -1
- package/src/table/run-support.ts +4 -7
- package/src/version.ts +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-support.d.ts","sourceRoot":"","sources":["../../src/table/run-support.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"run-support.d.ts","sourceRoot":"","sources":["../../src/table/run-support.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAI/C,MAAM,MAAM,IAAI,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAA;AACjD,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,2EAA2E;AAC3E,wBAAgB,OAAO,CAAC,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAS3E;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,QAAQ,CAGjF;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAMjF;AAED,yEAAyE;AACzE,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAKvD;AAKD,KAAK,aAAa,GAAG,CACnB,QAAQ,EAAE,SAAS,OAAO,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,EACrF,WAAW,EAAE,SAAS,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,SAAS,KAC5E,OAAO,CAAA;AAEZ;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAC3B,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAClG,OAAO,CAST"}
|
package/dist/version.d.ts
CHANGED
|
@@ -13,5 +13,5 @@
|
|
|
13
13
|
* Kept in sync with `package.json` by `test/unit/version-sync.test.ts`, which fails
|
|
14
14
|
* the build if this constant drifts from the published version.
|
|
15
15
|
*/
|
|
16
|
-
export declare const PARSEMAN_VERSION = "0.
|
|
16
|
+
export declare const PARSEMAN_VERSION = "0.59.0";
|
|
17
17
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "parseman",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.0",
|
|
4
4
|
"packageManager": "pnpm@10.15.1",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": "^20.19.0 || >=22.13.0"
|
|
@@ -96,6 +96,7 @@
|
|
|
96
96
|
"perf:guard:grammars": "node --import tsx/esm bench/grammar-perf-guard.ts",
|
|
97
97
|
"perf:xproc": "node --import tsx/esm bench/xproc-ab.ts",
|
|
98
98
|
"perf:xproc:gate": "node --import tsx/esm bench/xproc-gate.ts",
|
|
99
|
+
"perf:pretenure": "node --import tsx/esm bench/pretenure-check.ts",
|
|
99
100
|
"prepare": "git config core.hooksPath scripts/git-hooks || true; node scripts/build.mjs",
|
|
100
101
|
"prepublishOnly": "node scripts/check-changelog.mjs --publish",
|
|
101
102
|
"check:changelog": "node scripts/check-changelog.mjs",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Combinator, DispatchCase, DispatchMatcherCase, ParseContext, ParseResult, ParserMeta } from '../types.ts'
|
|
2
2
|
import { cstCaptureActive, pushCstLeaf } from '../cst/capture-buffer.ts'
|
|
3
3
|
import { rollbackTrivia, saveTriviaMark } from './trivia-skip.ts'
|
|
4
|
+
import { PlainLeaf } from '../cst/plain.js'
|
|
4
5
|
|
|
5
6
|
export type DispatchWhen<T> = {
|
|
6
7
|
readonly kind: 'when'
|
|
@@ -240,7 +241,7 @@ export function routed<T = string>(fallback?: Combinator<T>): Combinator<T> {
|
|
|
240
241
|
return { ok: false, expected: ['routed()'], span: { start: pos, end: pos } }
|
|
241
242
|
}
|
|
242
243
|
if (cstCaptureActive(ctx)) {
|
|
243
|
-
pushCstLeaf(ctx,
|
|
244
|
+
pushCstLeaf(ctx, new PlainLeaf(item.value, item.span))
|
|
244
245
|
}
|
|
245
246
|
return { ok: true, value: item.value as T, span: item.span }
|
|
246
247
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Combinator } from '../types.ts'
|
|
2
|
+
import { PlainSpan, PlainTriviaField } from '../cst/plain.js'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Where the trivia before a `field(…, { leadingTrivia: true })` began.
|
|
@@ -29,13 +30,13 @@ export const FIELD_TRAILING_TRIVIA = 2
|
|
|
29
30
|
export function triviaFieldCapture(
|
|
30
31
|
name: string, value: unknown, input: string, start: number, end: number, leadFrom: number, trailEnd: number,
|
|
31
32
|
): { name: string; value: unknown; span: { start: number; end: number }; leading: string; trailing: string } {
|
|
32
|
-
return
|
|
33
|
+
return new PlainTriviaField(
|
|
33
34
|
name,
|
|
34
35
|
value,
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
new PlainSpan(start, end),
|
|
37
|
+
leadFrom >= 0 && leadFrom < start ? input.slice(leadFrom, start) : '',
|
|
38
|
+
trailEnd > end ? input.slice(end, trailEnd) : '',
|
|
39
|
+
)
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
/** Is `p` a `field(…, { leadingTrivia: true })` — the term itself, not wrapped? */
|
|
@@ -5,6 +5,7 @@ import { failAt } from './probe.ts'
|
|
|
5
5
|
import { pushCstLeaf, cstCaptureActive } from '../cst/capture-buffer.ts'
|
|
6
6
|
import { directTerminalFailureExpected } from './expected.ts'
|
|
7
7
|
import type { ScalarParser } from './scalar.ts'
|
|
8
|
+
import { PlainLeaf, PlainSpan } from '../cst/plain.js'
|
|
8
9
|
|
|
9
10
|
export type KeywordsOptions = {
|
|
10
11
|
/** Match case-insensitively. */
|
|
@@ -164,8 +165,8 @@ export function keywords(words: readonly string[], opts: KeywordsOptions = {}):
|
|
|
164
165
|
const m = re.exec(input)
|
|
165
166
|
if (m === null) return failAt(ctx, directTerminalFailureExpected(def), pos)
|
|
166
167
|
const value = m[0]!
|
|
167
|
-
const span =
|
|
168
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx,
|
|
168
|
+
const span = new PlainSpan(pos, pos + value.length)
|
|
169
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, new PlainLeaf(value, span))
|
|
169
170
|
return { ok: true, value, span }
|
|
170
171
|
},
|
|
171
172
|
}
|
|
@@ -5,6 +5,7 @@ import { pushCstLeaf, cstCaptureActive } from '../cst/capture-buffer.ts'
|
|
|
5
5
|
import { recordLineRangeFromContext } from '../line-index.ts'
|
|
6
6
|
import { directTerminalFailureExpected } from './expected.ts'
|
|
7
7
|
import type { ScalarParser } from './scalar.ts'
|
|
8
|
+
import { PlainLeaf, PlainSpan } from '../cst/plain.js'
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* ASCII case-fold equality — the interpreter twin of the compiler's `foldEq`
|
|
@@ -96,9 +97,9 @@ export function literal(value: string, opts: LiteralOptions = {}): Combinator<st
|
|
|
96
97
|
const code = value.charCodeAt(0)
|
|
97
98
|
return function parse(input: string, pos: number, ctx: ParseContext): ParseResult<string> {
|
|
98
99
|
if (input.charCodeAt(pos) === code) {
|
|
99
|
-
const span =
|
|
100
|
+
const span = new PlainSpan(pos, pos + 1)
|
|
100
101
|
if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end)
|
|
101
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx,
|
|
102
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, new PlainLeaf(value, span))
|
|
102
103
|
return { ok: true, value, span }
|
|
103
104
|
}
|
|
104
105
|
return failAt(ctx, expected, pos)
|
|
@@ -111,9 +112,9 @@ export function literal(value: string, opts: LiteralOptions = {}): Combinator<st
|
|
|
111
112
|
}
|
|
112
113
|
const matchedValue = caseInsensitive ? input.slice(pos, end) : value
|
|
113
114
|
if (caseInsensitive ? asciiFoldEq(matchedValue, value) : input.startsWith(value, pos)) {
|
|
114
|
-
const span =
|
|
115
|
+
const span = new PlainSpan(pos, end)
|
|
115
116
|
if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, end)
|
|
116
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx,
|
|
117
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, new PlainLeaf(matchedValue, span))
|
|
117
118
|
return { ok: true, value: matchedValue, span }
|
|
118
119
|
}
|
|
119
120
|
return failAt(ctx, expected, pos)
|
package/src/combinators/node.ts
CHANGED
|
@@ -7,6 +7,8 @@ import { matchesEmpty, startsFirstSet } from './first-set.ts'
|
|
|
7
7
|
import { deriveExpected } from './expect.ts'
|
|
8
8
|
import { annotateSpanFromLineContext } from '../line-index.ts'
|
|
9
9
|
import { NODE_TAG, NODE_TYPE } from '../cst/reflection-symbols.ts'
|
|
10
|
+
import { PlainNode, PlainSpan } from '../cst/plain.js'
|
|
11
|
+
import { copySpan } from '../line-index.ts'
|
|
10
12
|
|
|
11
13
|
/**
|
|
12
14
|
* A CST/AST node rule. Runs `combinator` while collecting its terminals into
|
|
@@ -331,7 +333,7 @@ export function node<N>(
|
|
|
331
333
|
// Structural node: a `ctx.build` host if present, else a default CST.
|
|
332
334
|
: ctx.build
|
|
333
335
|
? ctx.build(nodeType, children, fields, span, rawChildren, triviaLog, st, tags)
|
|
334
|
-
:
|
|
336
|
+
: new PlainNode(nodeType, ctx.trackLines ? copySpan(span) : new PlainSpan(r.span.start, r.span.end), st ?? null, [...children])
|
|
335
337
|
const rawEntry = isCstChild(built)
|
|
336
338
|
? built
|
|
337
339
|
// A direct semantic object is opaque to the raw CST, but its source is
|
package/src/combinators/regex.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { shorthandRanges, parseClassRanges } from '../regex/classes.ts'
|
|
|
7
7
|
import { firstSetFromRegex } from '../regex/first-set.ts'
|
|
8
8
|
import { directTerminalFailureExpected } from './expected.ts'
|
|
9
9
|
import type { ScalarParser } from './scalar.ts'
|
|
10
|
+
import { PlainLeaf, PlainSpan } from '../cst/plain.js'
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* A regex terminal's first-set (for choice-dispatch fast paths) is derived by
|
|
@@ -195,9 +196,9 @@ export function regex(pattern: string | RegExp, flags = ''): Combinator<string>
|
|
|
195
196
|
if (scanEnd !== undefined) {
|
|
196
197
|
if (scanEnd === null) return failAt(ctx, directTerminalFailureExpected(def), pos)
|
|
197
198
|
const value = input.slice(pos, scanEnd)
|
|
198
|
-
const span =
|
|
199
|
+
const span = new PlainSpan(pos, scanEnd)
|
|
199
200
|
if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, scanEnd)
|
|
200
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx,
|
|
201
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, new PlainLeaf(value, span))
|
|
201
202
|
return { ok: true, value, span }
|
|
202
203
|
}
|
|
203
204
|
anchored.lastIndex = pos
|
|
@@ -205,9 +206,9 @@ export function regex(pattern: string | RegExp, flags = ''): Combinator<string>
|
|
|
205
206
|
if (m === null) {
|
|
206
207
|
return failAt(ctx, directTerminalFailureExpected(def), pos)
|
|
207
208
|
}
|
|
208
|
-
const span =
|
|
209
|
+
const span = new PlainSpan(pos, pos + m[0]!.length)
|
|
209
210
|
if (canMatchNewline && ctx.trackLines) recordLineRangeFromContext(ctx, input, pos, span.end)
|
|
210
|
-
if (cstCaptureActive(ctx)) pushCstLeaf(ctx,
|
|
211
|
+
if (cstCaptureActive(ctx)) pushCstLeaf(ctx, new PlainLeaf(m[0]!, span))
|
|
211
212
|
return { ok: true, value: m[0]!, span }
|
|
212
213
|
},
|
|
213
214
|
}
|
|
@@ -12,6 +12,7 @@ import { ref } from './ref.ts'
|
|
|
12
12
|
import { pushCstLeaf, cstCaptureActive } from '../cst/capture-buffer.ts'
|
|
13
13
|
import { token } from './token.ts'
|
|
14
14
|
import { recordLineRangeFromContext } from '../line-index.ts'
|
|
15
|
+
import { PlainLeaf, PlainSpan } from '../cst/plain.js'
|
|
15
16
|
|
|
16
17
|
export type ScanToOptions = {
|
|
17
18
|
/**
|
|
@@ -154,7 +155,7 @@ export function scanTo(
|
|
|
154
155
|
// returned value carries it). Skipped when no collector is active.
|
|
155
156
|
const emit = (end: number) => {
|
|
156
157
|
if (end > pos && cstCaptureActive(ctx)) {
|
|
157
|
-
const leaf =
|
|
158
|
+
const leaf = new PlainLeaf(input.slice(pos, end), new PlainSpan(pos, end))
|
|
158
159
|
pushCstLeaf(ctx, leaf)
|
|
159
160
|
}
|
|
160
161
|
}
|
package/src/combinators/token.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Combinator, ParseContext, ParseResult, ParserMeta } from '../types.ts'
|
|
2
2
|
import { pushCstLeaf, cstCaptureActive } from '../cst/capture-buffer.ts'
|
|
3
3
|
import { cstOutputHost } from '../compiler/build-arity.ts'
|
|
4
|
+
import { PlainLeaf } from '../cst/plain.js'
|
|
4
5
|
|
|
5
6
|
type LeafCstValue<U> = (ctx: ParseContext, input: string, start: number, end: number, value: U) => unknown
|
|
6
7
|
|
|
@@ -70,7 +71,7 @@ export function token(root: Combinator<unknown>): Combinator<string> {
|
|
|
70
71
|
|
|
71
72
|
const span = result.span
|
|
72
73
|
const value = input.slice(pos, span.end)
|
|
73
|
-
if (wasCapturing) pushCstLeaf(ctx,
|
|
74
|
+
if (wasCapturing) pushCstLeaf(ctx, new PlainLeaf(value, span))
|
|
74
75
|
return { ok: true, value, span }
|
|
75
76
|
},
|
|
76
77
|
}
|
|
@@ -133,11 +134,7 @@ function makeLeaf<T, U>(
|
|
|
133
134
|
}
|
|
134
135
|
if (!result.ok) return result
|
|
135
136
|
const value = fn(result.value, result.span)
|
|
136
|
-
if (wasCapturing) pushCstLeaf(ctx,
|
|
137
|
-
_tag: 'leaf',
|
|
138
|
-
value: cstValue(ctx, input, pos, result.span.end, value),
|
|
139
|
-
span: result.span,
|
|
140
|
-
})
|
|
137
|
+
if (wasCapturing) pushCstLeaf(ctx, new PlainLeaf(cstValue(ctx, input, pos, result.span.end, value), result.span))
|
|
141
138
|
return { ok: true, value, span: result.span }
|
|
142
139
|
},
|
|
143
140
|
}
|
package/src/compiler/linker.ts
CHANGED
|
@@ -19,6 +19,8 @@ import { GRAMMAR_REFLECTION } from '../cst/reflection.ts'
|
|
|
19
19
|
import { linkRules, RULE_ORDER, RULES_RECIPE, type LinkOptions, type RulesRecipe } from '../combinators/parser.ts'
|
|
20
20
|
import type { BuildHost, Combinator, CstCollapsePredicate, ParseContext, ParseResult } from '../types.ts'
|
|
21
21
|
import type { Runnable } from '../functional/run.ts'
|
|
22
|
+
import { PlainNode, PlainTaggedNode } from '../cst/plain.js'
|
|
23
|
+
import { copySpan } from '../line-index.ts'
|
|
22
24
|
|
|
23
25
|
/**
|
|
24
26
|
* Compile a `rules()` map to a **linkable artifact** — the composable, shippable
|
|
@@ -92,8 +94,8 @@ function buildCstNode(
|
|
|
92
94
|
// Carry the grammar's `ctx.state` snapshot onto the node (null when unset) — the
|
|
93
95
|
// CST contract includes `state` and incremental re-parse replays it on edit.
|
|
94
96
|
return tags !== undefined && tags.length > 0
|
|
95
|
-
?
|
|
96
|
-
:
|
|
97
|
+
? new PlainTaggedNode(type, tags, copySpan(span), state ?? null, [...children])
|
|
98
|
+
: new PlainNode(type, copySpan(span), state ?? null, [...children])
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ParseContext, Span } from '../types.ts'
|
|
2
|
-
import { annotateSpanFromLineContext } from '../line-index.ts'
|
|
2
|
+
import { annotateSpanFromLineContext, lineSpanFromContext } from '../line-index.ts'
|
|
3
|
+
import { PlainBuf, PlainLeaf } from './plain.js'
|
|
3
4
|
|
|
4
5
|
/** Lazy per-node capture state — arrays materialized on first push. */
|
|
5
6
|
export type CstCaptureBuf = {
|
|
@@ -42,7 +43,7 @@ export function beginCstNodeCapture(ctx: ParseContext): CstNodeCaptureSaved {
|
|
|
42
43
|
cap: ctx.captureTrivia,
|
|
43
44
|
buf: ctx._cstBuf,
|
|
44
45
|
}
|
|
45
|
-
ctx._cstBuf =
|
|
46
|
+
ctx._cstBuf = new PlainBuf(undefined, undefined, 0)
|
|
46
47
|
ctx._cstChildren = undefined
|
|
47
48
|
ctx._cstLeaves = undefined
|
|
48
49
|
ctx._cstRawChildren = undefined
|
|
@@ -79,14 +80,21 @@ export function endCstNodeCapture(
|
|
|
79
80
|
return materialized
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
/** Every caller passes a leaf `{ _tag, value, span }` (parseman's own terminals; this is not public API). */
|
|
84
|
+
function isLeaf(v: unknown): v is { _tag: 'leaf'; value: unknown; span: Span } {
|
|
85
|
+
return typeof v === 'object' && v !== null && (v as { _tag?: unknown })._tag === 'leaf'
|
|
86
|
+
}
|
|
87
|
+
|
|
82
88
|
export function pushCstLeaf(ctx: ParseContext, leaf: unknown): void {
|
|
83
89
|
if (!ctx.trackLines) {
|
|
84
90
|
pushCstChild(ctx, leaf, leaf)
|
|
85
91
|
return
|
|
86
92
|
}
|
|
87
|
-
const annotated =
|
|
88
|
-
?
|
|
89
|
-
: leaf
|
|
93
|
+
const annotated = isLeaf(leaf)
|
|
94
|
+
? new PlainLeaf(leaf.value, lineSpanFromContext(leaf.span, ctx))
|
|
95
|
+
: leaf && typeof leaf === 'object' && (leaf as { span?: unknown }).span
|
|
96
|
+
? { ...(leaf as Record<string, unknown>), span: annotateSpanFromLineContext((leaf as { span: Span }).span, ctx) }
|
|
97
|
+
: leaf
|
|
90
98
|
pushCstChild(ctx, annotated, annotated)
|
|
91
99
|
}
|
|
92
100
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The objects a parse keeps until it ends — spans, leaves, default nodes, field
|
|
3
|
+
* captures, capture buffers — built by `new` calls instead of object literals.
|
|
4
|
+
*
|
|
5
|
+
* WHY. V8 keeps an allocation site per object literal and decides, per site, whether to
|
|
6
|
+
* allocate straight into old space (pretenuring). The decision is taken during warm-up
|
|
7
|
+
* from where scavenges happen to fall, so identical processes split into a fast and a
|
|
8
|
+
* slow mode, and any change in bundle layout re-rolls it: on Node 26 a pretenured
|
|
9
|
+
* process ran 34–61 mark-compacts per run where a fast one ran 1. A `new` call has no
|
|
10
|
+
* allocation site, so these objects are never pretenured. Each constructor's
|
|
11
|
+
* `prototype` is `Object.prototype`, so what it builds is a plain object: same
|
|
12
|
+
* prototype, same `constructor`, same keys in the same order as the literal it replaces.
|
|
13
|
+
*
|
|
14
|
+
* ONE MODULE. Every constructor defines its own hidden class, so two copies of this
|
|
15
|
+
* module build two maps for the same shape, and every site that reads both turns
|
|
16
|
+
* polymorphic. The build therefore emits it once (`dist/cst/plain.js`) and every public
|
|
17
|
+
* entry imports that file (`scripts/build.mjs`). The emitted engine receives the same
|
|
18
|
+
* functions as factory parameters (`EMITTED_PARAMS`).
|
|
19
|
+
*/
|
|
20
|
+
import type { Span } from '../types.ts'
|
|
21
|
+
import type { CstCaptureBuf } from './capture-buffer.ts'
|
|
22
|
+
|
|
23
|
+
export type PlainLeafObject = { _tag: 'leaf'; value: unknown; span: Span }
|
|
24
|
+
export type PlainNodeObject = { _tag: 'node'; type: string; span: Span; state: unknown; children: unknown[] }
|
|
25
|
+
export type PlainFieldObject = { name: string; value: unknown; span: Span }
|
|
26
|
+
|
|
27
|
+
export declare const PlainSpan: new (start: number, end: number) => Span
|
|
28
|
+
export declare const PlainLeaf: new (value: unknown, span: Span) => PlainLeafObject
|
|
29
|
+
export type PlainLineSpanObject = { start: number; end: number; startLine: number; startColumn: number; endLine: number; endColumn: number }
|
|
30
|
+
export declare const PlainLineSpan: new (start: number, end: number, startLine: number, startColumn: number, endLine: number, endColumn: number) => PlainLineSpanObject
|
|
31
|
+
export declare const PlainTaggedNode: new (type: string, tags: readonly string[], span: Span, state: unknown, children: unknown[]) => { _tag: 'node'; type: string; tags: readonly string[]; span: Span; state: unknown; children: unknown[] }
|
|
32
|
+
export declare const PlainTriviaField: new (name: string, value: unknown, span: Span, leading: string, trailing: string) => { name: string; value: unknown; span: Span; leading: string; trailing: string }
|
|
33
|
+
export declare const PlainNode: new (type: string, span: Span, state: unknown, children: unknown[]) => PlainNodeObject
|
|
34
|
+
export declare const PlainField: new (name: string, value: unknown, span: Span) => PlainFieldObject
|
|
35
|
+
export declare const PlainBuf: new (rawOnly: true | undefined, noRaw: true | undefined, rawLen: number) => CstCaptureBuf
|
package/src/cst/plain.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Constructors for the objects a parse keeps until it ends. Plain JavaScript on
|
|
2
|
+
// purpose: a function constructor whose `prototype` is `Object.prototype` is the only
|
|
3
|
+
// spelling that builds a plain object with a `new` call, and TypeScript cannot declare
|
|
4
|
+
// one in a `.ts` file without a cast. `plain.d.ts` types them. Why constructors at all,
|
|
5
|
+
// and why this is ONE module shared by every bundle: see `plain.d.ts`.
|
|
6
|
+
|
|
7
|
+
export function PlainSpan(start, end) {
|
|
8
|
+
this.start = start
|
|
9
|
+
this.end = end
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function PlainLeaf(value, span) {
|
|
13
|
+
this._tag = 'leaf'
|
|
14
|
+
this.value = value
|
|
15
|
+
this.span = span
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function PlainLineSpan(start, end, startLine, startColumn, endLine, endColumn) {
|
|
19
|
+
this.start = start
|
|
20
|
+
this.end = end
|
|
21
|
+
this.startLine = startLine
|
|
22
|
+
this.startColumn = startColumn
|
|
23
|
+
this.endLine = endLine
|
|
24
|
+
this.endColumn = endColumn
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function PlainNode(type, span, state, children) {
|
|
28
|
+
this._tag = 'node'
|
|
29
|
+
this.type = type
|
|
30
|
+
this.span = span
|
|
31
|
+
this.state = state
|
|
32
|
+
this.children = children
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function PlainTaggedNode(type, tags, span, state, children) {
|
|
36
|
+
this._tag = 'node'
|
|
37
|
+
this.type = type
|
|
38
|
+
this.tags = tags
|
|
39
|
+
this.span = span
|
|
40
|
+
this.state = state
|
|
41
|
+
this.children = children
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function PlainField(name, value, span) {
|
|
45
|
+
this.name = name
|
|
46
|
+
this.value = value
|
|
47
|
+
this.span = span
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function PlainTriviaField(name, value, span, leading, trailing) {
|
|
51
|
+
this.name = name
|
|
52
|
+
this.value = value
|
|
53
|
+
this.span = span
|
|
54
|
+
this.leading = leading
|
|
55
|
+
this.trailing = trailing
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Every field assigned, so the three buffer kinds share one shape. `rawLen` is always a
|
|
59
|
+
// count: a field that held `undefined` in most buffers and a number in some would
|
|
60
|
+
// generalize and deoptimize the pushes that read it.
|
|
61
|
+
export function PlainBuf(rawOnly, noRaw, rawLen) {
|
|
62
|
+
this.rawOnly = rawOnly
|
|
63
|
+
this.noRaw = noRaw
|
|
64
|
+
this.rawLen = rawLen
|
|
65
|
+
this.single = undefined
|
|
66
|
+
this.ch = undefined
|
|
67
|
+
this.rawSingle = undefined
|
|
68
|
+
this.raw = undefined
|
|
69
|
+
this.tl = undefined
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const f of [PlainSpan, PlainLineSpan, PlainLeaf, PlainNode, PlainTaggedNode, PlainField, PlainTriviaField, PlainBuf]) {
|
|
73
|
+
f.prototype = Object.prototype
|
|
74
|
+
}
|
package/src/line-index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Span } from './types.ts'
|
|
2
|
+
import { PlainLineSpan, PlainSpan } from './cst/plain.js'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Precomputed index of newline positions for O(log n) offset->line/col lookup.
|
|
@@ -98,6 +99,42 @@ export function annotateSpan(span: Span, index: LineIndex): Span {
|
|
|
98
99
|
}
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
const SPAN_KEYS = ['start', 'end', 'startLine', 'startColumn', 'endLine', 'endColumn'] as const
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* A copy of a span, exactly as given: the same keys in the same order. The two shapes
|
|
106
|
+
* parseman builds itself, `{ start, end }` and the six-key line span, are rebuilt by their
|
|
107
|
+
* constructors (`src/cst/plain.d.ts`); any other shape a caller passes (an extra key, a
|
|
108
|
+
* different order, a partial line span) is spread, as before.
|
|
109
|
+
*/
|
|
110
|
+
export function copySpan(span: Span): Span {
|
|
111
|
+
// `for…in` walks the keys in order without allocating a key list.
|
|
112
|
+
let n = 0
|
|
113
|
+
for (const key in span) {
|
|
114
|
+
if (key !== SPAN_KEYS[n]) return { ...span }
|
|
115
|
+
n++
|
|
116
|
+
}
|
|
117
|
+
if (n === 2) return new PlainSpan(span.start, span.end)
|
|
118
|
+
if (n === 6 && span.startLine !== undefined && span.startColumn !== undefined && span.endLine !== undefined && span.endColumn !== undefined) {
|
|
119
|
+
return new PlainLineSpan(span.start, span.end, span.startLine, span.startColumn, span.endLine, span.endColumn)
|
|
120
|
+
}
|
|
121
|
+
return { ...span }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* `annotateSpanFromLineContext` for a span parseman built itself (`{ start, end }` only),
|
|
126
|
+
* returning a constructor-built line span (`src/cst/plain.d.ts`). The public form keeps
|
|
127
|
+
* any extra keys a caller's span carries, so it spreads.
|
|
128
|
+
*/
|
|
129
|
+
export function lineSpanFromContext(span: Span, ctx: LineTrackingContext): Span {
|
|
130
|
+
if (!ctx.trackLines) return span
|
|
131
|
+
const index = ctx._lineIndex ?? (ctx._lineStarts ? { lineStarts: ctx._lineStarts } : undefined)
|
|
132
|
+
if (!index) return span
|
|
133
|
+
const s = offsetToLineCol(index, span.start)
|
|
134
|
+
const e = offsetToLineCol(index, span.end)
|
|
135
|
+
return new PlainLineSpan(span.start, span.end, s.line, s.col, e.line, e.col)
|
|
136
|
+
}
|
|
137
|
+
|
|
101
138
|
export function annotateSpanFromLineContext(
|
|
102
139
|
span: Span,
|
|
103
140
|
ctx: LineTrackingContext,
|
|
@@ -12,6 +12,7 @@ import { OP_ATTEMPT, OP_NODE, OP_NODE_TRACK, OP_REPV, OP_SCAN } from '../table/o
|
|
|
12
12
|
import { emitAssemblySource, type EmitResult } from '../table/emit-assembly.ts'
|
|
13
13
|
import { defaultAssemblyCfgs } from '../table/emit.ts'
|
|
14
14
|
import { assemblyExtraIps, resolveTable, type TableProgram } from '../table/program.ts'
|
|
15
|
+
import { FACTORY_ARITY_GUARD } from '../table/emit.ts'
|
|
15
16
|
|
|
16
17
|
type Ast = { type: string; start?: number; end?: number; [key: string]: unknown }
|
|
17
18
|
|
|
@@ -271,8 +272,8 @@ export function supercompileRuleMapReplacement(
|
|
|
271
272
|
if (typeof body.start !== 'number' || typeof body.end !== 'number') return replacement
|
|
272
273
|
const start = body.start - prefix.length + 1
|
|
273
274
|
const end = body.end - prefix.length - 1
|
|
274
|
-
if (replacement.slice(start, end) !== canonical.source + '\n') return replacement
|
|
275
|
-
const transformed = replacement.slice(0, start) + candidate.source + '\n' + replacement.slice(end)
|
|
275
|
+
if (replacement.slice(start, end) !== FACTORY_ARITY_GUARD + canonical.source + '\n') return replacement
|
|
276
|
+
const transformed = replacement.slice(0, start) + FACTORY_ARITY_GUARD + candidate.source + '\n' + replacement.slice(end)
|
|
276
277
|
const checked = parseSync('parseman-entry-supercompile-replacement-result.js', prefix + transformed)
|
|
277
278
|
return checked.errors.length === 0 ? transformed : replacement
|
|
278
279
|
}
|
package/src/recovery/scan.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Combinator, FirstSet, ParseContext, ParseError, ParseResult, Recov
|
|
|
2
2
|
import { cstCaptureActive, pushCstChild } from '../cst/capture-buffer.ts'
|
|
3
3
|
import { rangesHaveLead } from '../lead-ranges.ts'
|
|
4
4
|
import { annotateSpanFromLineContext, recordLineRangeFromContext } from '../line-index.ts'
|
|
5
|
+
import { PlainSpan } from '../cst/plain.js'
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Recovery MECHANISM — sync-source-agnostic. These primitives implement "scan
|
|
@@ -112,7 +113,7 @@ export function recoverScan(
|
|
|
112
113
|
): { error: ParseError; end: number } {
|
|
113
114
|
let scanPos = from
|
|
114
115
|
while (scanPos < input.length && !probeAt(sync, input, scanPos, ctx)) scanPos++
|
|
115
|
-
const span =
|
|
116
|
+
const span = new PlainSpan(from, scanPos)
|
|
116
117
|
if (ctx.trackLines) recordLineRangeFromContext(ctx, input, from, scanPos)
|
|
117
118
|
const error: ParseError = { _tag: 'parseError', span: ctx.trackLines ? annotateSpanFromLineContext(span, ctx) : span, expected }
|
|
118
119
|
ctx._errors?.push(error)
|