parseman 0.46.0 → 0.47.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/README.md +8 -3
- package/dist/analysis/choice-cost-render.d.ts +12 -4
- package/dist/analysis/choice-cost-render.d.ts.map +1 -1
- package/dist/analysis/choice-cost.d.ts +2 -2
- package/dist/analysis/choice-cost.d.ts.map +1 -1
- package/dist/analysis/commitment.d.ts +131 -0
- package/dist/analysis/commitment.d.ts.map +1 -0
- package/dist/analysis/diagnostics.cjs +9929 -7465
- package/dist/analysis/diagnostics.cjs.map +3 -4
- package/dist/analysis/diagnostics.d.ts +2 -2
- package/dist/analysis/diagnostics.js +9929 -7465
- package/dist/analysis/diagnostics.js.map +3 -4
- package/dist/analysis/duplication.d.ts.map +1 -1
- package/dist/analysis/fix.d.ts +3 -3
- package/dist/analysis/fix.d.ts.map +1 -1
- package/dist/analysis/format-number.d.ts +23 -0
- package/dist/analysis/format-number.d.ts.map +1 -0
- package/dist/analysis/gating.d.ts +39 -2
- package/dist/analysis/gating.d.ts.map +1 -1
- package/dist/analysis/rebuild.d.ts.map +1 -1
- package/dist/analysis/terminal.d.ts +5 -2
- package/dist/analysis/terminal.d.ts.map +1 -1
- package/dist/cli/index.cjs +10865 -8535
- package/dist/cli/index.cjs.map +3 -4
- package/dist/cli/index.js +10865 -8535
- package/dist/cli/index.js.map +3 -4
- package/dist/combinators/adjacency.d.ts +100 -0
- package/dist/combinators/adjacency.d.ts.map +1 -0
- package/dist/combinators/choice.d.ts.map +1 -1
- package/dist/combinators/completions.d.ts.map +1 -1
- package/dist/combinators/dispatch.d.ts +8 -1
- package/dist/combinators/dispatch.d.ts.map +1 -1
- package/dist/combinators/expect.d.ts.map +1 -1
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/gate.d.ts.map +1 -1
- package/dist/combinators/grammar.d.ts +22 -0
- package/dist/combinators/grammar.d.ts.map +1 -1
- package/dist/combinators/literal.d.ts +8 -0
- package/dist/combinators/literal.d.ts.map +1 -1
- package/dist/combinators/map.d.ts +0 -1
- package/dist/combinators/map.d.ts.map +1 -1
- package/dist/combinators/node.d.ts +13 -3
- package/dist/combinators/node.d.ts.map +1 -1
- package/dist/combinators/parser.d.ts +1 -1
- package/dist/combinators/parser.d.ts.map +1 -1
- package/dist/combinators/ref.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts +1 -16
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/repeat.d.ts +30 -2
- package/dist/combinators/repeat.d.ts.map +1 -1
- package/dist/combinators/scanTo.d.ts +71 -1
- package/dist/combinators/scanTo.d.ts.map +1 -1
- package/dist/combinators/sequence.d.ts.map +1 -1
- package/dist/combinators/token.d.ts.map +1 -1
- package/dist/combinators/trivia-boundary.d.ts +26 -0
- package/dist/combinators/trivia-boundary.d.ts.map +1 -0
- package/dist/combinators/trivia-skip.d.ts +40 -1
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/combinators/withCtx.d.ts.map +1 -1
- package/dist/compiler/compile-linkable-table.d.ts +104 -0
- package/dist/compiler/compile-linkable-table.d.ts.map +1 -0
- package/dist/compiler/degradation.d.ts +0 -2
- package/dist/compiler/degradation.d.ts.map +1 -1
- package/dist/compiler/fields.d.ts +13 -0
- package/dist/compiler/fields.d.ts.map +1 -1
- package/dist/compiler/grammar-coverage-ids.d.ts +5 -5
- package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
- package/dist/compiler/ir-serialize.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts +47 -53
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/compiler/token-alphabet.d.ts +81 -0
- package/dist/compiler/token-alphabet.d.ts.map +1 -0
- package/dist/compiler/token-dispatch.d.ts +95 -0
- package/dist/compiler/token-dispatch.d.ts.map +1 -0
- package/dist/compiler/token-scanner.d.ts +53 -0
- package/dist/compiler/token-scanner.d.ts.map +1 -0
- package/dist/compiler/value-usage.d.ts.map +1 -1
- package/dist/coverage.d.ts +1 -1
- package/dist/coverage.d.ts.map +1 -1
- package/dist/cst/capture-buffer.d.ts +35 -0
- package/dist/cst/capture-buffer.d.ts.map +1 -1
- package/dist/cst/reflection-attach.d.ts +8 -0
- package/dist/cst/reflection-attach.d.ts.map +1 -0
- package/dist/cst/reflection-symbols.d.ts +10 -0
- package/dist/cst/reflection-symbols.d.ts.map +1 -0
- package/dist/cst/reflection.d.ts +2 -4
- package/dist/cst/reflection.d.ts.map +1 -1
- package/dist/cst/root-trivia-scope.d.ts +26 -0
- package/dist/cst/root-trivia-scope.d.ts.map +1 -0
- package/dist/cst/trivia-charscan.d.ts +18 -0
- package/dist/cst/trivia-charscan.d.ts.map +1 -0
- package/dist/cst/trivia-kinds.d.ts +19 -0
- package/dist/cst/trivia-kinds.d.ts.map +1 -1
- package/dist/functional/doc.d.ts.map +1 -1
- package/dist/functional/run-tabled.d.ts +12 -0
- package/dist/functional/run-tabled.d.ts.map +1 -0
- package/dist/functional/run.d.ts +25 -42
- package/dist/functional/run.d.ts.map +1 -1
- package/dist/grammar-metadata.d.ts +3 -0
- package/dist/grammar-metadata.d.ts.map +1 -0
- package/dist/index.cjs +9224 -7203
- package/dist/index.cjs.map +3 -4
- package/dist/index.d.ts +18 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9219 -7198
- package/dist/index.js.map +3 -4
- package/dist/language-service/index.cjs +311 -121
- package/dist/language-service/index.cjs.map +3 -4
- package/dist/language-service/index.js +311 -121
- package/dist/language-service/index.js.map +3 -4
- package/dist/oracle/index.cjs.map +0 -1
- package/dist/oracle/index.js.map +0 -1
- package/dist/parse-context.d.ts +49 -0
- package/dist/parse-context.d.ts.map +1 -0
- package/dist/plugin/evaluator.d.ts +40 -1
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.cjs +11146 -9593
- package/dist/plugin/index.cjs.map +3 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +11146 -9593
- package/dist/plugin/index.js.map +3 -4
- package/dist/regex/classes.d.ts +28 -0
- package/dist/regex/classes.d.ts.map +1 -1
- package/dist/regex/first-set.d.ts +14 -0
- package/dist/regex/first-set.d.ts.map +1 -1
- package/dist/run/index.cjs +136 -51
- package/dist/run/index.cjs.map +2 -3
- package/dist/run/index.js +136 -51
- package/dist/run/index.js.map +2 -3
- package/dist/spec/index.cjs +2898 -1623
- package/dist/spec/index.cjs.map +3 -4
- package/dist/spec/index.js +2898 -1623
- package/dist/spec/index.js.map +3 -4
- package/dist/spec/model.d.ts +8 -0
- package/dist/spec/model.d.ts.map +1 -1
- package/dist/table/assemble.d.ts +307 -0
- package/dist/table/assemble.d.ts.map +1 -0
- package/dist/table/cell.d.ts +42 -0
- package/dist/table/cell.d.ts.map +1 -0
- package/dist/table/child-slots.d.ts +30 -0
- package/dist/table/child-slots.d.ts.map +1 -0
- package/dist/table/compile-rule-map.d.ts +136 -0
- package/dist/table/compile-rule-map.d.ts.map +1 -0
- package/dist/table/compile.d.ts +56 -0
- package/dist/table/compile.d.ts.map +1 -0
- package/dist/table/duplication-hook.d.ts +32 -0
- package/dist/table/duplication-hook.d.ts.map +1 -0
- package/dist/table/emit-assembly.d.ts +139 -0
- package/dist/table/emit-assembly.d.ts.map +1 -0
- package/dist/table/emit.d.ts +113 -0
- package/dist/table/emit.d.ts.map +1 -0
- package/dist/table/encode-baseline.d.ts +16 -0
- package/dist/table/encode-baseline.d.ts.map +1 -0
- package/dist/table/encode.d.ts +71 -0
- package/dist/table/encode.d.ts.map +1 -0
- package/dist/table/exec-baseline.d.ts +9 -0
- package/dist/table/exec-baseline.d.ts.map +1 -0
- package/dist/table/exec.d.ts +72 -0
- package/dist/table/exec.d.ts.map +1 -0
- package/dist/table/fold.d.ts +25 -0
- package/dist/table/fold.d.ts.map +1 -0
- package/dist/table/index.cjs +11375 -0
- package/dist/table/index.cjs.map +6 -0
- package/dist/table/index.d.ts +100 -0
- package/dist/table/index.d.ts.map +1 -0
- package/dist/table/index.js +11352 -0
- package/dist/table/index.js.map +6 -0
- package/dist/table/inspect.d.ts +17 -0
- package/dist/table/inspect.d.ts.map +1 -0
- package/dist/table/ops.d.ts +446 -0
- package/dist/table/ops.d.ts.map +1 -0
- package/dist/table/program.d.ts +493 -0
- package/dist/table/program.d.ts.map +1 -0
- package/dist/table/run-support.d.ts +38 -0
- package/dist/table/run-support.d.ts.map +1 -0
- package/dist/{compiler/scannable-run.d.ts → table/scan-shapes.d.ts} +14 -13
- package/dist/table/scan-shapes.d.ts.map +1 -0
- package/dist/table/site-labels.d.ts +58 -0
- package/dist/table/site-labels.d.ts.map +1 -0
- package/dist/table/stamp.d.ts +70 -0
- package/dist/table/stamp.d.ts.map +1 -0
- package/dist/types.d.ts +80 -24
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +25 -2
- package/src/analysis/choice-cost-gate.ts +359 -0
- package/src/analysis/choice-cost-render.ts +293 -0
- package/src/analysis/choice-cost.ts +1008 -0
- package/src/analysis/commitment.ts +557 -0
- package/src/analysis/corpus.ts +130 -0
- package/src/analysis/diagnose-render.ts +625 -0
- package/src/analysis/diagnose.ts +317 -0
- package/src/analysis/diagnostics.ts +33 -0
- package/src/analysis/duplication.ts +1955 -0
- package/src/analysis/fix-render.ts +217 -0
- package/src/analysis/fix.ts +669 -0
- package/src/analysis/format-number.ts +31 -0
- package/src/analysis/gating.ts +792 -0
- package/src/analysis/grammar.ts +72 -0
- package/src/analysis/rebuild.ts +287 -0
- package/src/analysis/terminal.ts +336 -0
- package/src/cli/index.ts +413 -0
- package/src/combinators/adjacency.ts +171 -0
- package/src/combinators/attempt.ts +41 -0
- package/src/combinators/case-fold.ts +90 -0
- package/src/combinators/choice.ts +466 -0
- package/src/combinators/completions.ts +64 -0
- package/src/combinators/dispatch.ts +358 -0
- package/src/combinators/expect.ts +159 -0
- package/src/combinators/first-set.ts +322 -0
- package/src/combinators/gate.ts +50 -0
- package/src/combinators/grammar.ts +272 -0
- package/src/combinators/keywords.ts +145 -0
- package/src/combinators/literal.ts +102 -0
- package/src/combinators/map.ts +106 -0
- package/src/combinators/node.ts +345 -0
- package/src/combinators/not.ts +55 -0
- package/src/combinators/parser.ts +266 -0
- package/src/combinators/peek.ts +63 -0
- package/src/combinators/probe.ts +25 -0
- package/src/combinators/ref.ts +109 -0
- package/src/combinators/regex.ts +170 -0
- package/src/combinators/repeat.ts +561 -0
- package/src/combinators/scanTo.ts +393 -0
- package/src/combinators/sequence.ts +221 -0
- package/src/combinators/token.ts +120 -0
- package/src/combinators/trivia-boundary.ts +48 -0
- package/src/combinators/trivia-skip.ts +821 -0
- package/src/combinators/withCtx.ts +45 -0
- package/src/compiler/build-arity.ts +330 -0
- package/src/compiler/compile-linkable-table.ts +209 -0
- package/src/compiler/degradation.ts +266 -0
- package/src/compiler/fields.ts +191 -0
- package/src/compiler/grammar-coverage-ids.ts +129 -0
- package/src/compiler/ir-serialize.ts +615 -0
- package/src/compiler/linker.ts +954 -0
- package/src/compiler/token-alphabet.ts +209 -0
- package/src/compiler/token-dispatch.ts +353 -0
- package/src/compiler/token-scanner.ts +318 -0
- package/src/compiler/value-usage.ts +147 -0
- package/src/coverage.ts +383 -0
- package/src/cst/capture-buffer.ts +297 -0
- package/src/cst/host-mode.ts +69 -0
- package/src/cst/reflection-attach.ts +11 -0
- package/src/cst/reflection-symbols.ts +9 -0
- package/src/cst/reflection.ts +149 -0
- package/src/cst/relative-spans.ts +207 -0
- package/src/cst/root-trivia-scope.ts +30 -0
- package/src/cst/trivia-charscan.ts +312 -0
- package/src/cst/trivia-entries.ts +285 -0
- package/src/cst/trivia-index.ts +131 -0
- package/src/cst/trivia-kinds.ts +358 -0
- package/src/cst/types.ts +76 -0
- package/src/cst/walk.ts +101 -0
- package/src/functional/doc.ts +959 -0
- package/src/functional/run-tabled.ts +153 -0
- package/src/functional/run.ts +558 -0
- package/src/grammar-metadata.ts +2 -0
- package/src/index.ts +189 -0
- package/src/language-service/index.ts +221 -0
- package/src/line-index.ts +131 -0
- package/src/oracle/digest.ts +620 -0
- package/src/oracle/index.ts +55 -0
- package/src/parse-context.ts +91 -0
- package/src/plugin/direct-builder-static.ts +102 -0
- package/src/plugin/evaluator.ts +1505 -0
- package/src/plugin/free-identifiers.ts +338 -0
- package/src/plugin/index.ts +2281 -0
- package/src/plugin/reducer-resolver.ts +606 -0
- package/src/recovery/scan.ts +155 -0
- package/src/regex/classes.ts +183 -0
- package/src/regex/first-set.ts +316 -0
- package/src/run/index.ts +31 -0
- package/src/spec/ebnf.ts +128 -0
- package/src/spec/index.ts +54 -0
- package/src/spec/model.ts +636 -0
- package/src/spec/railroad-lib.ts +559 -0
- package/src/spec/railroad.ts +256 -0
- package/src/table/assemble.ts +2994 -0
- package/src/table/cell.ts +42 -0
- package/src/table/child-slots.ts +113 -0
- package/src/table/compile-rule-map.ts +336 -0
- package/src/table/compile.ts +244 -0
- package/src/table/duplication-hook.ts +89 -0
- package/src/table/emit-assembly.ts +1766 -0
- package/src/table/emit.ts +418 -0
- package/src/table/encode-baseline.ts +340 -0
- package/src/table/encode.ts +1569 -0
- package/src/table/exec-baseline.ts +537 -0
- package/src/table/exec.ts +1672 -0
- package/src/table/fold.ts +38 -0
- package/src/table/index.ts +108 -0
- package/src/table/inspect.ts +52 -0
- package/src/table/ops.ts +459 -0
- package/src/table/program.ts +855 -0
- package/src/table/run-support.ts +66 -0
- package/src/table/scan-shapes.ts +1578 -0
- package/src/table/site-labels.ts +228 -0
- package/src/table/stamp.ts +229 -0
- package/src/types.ts +563 -0
- package/src/version.ts +16 -0
- package/dist/compiler/codegen.d.ts +0 -334
- package/dist/compiler/codegen.d.ts.map +0 -1
- package/dist/compiler/inline-build.d.ts +0 -11
- package/dist/compiler/inline-build.d.ts.map +0 -1
- package/dist/compiler/inline-callback.d.ts +0 -21
- package/dist/compiler/inline-callback.d.ts.map +0 -1
- package/dist/compiler/line-index.d.ts +0 -2
- package/dist/compiler/line-index.d.ts.map +0 -1
- package/dist/compiler/module-hoist.d.ts +0 -106
- package/dist/compiler/module-hoist.d.ts.map +0 -1
- package/dist/compiler/scannable-run.d.ts.map +0 -1
- package/dist/compiler/scannable-terminal.d.ts +0 -23
- package/dist/compiler/scannable-terminal.d.ts.map +0 -1
- package/dist/compiler/trivia-fast-path.d.ts +0 -38
- package/dist/compiler/trivia-fast-path.d.ts.map +0 -1
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import type { FoldedProgram, TableProgram } from './program.ts'
|
|
2
|
+
import { resolveTable } from './program.ts'
|
|
3
|
+
import { EMITTED_PARAMS, Unemittable, emitAssemblySource } from './emit-assembly.ts'
|
|
4
|
+
import { cfgKey, type RunCfg } from './assemble.ts'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Print a program as the module a build emits.
|
|
8
|
+
*
|
|
9
|
+
* Everything here is DATA plus the author's own reducers. The recognizer is not
|
|
10
|
+
* in this string — it is `exec.ts`, imported from the runtime, shared by every
|
|
11
|
+
* grammar in the bundle and by every variant of each.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
function jsString(s: string): string {
|
|
15
|
+
return JSON.stringify(s)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Serialise one const-pool entry, or REFUSE it.
|
|
20
|
+
*
|
|
21
|
+
* The fallthrough was `JSON.stringify(v)`, which returns the VALUE `undefined`
|
|
22
|
+
* — not a string — for a function, a symbol, or `undefined` itself. Template
|
|
23
|
+
* interpolation then wrote the text `undefined` into the module, producing an
|
|
24
|
+
* artifact that loads and misbehaves rather than one that fails to build. A
|
|
25
|
+
* combinator object reaching here would emit `{}` for the same reason.
|
|
26
|
+
*
|
|
27
|
+
* Fail closed: the pool must only ever hold what the driver can read back.
|
|
28
|
+
*/
|
|
29
|
+
function emitConst(v: unknown): string {
|
|
30
|
+
if (v instanceof RegExp) return `/${v.source}/${v.flags}`
|
|
31
|
+
if (typeof v === 'string') return jsString(v)
|
|
32
|
+
if (v === null || typeof v === 'number' || typeof v === 'boolean') return JSON.stringify(v)
|
|
33
|
+
// An ARRAY of those primitives round-trips exactly, so it belongs on the
|
|
34
|
+
// accept side of this guard rather than the refuse side. `node(tags)` parks a
|
|
35
|
+
// `readonly string[]` in the pool and the host reads it as its 8th argument;
|
|
36
|
+
// refusing it made every tags-bearing grammar unemittable for no reason the
|
|
37
|
+
// guard's own criterion supports. Nested arrays and objects still refuse.
|
|
38
|
+
if (Array.isArray(v) && v.every(x => x === null || typeof x === 'string' || typeof x === 'number' || typeof x === 'boolean')) {
|
|
39
|
+
return `[${v.map(x => (typeof x === 'string' ? jsString(x) : JSON.stringify(x))).join(',')}]`
|
|
40
|
+
}
|
|
41
|
+
// A PLAIN OBJECT whose values are those primitives (or arrays of them)
|
|
42
|
+
// round-trips exactly too, by the same criterion that admitted arrays above.
|
|
43
|
+
// `withCtx(extra, …)` parks one here — `{ inFn: true }` and the like — and
|
|
44
|
+
// refusing it made every withCtx-bearing grammar unemittable for no reason the
|
|
45
|
+
// guard supports. Only a null-prototype-safe plain object qualifies: anything
|
|
46
|
+
// with a class, a function value, or nesting beyond one array level refuses.
|
|
47
|
+
if (
|
|
48
|
+
typeof v === 'object' && v !== null && !Array.isArray(v) && !(v instanceof RegExp)
|
|
49
|
+
&& (Object.getPrototypeOf(v) === Object.prototype || Object.getPrototypeOf(v) === null)
|
|
50
|
+
&& Object.values(v).every(x =>
|
|
51
|
+
x === null || typeof x === 'string' || typeof x === 'number' || typeof x === 'boolean'
|
|
52
|
+
|| (Array.isArray(x) && x.every(y => y === null || typeof y === 'string' || typeof y === 'number' || typeof y === 'boolean')))
|
|
53
|
+
) {
|
|
54
|
+
const entries = Object.entries(v).map(([key, val]) => `${jsString(key)}:${
|
|
55
|
+
typeof val === 'string' ? jsString(val)
|
|
56
|
+
: Array.isArray(val) ? `[${val.map(y => (typeof y === 'string' ? jsString(y) : JSON.stringify(y))).join(',')}]`
|
|
57
|
+
: JSON.stringify(val)}`)
|
|
58
|
+
return `{${entries.join(',')}}`
|
|
59
|
+
}
|
|
60
|
+
const shown = typeof v === 'object' ? Object.prototype.toString.call(v) : typeof v
|
|
61
|
+
throw new TypeError(
|
|
62
|
+
`emitConst: cannot serialise a const-pool entry of type ${shown}. ` +
|
|
63
|
+
'Only strings, numbers, booleans, null and RegExp round-trip through the emitted module; ' +
|
|
64
|
+
'anything else must be carried in prog.fns or refused at encode time.',
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function emitTriviaSpec(t: import('./program.ts').TriviaSpec): string {
|
|
69
|
+
if (t.plain !== undefined) return `{arms:[],plain:[${jsString(t.plain[0])},${jsString(t.plain[1])}]}`
|
|
70
|
+
if (t.alts !== undefined) {
|
|
71
|
+
const alts = t.alts.map(a => `[${jsString(a[0])},${jsString(a[1])}]`).join(',')
|
|
72
|
+
return `{arms:[],alts:[${alts}],min:${t.min ?? 1}}`
|
|
73
|
+
}
|
|
74
|
+
return `{arms:[${t.arms.map(a => `[${jsString(a[0])},${jsString(a[1])},${jsString(a[2])}]`).join(',')}]}`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function emitRef(r: import('./program.ts').SubtreeRef): string {
|
|
78
|
+
return `[${r[0]},${r[1]}]`
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function emitScanSpec(s: import('./program.ts').ScanSpec): string {
|
|
82
|
+
const parts = [`kind:${s.kind}`, `flags:${s.flags}`, `skip:[${s.skip.map(emitRef).join(',')}]`]
|
|
83
|
+
if (s.sentinel !== undefined) parts.push(`sentinel:${emitRef(s.sentinel)}`)
|
|
84
|
+
if (s.sent !== undefined) parts.push(`sent:${s.sent === null ? 'null' : jsString(s.sent)}`)
|
|
85
|
+
if (s.open !== undefined) parts.push(`open:${jsString(s.open)}`)
|
|
86
|
+
if (s.close !== undefined) parts.push(`close:${jsString(s.close)}`)
|
|
87
|
+
return `{${parts.join(',')}}`
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function emitDispatchSpec(d: import('./program.ts').DispatchSpec): string {
|
|
91
|
+
return '{'
|
|
92
|
+
+ `key:[${d.key.map(jsString).join(',')}],`
|
|
93
|
+
+ `keyArm:[${d.keyArm.join(',')}],`
|
|
94
|
+
+ `fold:[${d.fold.map(jsString).join(',')}],`
|
|
95
|
+
+ `foldArm:[${d.foldArm.join(',')}],`
|
|
96
|
+
+ `match:[${d.match.map(m => `[${m[0]},${jsString(m[1])},${jsString(m[2])},${m[3]}]`).join(',')}],`
|
|
97
|
+
+ `routed:[${d.routed.join(',')}],`
|
|
98
|
+
+ `expected:[${d.expected.map(jsString).join(',')}]`
|
|
99
|
+
+ '}'
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* THE OPTION SETS A BUILD PRE-COMPILES, by default.
|
|
104
|
+
*
|
|
105
|
+
* `hostCst` and `trackLines` are ENCODE settings (`TableSettings`), so a program
|
|
106
|
+
* already fixes them — a table encoded for `hostMode: 'ast'` has no CST rows to
|
|
107
|
+
* select. `tolerant` is a per-CALL option (`run({ tolerant })`), so both answers
|
|
108
|
+
* are live for one artifact and both are emitted.
|
|
109
|
+
*
|
|
110
|
+
* `coverage` is never emitted: `emitAssemblySource` refuses a coverage assembly
|
|
111
|
+
* outright, so there is nothing to pre-compile and the closure engine is the
|
|
112
|
+
* correct answer. `probe` (`completionsAt`) is not emitted by default either —
|
|
113
|
+
* it is a language-service path, cold, and doubling every artifact for it is a
|
|
114
|
+
* cost with no reader. Both fall to the closure engine, which is OBSERVABLE on
|
|
115
|
+
* `Assembly.emitRefusal` rather than silent.
|
|
116
|
+
*
|
|
117
|
+
* Overgeneration is deliberate: an emitted variant nobody selects costs bytes
|
|
118
|
+
* and zero runtime.
|
|
119
|
+
*/
|
|
120
|
+
export function defaultAssemblyCfgs(prog: TableProgram): RunCfg[] {
|
|
121
|
+
const hostCst = prog.hostMode === 'cst'
|
|
122
|
+
const trackLines = prog.lines === 1
|
|
123
|
+
return [false, true].map(tolerant => ({
|
|
124
|
+
hostCst, trackLines, tolerant, coverage: false, probe: false,
|
|
125
|
+
}))
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Print the assemblies a build pre-compiled, as the `a:` field of the program
|
|
130
|
+
* literal — see `TableProgram.asm`.
|
|
131
|
+
*
|
|
132
|
+
* THE FACTORY IS A REAL FUNCTION LITERAL. This is a low-level experiment, not
|
|
133
|
+
* a normal compiler route: normal `compile()` and macro output both serialize
|
|
134
|
+
* `a:[]` and use the compact closure assembler. If a future specialization can
|
|
135
|
+
* meet semantic identity and the size budget, it must preserve that one public
|
|
136
|
+
* artifact contract rather than recreate the old runtime/macro split.
|
|
137
|
+
*
|
|
138
|
+
* A refusal is not an error. `Unemittable` names a construct the emitter does
|
|
139
|
+
* not lower; that option set simply gets no entry, and `assemble.ts` runs the
|
|
140
|
+
* closure engine for it and RECORDS why on `Assembly.emitRefusal`.
|
|
141
|
+
*/
|
|
142
|
+
function emitAssemblies(prog: TableProgram, cfgs: readonly RunCfg[]): string[] {
|
|
143
|
+
/**
|
|
144
|
+
* `a:[]` IS NOT `a` ABSENT, and the difference is the whole property.
|
|
145
|
+
*
|
|
146
|
+
* The field's PRESENCE is the compiler artifact contract. That is what switches
|
|
147
|
+
* the runtime `Function` constructor off (`assemble.ts`), and it costs four
|
|
148
|
+
* bytes. Its CONTENTS are optional experimental factories; with none, the
|
|
149
|
+
* artifact runs the closure engine — no eval, no
|
|
150
|
+
* size growth, and the refusal readable on `Assembly.emitRefusal`.
|
|
151
|
+
*
|
|
152
|
+
* Pre-compiling is therefore a SPEED option, not a correctness one, and it is
|
|
153
|
+
* priced: two assemblies take json's module from 1,382 B to 58,823 B (42.6x)
|
|
154
|
+
* and css's from 8,987 B to 341,517 B (38.0x). That is the emitted engine's
|
|
155
|
+
* source, which the runtime used to build with `new Function` on every load
|
|
156
|
+
* instead of carrying. Defaulting it ON would hand back the 14x size win the
|
|
157
|
+
* table lowering exists for, so the default is OFF and `defaultAssemblyCfgs`
|
|
158
|
+
* is the one-liner for a consumer who wants emitted speed under a CSP.
|
|
159
|
+
*
|
|
160
|
+
* THE DEFAULT IS A MEASUREMENT, NOT A COMMITMENT. It rests on two numbers —
|
|
161
|
+
* the size cost above, and the absence of a demonstrated speed gap the
|
|
162
|
+
* pre-compiled path closes — and only the first of those is settled. A macro
|
|
163
|
+
* artifact measured ~29% slower than `tableRules` over the same grammar
|
|
164
|
+
* during 0.47 (36.0 vs 27.8 ms, 0/16 wins, -0.1% control, identical trees);
|
|
165
|
+
* that is being diagnosed as an emitted-PROGRAM difference, not an engine
|
|
166
|
+
* one, but if pre-compiling turns out to close it the default is an owner
|
|
167
|
+
* decision to reopen. Do not harden this into a design rule.
|
|
168
|
+
*/
|
|
169
|
+
if (cfgs.length === 0) return ['a:[],']
|
|
170
|
+
const t = resolveTable(prog)
|
|
171
|
+
// The scan pool and the scan-skip sets are linked from subtrees, so their
|
|
172
|
+
// sites need emitted names too — same list `assemble.ts` builds.
|
|
173
|
+
const extraIps: number[] = []
|
|
174
|
+
for (const s of prog.scans ?? []) {
|
|
175
|
+
for (const r of s.skip) extraIps.push(r[0])
|
|
176
|
+
if (s.sentinel !== undefined) extraIps.push(s.sentinel[0])
|
|
177
|
+
}
|
|
178
|
+
for (const set of prog.scanSkip ?? []) for (const r of set) extraIps.push(r[0])
|
|
179
|
+
|
|
180
|
+
const out: string[] = []
|
|
181
|
+
const seen = new Set<number>()
|
|
182
|
+
for (const cfg of cfgs) {
|
|
183
|
+
const key = cfgKey(cfg)
|
|
184
|
+
if (seen.has(key)) continue
|
|
185
|
+
seen.add(key)
|
|
186
|
+
let em
|
|
187
|
+
try {
|
|
188
|
+
em = emitAssemblySource(t, prog, cfg, extraIps)
|
|
189
|
+
} catch (e) {
|
|
190
|
+
if (e instanceof Unemittable) continue
|
|
191
|
+
throw e
|
|
192
|
+
}
|
|
193
|
+
const plan = em.plan
|
|
194
|
+
out.push('{'
|
|
195
|
+
+ `key:${key},`
|
|
196
|
+
+ `factory:function(${EMITTED_PARAMS.join(',')}){${em.source}\n},`
|
|
197
|
+
+ 'plan:{'
|
|
198
|
+
+ `classes:[${plan.classes.map(r => `[${r.join(',')}]`).join(',')}],`
|
|
199
|
+
+ `armExpected:[${plan.armExpected.map(r => `[${r.join(',')}]`).join(',')}],`
|
|
200
|
+
+ `masks:[${plan.masks.join(',')}]`
|
|
201
|
+
+ '},'
|
|
202
|
+
+ `reached:[${[...em.reached].join(',')}]`
|
|
203
|
+
+ '}')
|
|
204
|
+
}
|
|
205
|
+
return [`a:[${out.join(',')}],`]
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export type EmitOptions = {
|
|
209
|
+
/** Name of the exported binding. */
|
|
210
|
+
readonly name?: string
|
|
211
|
+
/**
|
|
212
|
+
* Optional experimental emitted factories — see `defaultAssemblyCfgs`.
|
|
213
|
+
*
|
|
214
|
+
* The normal compiler and macro both emit `a:[]`: that is the canonical
|
|
215
|
+
* compact closure artifact and it never reaches the `Function` constructor.
|
|
216
|
+
* Supplying factories here is a low-level serialization experiment, not a
|
|
217
|
+
* second normal compilation path; it is not used by the macro plugin.
|
|
218
|
+
*/
|
|
219
|
+
readonly assemblies?: readonly RunCfg[]
|
|
220
|
+
/**
|
|
221
|
+
* Sources for the author callbacks, in `prog.fns` order. A build has these
|
|
222
|
+
* from the module it is lowering; pass `undefined` to emit a placeholder and
|
|
223
|
+
* measure only the machinery.
|
|
224
|
+
*/
|
|
225
|
+
readonly fnSources?: readonly string[]
|
|
226
|
+
/** Import specifier for the shared driver. */
|
|
227
|
+
readonly runtime?: string
|
|
228
|
+
/**
|
|
229
|
+
* NAME of the driver export to import from `runtime`, defaulting to the
|
|
230
|
+
* shipped one.
|
|
231
|
+
*
|
|
232
|
+
* `runtime` alone was not enough to say which engine a module binds, and that
|
|
233
|
+
* gap was load-bearing: `parseman/table` exports the ASSEMBLER as `tableRules`
|
|
234
|
+
* while `src/table/exec.ts` exports the reference INTERPRETER under a name
|
|
235
|
+
* that used to be identical. Pointing `runtime` at `exec.ts` therefore emitted
|
|
236
|
+
* a module that read as the shipped artifact and ran the reference engine.
|
|
237
|
+
* The reference export is now `execRules`, and a differential that wants it
|
|
238
|
+
* has to SAY so here — which is the whole point of separating the names.
|
|
239
|
+
*/
|
|
240
|
+
readonly runtimeRef?: string
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Refuse a program the printer cannot express, naming the CONSTRUCT. */
|
|
244
|
+
function assertPrintable(prog: TableProgram, who: string): void {
|
|
245
|
+
// Fail with the CONSTRUCT, not with a type name from inside the printer.
|
|
246
|
+
// Every one of these is expressible as data and simply not expressed yet, so
|
|
247
|
+
// the message says which one to go and lower.
|
|
248
|
+
if (prog.runtimeOnly !== undefined && prog.runtimeOnly.length > 0) {
|
|
249
|
+
throw new TypeError(
|
|
250
|
+
`${who}: this grammar is RUNTIME-ONLY — it parses correctly but cannot be `
|
|
251
|
+
+ `printed as a module. Unlowered constructs: ${prog.runtimeOnly.join(', ')}. `
|
|
252
|
+
+ `Each parks a live combinator in the const pool; each is expressible as table rows.`,
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* The program's fields as `key:value,` lines — the body of the object literal
|
|
259
|
+
* `tableRules` reads. Shared with the folded emitter, which prints exactly these
|
|
260
|
+
* for its ONE base table.
|
|
261
|
+
*/
|
|
262
|
+
function programFields(prog: TableProgram, fns: readonly string[], opts: EmitOptions = {}): string[] {
|
|
263
|
+
return [
|
|
264
|
+
...emitAssemblies(prog, opts.assemblies ?? []),
|
|
265
|
+
`c:[${prog.code.join(',')}],`,
|
|
266
|
+
`k:[${prog.k.map(emitConst).join(',')}],`,
|
|
267
|
+
`x:[${prog.cc.map(jsString).join(',')}],`,
|
|
268
|
+
`e:[${prog.fx.map(f => `[${f.map(jsString).join(',')}]`).join(',')}],`,
|
|
269
|
+
`d:[${prog.disp.map(a => `[${a.join(',')}]`).join(',')}],`,
|
|
270
|
+
`r:${JSON.stringify(prog.rules)},`,
|
|
271
|
+
...(prog.lines === 1 ? ['l:1,'] : []),
|
|
272
|
+
// EVERY FIELD `expandCompact` READS MUST BE WRITTEN HERE. `p`/`lb`/`rc`
|
|
273
|
+
// were read but never emitted: a dispatching grammar's module threw
|
|
274
|
+
// "Cannot read properties of undefined (reading 'byKey')" on every input,
|
|
275
|
+
// and a labelled-trivia grammar's entries carried no `_meta`, so
|
|
276
|
+
// `run({ rootTrivia })` rejected a grammar that plainly has labels — the
|
|
277
|
+
// exact failure the root-trivia work exists to prevent, one hop downstream.
|
|
278
|
+
...(prog.dsp.length === 0 ? [] : [`p:[${prog.dsp.map(emitDispatchSpec).join(',')}],`]),
|
|
279
|
+
...(prog.labels === undefined ? [] : [`lb:[${prog.labels.map(jsString).join(',')}],`]),
|
|
280
|
+
...(prog.classified === 1 ? ['rc:1,'] : []),
|
|
281
|
+
// Without this a recovery table's MODULE loads as a strict one: the extra
|
|
282
|
+
// operands are still in `c`, but nothing selects the pieces that read them,
|
|
283
|
+
// so a tolerant parse of the emitted artifact silently collects no errors.
|
|
284
|
+
...(prog.rec === 1 ? ['rv:1,'] : []),
|
|
285
|
+
// The coverage DENOMINATOR travels with the counter rows or not at all. An
|
|
286
|
+
// emitted module whose `c` stream holds `OP_COV` rows and whose `cv` pool went
|
|
287
|
+
// missing has no ids to credit and no definitions to divide by — the
|
|
288
|
+
// "no measurement reported as full coverage" shape, one hop downstream, which
|
|
289
|
+
// is why `assemble` throws on that combination rather than counting nothing.
|
|
290
|
+
...(prog.cov === undefined ? [] : [`cv:[${prog.cov.map(([id, kind]) => `[${jsString(id)},${kind}]`).join(',')}],`]),
|
|
291
|
+
...(prog.hostMode === undefined ? [] : [`h:${jsString(prog.hostMode)},`]),
|
|
292
|
+
...(prog.triviaSpecs === undefined ? [] : [`tv:[${prog.triviaSpecs.map(emitTriviaSpec).join(',')}],`]),
|
|
293
|
+
...(prog.scans === undefined ? [] : [`sc:[${prog.scans.map(emitScanSpec).join(',')}],`]),
|
|
294
|
+
// Both halves or neither: `scanSkipOf` without `scanSkip` installs nothing,
|
|
295
|
+
// and `scanSkip` without `scanSkipOf` installs it nowhere. Either way the
|
|
296
|
+
// module parses and silently skips a different set than the table it came
|
|
297
|
+
// from — the failure shape this lowering exists to remove.
|
|
298
|
+
...(prog.scanSkip === undefined ? [] : [
|
|
299
|
+
`ss:[${prog.scanSkip.map(set => `[${set.map(emitRef).join(',')}]`).join(',')}],`,
|
|
300
|
+
`so:[${(prog.scanSkipOf ?? []).join(',')}],`,
|
|
301
|
+
]),
|
|
302
|
+
`f:[${fns.join(',')}]`,
|
|
303
|
+
]
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function emitTableModule(prog: TableProgram, opts: EmitOptions = {}): string {
|
|
307
|
+
assertPrintable(prog, 'emitTableModule')
|
|
308
|
+
const name = opts.name ?? 'grammar'
|
|
309
|
+
const runtime = opts.runtime ?? 'parseman/table'
|
|
310
|
+
const fns = opts.fnSources ?? prog.fns.map(() => '() => {}')
|
|
311
|
+
const ref = opts.runtimeRef ?? 'tableRules'
|
|
312
|
+
return [
|
|
313
|
+
`import { ${ref} } from ${jsString(runtime)}`,
|
|
314
|
+
`export const ${name} = /* @__PURE__ */ ${ref}({`,
|
|
315
|
+
...programFields(prog, fns, opts),
|
|
316
|
+
`})`,
|
|
317
|
+
].join('\n')
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export type ExpressionEmitOptions = EmitOptions & {
|
|
321
|
+
/**
|
|
322
|
+
* Which rule the expression evaluates to, or `null` for THE WHOLE RULE MAP.
|
|
323
|
+
*
|
|
324
|
+
* `null` is what a `rules()` call site needs: its initialiser evaluates to the
|
|
325
|
+
* `{ name: fn, … }` object, exactly what `tableRules(…)` already returns, so
|
|
326
|
+
* the map form is the expression WITHOUT the trailing index — not a second
|
|
327
|
+
* emitter. `compileRuleMap`'s replacement has the same shape and the same
|
|
328
|
+
* splice point, which is what makes the two lowerings interchangeable there.
|
|
329
|
+
*/
|
|
330
|
+
readonly entry?: string | null
|
|
331
|
+
/** Static metadata object passed to `tableRules` as its second argument. */
|
|
332
|
+
readonly metadataSource?: string
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* The table as an EXPRESSION rather than a module — for an inliner that replaces
|
|
337
|
+
* a grammar's initialiser in place.
|
|
338
|
+
*
|
|
339
|
+
* It references `tableRules` by name instead of carrying the driver, so it is
|
|
340
|
+
* not self-contained. That is deliberate and it is not a cost: the reference
|
|
341
|
+
* resolves to `parseman/table`, a package the consumer already depends on for
|
|
342
|
+
* `run()`. The alternative — inlining the driver per grammar — is precisely the
|
|
343
|
+
* 2.10 MB that this lowering exists to replace with 0.56 MB.
|
|
344
|
+
*
|
|
345
|
+
* The caller owns the import. `emitTableModule` writes its own; an inliner
|
|
346
|
+
* splicing this into existing source must ensure the binding is in scope.
|
|
347
|
+
*/
|
|
348
|
+
export function emitTableExpression(prog: TableProgram, opts: ExpressionEmitOptions = {}): string {
|
|
349
|
+
assertPrintable(prog, 'emitTableExpression')
|
|
350
|
+
const entry = opts.entry === undefined ? 'grammar' : opts.entry
|
|
351
|
+
const ref = opts.runtimeRef ?? 'tableRules'
|
|
352
|
+
const fns = opts.fnSources ?? prog.fns.map(() => '() => {}')
|
|
353
|
+
const close = `}${opts.metadataSource === undefined ? '' : `,${opts.metadataSource}`})`
|
|
354
|
+
return [
|
|
355
|
+
`/* @__PURE__ */ ${ref}({`,
|
|
356
|
+
...programFields(prog, fns, opts),
|
|
357
|
+
entry === null ? close : `${close}[${jsString(entry)}]`,
|
|
358
|
+
].join('\n')
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
export type FoldedEmitOptions = EmitOptions & {
|
|
362
|
+
/**
|
|
363
|
+
* Exported binding per variant name. A variant with no entry is still carried
|
|
364
|
+
* in the table and simply not given a name of its own.
|
|
365
|
+
*/
|
|
366
|
+
readonly names?: Readonly<Record<string, string>>
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Print a FOLDED program: one base table, plus the row edits per variant.
|
|
371
|
+
*
|
|
372
|
+
* This is G4's deliverable. The four `trackLines` x `hostMode` artifacts a
|
|
373
|
+
* dialect ships stop being four near-copies of one table and become one table
|
|
374
|
+
* and three short lists of `(offset, word)` pairs — which is what the measured
|
|
375
|
+
* difference between them actually is. The reducer pool, the const pool, the
|
|
376
|
+
* char classes, the expected sets, the dispatch tables and the rule index are
|
|
377
|
+
* printed ONCE, because they are byte-identical in every variant.
|
|
378
|
+
*
|
|
379
|
+
* The base's own `l`/`h` scalars are NOT printed: every variant, base included,
|
|
380
|
+
* carries its own on its delta, so no variant inherits the base's line-tracking
|
|
381
|
+
* or host mode by accident.
|
|
382
|
+
*/
|
|
383
|
+
export function emitFoldedModule(folded: FoldedProgram, opts: FoldedEmitOptions = {}): string {
|
|
384
|
+
assertPrintable(folded.base, 'emitFoldedModule')
|
|
385
|
+
const runtime = opts.runtime ?? 'parseman/table'
|
|
386
|
+
const fns = opts.fnSources ?? folded.base.fns.map(() => '() => {}')
|
|
387
|
+
const base = programFields(folded.base, fns).filter(l => !l.startsWith('l:') && !l.startsWith('h:'))
|
|
388
|
+
const variants = Object.keys(folded.variants).map(n => {
|
|
389
|
+
const d = folded.variants[n]!
|
|
390
|
+
const parts = [
|
|
391
|
+
...(d.at.length === 0 ? [] : [`a:[${d.at.join(',')}]`, `t:[${d.to.join(',')}]`]),
|
|
392
|
+
...(d.lines === undefined ? [] : [`l:${d.lines}`]),
|
|
393
|
+
...(d.hostMode === undefined ? [] : [`h:${jsString(d.hostMode)}`]),
|
|
394
|
+
]
|
|
395
|
+
return `${jsString(n)}:{${parts.join(',')}}`
|
|
396
|
+
})
|
|
397
|
+
const names = opts.names ?? {}
|
|
398
|
+
return [
|
|
399
|
+
`import { expandCompactFolded, tableVariants } from ${jsString(runtime)}`,
|
|
400
|
+
`const _t = /* @__PURE__ */ expandCompactFolded({`,
|
|
401
|
+
`b:{`,
|
|
402
|
+
...base,
|
|
403
|
+
`},`,
|
|
404
|
+
`v:{${variants.join(',')}}`,
|
|
405
|
+
`})`,
|
|
406
|
+
...Object.keys(names).map(n =>
|
|
407
|
+
`export const ${names[n]!} = /* @__PURE__ */ tableVariants(_t, ${jsString(n)})`),
|
|
408
|
+
].join('\n')
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* The part of the emitted module that is MACHINERY — the table, excluding the
|
|
413
|
+
* author's reducers, which every lowering must emit alike. This is the number
|
|
414
|
+
* that is comparable to codegen's per-rule cost.
|
|
415
|
+
*/
|
|
416
|
+
export function emitTableOnly(prog: TableProgram): string {
|
|
417
|
+
return emitTableModule(prog, { fnSources: [] })
|
|
418
|
+
}
|