parseman 0.45.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-gate.d.ts +158 -0
- package/dist/analysis/choice-cost-gate.d.ts.map +1 -0
- package/dist/analysis/choice-cost-render.d.ts +96 -0
- package/dist/analysis/choice-cost-render.d.ts.map +1 -0
- package/dist/analysis/choice-cost.d.ts +383 -0
- package/dist/analysis/choice-cost.d.ts.map +1 -0
- package/dist/analysis/commitment.d.ts +131 -0
- package/dist/analysis/commitment.d.ts.map +1 -0
- package/dist/analysis/corpus.d.ts +73 -0
- package/dist/analysis/corpus.d.ts.map +1 -0
- package/dist/analysis/diagnose-render.d.ts +65 -0
- package/dist/analysis/diagnose-render.d.ts.map +1 -0
- package/dist/analysis/diagnose.d.ts +15 -0
- package/dist/analysis/diagnose.d.ts.map +1 -1
- package/dist/analysis/diagnostics.cjs +13736 -0
- package/dist/analysis/diagnostics.cjs.map +6 -0
- package/dist/analysis/diagnostics.d.ts +27 -0
- package/dist/analysis/diagnostics.d.ts.map +1 -0
- package/dist/analysis/diagnostics.js +13713 -0
- package/dist/analysis/diagnostics.js.map +6 -0
- package/dist/analysis/duplication.d.ts.map +1 -1
- package/dist/analysis/fix-render.d.ts +43 -0
- package/dist/analysis/fix-render.d.ts.map +1 -0
- package/dist/analysis/fix.d.ts +172 -0
- package/dist/analysis/fix.d.ts.map +1 -0
- 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 +47 -2
- package/dist/analysis/gating.d.ts.map +1 -1
- package/dist/analysis/rebuild.d.ts +61 -0
- package/dist/analysis/rebuild.d.ts.map +1 -0
- package/dist/analysis/terminal.d.ts +145 -0
- package/dist/analysis/terminal.d.ts.map +1 -0
- package/dist/cli/index.cjs +14837 -0
- package/dist/cli/index.cjs.map +6 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +14814 -0
- package/dist/cli/index.js.map +6 -0
- 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/build-arity.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 +79 -47
- 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 +10499 -7478
- package/dist/index.cjs.map +3 -4
- package/dist/index.d.ts +24 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10499 -7478
- 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 +41 -2
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/free-identifiers.d.ts +23 -0
- package/dist/plugin/free-identifiers.d.ts.map +1 -0
- package/dist/plugin/index.cjs +11381 -9442
- package/dist/plugin/index.cjs.map +3 -4
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +11381 -9442
- package/dist/plugin/index.js.map +3 -4
- package/dist/plugin/reducer-resolver.d.ts +17 -1
- package/dist/plugin/reducer-resolver.d.ts.map +1 -1
- 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 -1622
- package/dist/spec/index.cjs.map +3 -4
- package/dist/spec/index.js +2898 -1622
- 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 +41 -3
- 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/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,244 @@
|
|
|
1
|
+
import type { Combinator, CompiledParser, ParseContext, ParseError, ParseResult } from '../types.ts'
|
|
2
|
+
import type { HostMode } from '../cst/host-mode.ts'
|
|
3
|
+
import { createParseContext } from '../parse-context.ts'
|
|
4
|
+
import { encodeTableProgram, type TableSettings } from './encode.ts'
|
|
5
|
+
import { emitTableModule, emitTableExpression } from './emit.ts'
|
|
6
|
+
import { tableRules } from './assemble.ts'
|
|
7
|
+
import { closureArtifact } from './program.ts'
|
|
8
|
+
import { buildGrammarPlan } from '../compiler/grammar-coverage-ids.ts'
|
|
9
|
+
import { runDuplicationDiagnostic, type DuplicationOption } from './duplication-hook.ts'
|
|
10
|
+
import { beginCompileDegradationDrain } from '../compiler/degradation.ts'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* `compile()` FOR THE TABLE LOWERING — same contract, different artifact.
|
|
14
|
+
*
|
|
15
|
+
* The entry rule is named rather than positional because the table indexes rules
|
|
16
|
+
* by name. A root combinator is simply a one-rule map: `{ Entry: root }` encodes
|
|
17
|
+
* and round-trips value-identically to the interpreter, which is what makes this
|
|
18
|
+
* a drop-in for the source-lowering `compile()` rather than a second API.
|
|
19
|
+
*/
|
|
20
|
+
const ENTRY = 'Entry'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* THE LAST RESORT FOR A REDUCER'S SOURCE — the function's own text.
|
|
24
|
+
*
|
|
25
|
+
* Third in precedence, behind the encoder's captured `fnSrc`/`buildSrc`/`predSrc`
|
|
26
|
+
* and behind a positional list. It exists because `compile` is the library
|
|
27
|
+
* `compile()` (`src/index.ts`), and a grammar BUILT AT RUNTIME has no captured
|
|
28
|
+
* sources at all — every `examples/*` fixture is one. Refusing them outright would
|
|
29
|
+
* make `.source` and `.inlineExpression` empty for every library caller, which
|
|
30
|
+
* `test/unit/table-compile.test.ts` pins against directly.
|
|
31
|
+
*
|
|
32
|
+
* WHAT IT IS NOT is a licence to print a placeholder. `() => {}` is only ever
|
|
33
|
+
* produced here if the author literally wrote it, so a reducer that silently
|
|
34
|
+
* returns nothing is not reachable through this path. A closure over its module's
|
|
35
|
+
* privates prints text that throws a ReferenceError when the module loads — LOUD,
|
|
36
|
+
* at load, naming the binding — which is the failure mode this project prefers
|
|
37
|
+
* over an artifact that parses and returns the wrong tree.
|
|
38
|
+
*
|
|
39
|
+
* Native and bound functions have no recoverable body (`[native code]`), so they
|
|
40
|
+
* return null and the caller refuses BY NAME.
|
|
41
|
+
*/
|
|
42
|
+
function ownSource(fn: unknown): string | null {
|
|
43
|
+
if (typeof fn !== 'function') return null
|
|
44
|
+
const text = Function.prototype.toString.call(fn)
|
|
45
|
+
if (!text.includes('[native code]')) return text
|
|
46
|
+
// A BUILT-IN GLOBAL USED AS A REDUCER — `leaf(regex(…), parseFloat)` is exactly
|
|
47
|
+
// that, and `examples/graphql/parser.ts` writes it. It has no body to print, but
|
|
48
|
+
// its NAME is a global binding that denotes the identical function in any module,
|
|
49
|
+
// so the name IS the faithful source. Verified by identity against `globalThis`,
|
|
50
|
+
// never by name alone: `Number.parseFloat` and a shadowed local both fail that.
|
|
51
|
+
const name = (fn as { name?: unknown }).name
|
|
52
|
+
return typeof name === 'string' && name.length > 0
|
|
53
|
+
&& (globalThis as unknown as Record<string, unknown>)[name] === fn
|
|
54
|
+
? name
|
|
55
|
+
: null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* THE CONTRACT DIVERGENCE, stated rather than hidden.
|
|
60
|
+
*
|
|
61
|
+
* `CompiledParser.inlineExpression` is documented as a self-contained IIFE with
|
|
62
|
+
* "no external references". A table artifact cannot honour that clause and stay
|
|
63
|
+
* a table: the shared driver being EXTERNAL is the entire reason the artifact is
|
|
64
|
+
* 0.56 MB instead of source lowering's 2.10 MB. Inlining the driver per grammar
|
|
65
|
+
* reproduces exactly the size this lowering exists to avoid.
|
|
66
|
+
*
|
|
67
|
+
* So the expression references `tableRules`, and the consumer is responsible for
|
|
68
|
+
* having imported it. That is a real change to what an inliner must do, and it
|
|
69
|
+
* is the reason this is a lowering swap rather than a rename.
|
|
70
|
+
*
|
|
71
|
+
* It is NOT null. Returning null would be the quiet failure: `plugin/index.ts`
|
|
72
|
+
* warns and SKIPS on null, leaving the grammar interpreted — 79 ms against 14 ms
|
|
73
|
+
* on `benchmark.less`, a ~5x regression that no test would have reported.
|
|
74
|
+
*/
|
|
75
|
+
export type TableCompileOptions = {
|
|
76
|
+
readonly hostMode?: HostMode
|
|
77
|
+
readonly trackLines?: boolean
|
|
78
|
+
/**
|
|
79
|
+
* ACCEPTED AND IGNORED — recovery is ALWAYS lowered (see `TableSettings.recovery`).
|
|
80
|
+
* Kept so this stays a drop-in for `compile(g, …, { recovery: true })`.
|
|
81
|
+
*/
|
|
82
|
+
readonly recovery?: boolean
|
|
83
|
+
/**
|
|
84
|
+
* GRAMMAR-COVERAGE COUNTERS, lowered as `OP_COV` rows plus the definition pool
|
|
85
|
+
* (`TableProgram.cov`). Same ids as the source lowering, because both mint them
|
|
86
|
+
* from the same `buildGrammarPlan` walk over the same combinator graph.
|
|
87
|
+
*
|
|
88
|
+
* COUNTERS ONLY — owner ruling. `_grammarTrace`'s six phases are emitted at
|
|
89
|
+
* ~40 fine-grained sites by codegen and are deferred to 0.48
|
|
90
|
+
* (`notes/RELEASE-0.48-TARGET.md` §1). A table entry rejects a context carrying
|
|
91
|
+
* a trace sink before parsing: silently returning an empty trace would look
|
|
92
|
+
* exactly like a real trace whose grammar sites were never visited. The
|
|
93
|
+
* plugin's coverage path reads `_grammarCoverage` and the definitions stamp,
|
|
94
|
+
* neither of which is trace.
|
|
95
|
+
*
|
|
96
|
+
* The remaining options below are accepted for signature compatibility with
|
|
97
|
+
* `compile()`. Any that the table lowering cannot honour THROWS rather than
|
|
98
|
+
* being ignored — a compile that silently drops the instrumentation its caller
|
|
99
|
+
* asked for is the failure class this project keeps finding, and coverage that
|
|
100
|
+
* silently reports nothing is worse than a build error that says why.
|
|
101
|
+
*/
|
|
102
|
+
readonly coverage?: boolean
|
|
103
|
+
readonly duplication?: DuplicationOption
|
|
104
|
+
// NO `maxInline`. It bounded how large ONE emitted function could grow before the
|
|
105
|
+
// source lowering stopped inlining; a table has no emitted function to bound, so the
|
|
106
|
+
// option had nothing to read it and a caller setting it got silence.
|
|
107
|
+
/** Identifier the emitted expression expects to find `tableRules` under. */
|
|
108
|
+
readonly runtimeRef?: string
|
|
109
|
+
/** Source text per `prog.fns` entry, for a module/expression that must be printable. */
|
|
110
|
+
readonly fnSources?: readonly string[]
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export function compile<T>(
|
|
114
|
+
combinator: Combinator<T>,
|
|
115
|
+
mapFnSources?: readonly string[],
|
|
116
|
+
opts: TableCompileOptions = {},
|
|
117
|
+
): CompiledParser<T> {
|
|
118
|
+
// Argument order mirrors `compile(combinator, mapFnSources?, opts?)` so this is
|
|
119
|
+
// a drop-in, not a second API with a different shape.
|
|
120
|
+
// THE SAME WALK CODEGEN USES FOR A SINGLE ROOT (`codegen.ts:5377`): no winner
|
|
121
|
+
// map, so the paths are rooted at `entry` and the ids are the ones a caller
|
|
122
|
+
// switching lowerings already has. Building it differently here would produce a
|
|
123
|
+
// set that is internally consistent and lines up with nothing.
|
|
124
|
+
runDuplicationDiagnostic(combinator, opts.duplication)
|
|
125
|
+
// ONE AGGREGATED BLOCK per compile, as the source lowering drained it. Without this
|
|
126
|
+
// every degradation prints the instant it is recorded, so a grammar with twenty
|
|
127
|
+
// unconfirmable reducers produces twenty walls of text instead of the capped, counted
|
|
128
|
+
// summary — and `PARSEMAN_DEGRADATION=error` stops at the first instead of listing all.
|
|
129
|
+
const drain = beginCompileDegradationDrain()
|
|
130
|
+
let compiled = false
|
|
131
|
+
try {
|
|
132
|
+
const out = compileImpl(combinator, mapFnSources, opts)
|
|
133
|
+
compiled = true
|
|
134
|
+
return out
|
|
135
|
+
} finally { drain(compiled) }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function compileImpl<T>(
|
|
139
|
+
combinator: Combinator<T>,
|
|
140
|
+
mapFnSources?: readonly string[],
|
|
141
|
+
opts: TableCompileOptions = {},
|
|
142
|
+
): CompiledParser<T> {
|
|
143
|
+
const plan = opts.coverage === true ? buildGrammarPlan(combinator as Combinator<unknown>) : undefined
|
|
144
|
+
const settings: TableSettings = {
|
|
145
|
+
...(opts.hostMode === undefined ? {} : { hostMode: opts.hostMode }),
|
|
146
|
+
...(opts.trackLines === undefined ? {} : { trackLines: opts.trackLines }),
|
|
147
|
+
...(plan === undefined ? {} : { coverage: plan }),
|
|
148
|
+
}
|
|
149
|
+
// `encodeTableProgram`, NOT `encodeTable`. The plain `encodeTable` drops the
|
|
150
|
+
// `fnSrcs` side-channel on the floor, and dropping it is what made a printed
|
|
151
|
+
// module return NO VALUE: `emitTable*` falls back to `prog.fns.map(() => '() => {}')`
|
|
152
|
+
// (emit.ts:178/219/252), so every author reducer — `node` build fns, `transform`,
|
|
153
|
+
// `leaf` — became an empty arrow. The grammar still parsed, still returned `ok`,
|
|
154
|
+
// and returned `undefined` where the interpreter and codegen both returned a tree.
|
|
155
|
+
// `compileRuleMap` has guarded this since it was written (compile-rule-map.ts);
|
|
156
|
+
// this is the same guard on the single-root entry point.
|
|
157
|
+
const { prog, fnSrcs } = encodeTableProgram({ [ENTRY]: combinator as Combinator<unknown> }, settings)
|
|
158
|
+
/**
|
|
159
|
+
* `compile()` and the macro must hand the driver the SAME artifact shape.
|
|
160
|
+
*
|
|
161
|
+
* An absent `asm` previously selected runtime `new Function` emission while
|
|
162
|
+
* macro output printed `a:[]` and selected closure assembly. That made one
|
|
163
|
+
* grammar have two normal execution paths. The compact table artifact is now
|
|
164
|
+
* canonical: both paths carry an explicit empty inventory and use the shared
|
|
165
|
+
* closure assembler. Static factories remain an opt-in experiment until they
|
|
166
|
+
* can satisfy both semantic identity and the size budget.
|
|
167
|
+
*/
|
|
168
|
+
const artifact = closureArtifact(prog)
|
|
169
|
+
const entry = tableRules(artifact)[ENTRY]!
|
|
170
|
+
|
|
171
|
+
// Captured-first, supplied-as-fill-in. The encoder records a source per author
|
|
172
|
+
// callback from the def (`fnSrc` / `buildSrc` / `predSrc` / `gateSrcs`), which the
|
|
173
|
+
// macro evaluator sets; the out-of-band list is the escape for a combinator built
|
|
174
|
+
// at runtime, and it only fills HOLES.
|
|
175
|
+
//
|
|
176
|
+
// The list is POSITIONAL, in `prog.fns` order, so it is only usable when it indexes
|
|
177
|
+
// THIS pool — a different length means it came from a different walk (the evaluator
|
|
178
|
+
// collects one entry per callback it SEES; the encoder pools one per callback it
|
|
179
|
+
// EMITS, and those need not coincide) and applying it would misassign reducers
|
|
180
|
+
// silently. A mismatched list is therefore not consulted at all; if that leaves a
|
|
181
|
+
// hole, the refusal below fires and says so, which is the safe direction.
|
|
182
|
+
const offered = opts.fnSources ?? mapFnSources
|
|
183
|
+
const supplied = offered !== undefined && offered.length === fnSrcs.length ? offered : undefined
|
|
184
|
+
const sources = fnSrcs.map((s, i) => s ?? supplied?.[i] ?? ownSource(prog.fns[i]))
|
|
185
|
+
|
|
186
|
+
// `runtimeOnly` reasons mean the program RUNS but cannot be printed. The parse
|
|
187
|
+
// functions below are unaffected; only `source` / `inlineExpression` are, and
|
|
188
|
+
// they degrade to null rather than throwing out of the printer at a call site
|
|
189
|
+
// that only wanted to parse.
|
|
190
|
+
//
|
|
191
|
+
// AN UNSOURCED REDUCER IS ONE OF THOSE REASONS. It is not a licence to print a
|
|
192
|
+
// placeholder: a module that loads, parses, and returns the wrong tree is worse
|
|
193
|
+
// than one that refuses, because nothing reports it. REFUSE, with a name.
|
|
194
|
+
const unsourced = sources.reduce<number[]>((acc, s, i) => (s === null ? [...acc, i] : acc), [])
|
|
195
|
+
const runtimeOnly = [
|
|
196
|
+
...(prog.runtimeOnly ?? []),
|
|
197
|
+
...(unsourced.length === 0
|
|
198
|
+
? []
|
|
199
|
+
: [`author reducer source missing for prog.fns[${unsourced.join(', ')}] — printing would emit \`() => {}\` and the parse would return no value`]),
|
|
200
|
+
]
|
|
201
|
+
const printable = runtimeOnly.length === 0
|
|
202
|
+
const emitOpts = { fnSources: sources as string[] }
|
|
203
|
+
const source = printable ? emitTableModule(artifact, { name: 'grammar', ...emitOpts }) : ''
|
|
204
|
+
const inlineExpression = printable
|
|
205
|
+
? emitTableExpression(artifact, { entry: ENTRY, runtimeRef: opts.runtimeRef ?? 'tableRules', ...emitOpts })
|
|
206
|
+
: null
|
|
207
|
+
|
|
208
|
+
const runOnce = (input: string, pos: number, ctx: ParseContext): ParseResult<T> =>
|
|
209
|
+
entry(input, pos, ctx) as ParseResult<T>
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
source,
|
|
213
|
+
inlineExpression,
|
|
214
|
+
...(printable ? {} : { runtimeOnly }),
|
|
215
|
+
// The DENOMINATOR, handed back rather than scraped out of the emitted text.
|
|
216
|
+
// `plugin/index.ts` prefers a compiler-supplied `coverageDefinitions` and only
|
|
217
|
+
// falls back to regex-scanning generated hooks; a table has no hooks to scan,
|
|
218
|
+
// so this is the channel.
|
|
219
|
+
...(plan === undefined ? {} : { coverageDefinitions: plan.definitions }),
|
|
220
|
+
parse(input: string, pos = 0): ParseResult<T> {
|
|
221
|
+
return runOnce(input, pos, createParseContext())
|
|
222
|
+
},
|
|
223
|
+
parseWithContext(input: string, parseCtx: ParseContext, pos = 0): ParseResult<T> {
|
|
224
|
+
return runOnce(input, pos, parseCtx)
|
|
225
|
+
},
|
|
226
|
+
/**
|
|
227
|
+
* NO LONGER REFUSES. It used to throw unless the caller passed
|
|
228
|
+
* `{ recovery: true }`, because setting `_tolerant` on a table with no
|
|
229
|
+
* recovery rows would report a clean parse with an empty error list whatever
|
|
230
|
+
* the input — refusing was the right answer to that, and OPTIONAL RECOVERY
|
|
231
|
+
* was the wrong question. `compile()` requires no such flag, so the refusal
|
|
232
|
+
* was a `CompiledParser` contract break; recovery is now always lowered and
|
|
233
|
+
* there is nothing left to refuse.
|
|
234
|
+
*/
|
|
235
|
+
parseWithErrors(input: string, pos = 0): ParseResult<T> & { errors: ParseError[] } {
|
|
236
|
+
const ctx = createParseContext()
|
|
237
|
+
const errors: ParseError[] = []
|
|
238
|
+
ctx._errors = errors
|
|
239
|
+
ctx._tolerant = true
|
|
240
|
+
const r = runOnce(input, pos, ctx)
|
|
241
|
+
return { ...r, errors } as ParseResult<T> & { errors: ParseError[] }
|
|
242
|
+
},
|
|
243
|
+
}
|
|
244
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { Combinator } from '../types.ts'
|
|
2
|
+
import { analyzeDuplication, analyzeDuplicationRules, formatDuplicationFindings, duplicationFindingCount, type DuplicationReport, type DuplicationWarnLevel } from '../analysis/duplication.ts'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* THE DUPLICATION DIAGNOSTIC'S LOWERING HOOK.
|
|
6
|
+
*
|
|
7
|
+
* This ran inside the source lowering, so deleting that lowering would have silently
|
|
8
|
+
* taken it with it — the analysis in `analysis/duplication.ts` would survive and
|
|
9
|
+
* nothing would call it, which reads exactly like a clean grammar. It is not a
|
|
10
|
+
* property of how a grammar is lowered, so it lives with the lowering that remains.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* The `duplication` compile option. A bare level is shorthand for `{ level }`; the
|
|
14
|
+
* object form adds the `accept` allowlist (finding ids acknowledged as intentional)
|
|
15
|
+
* and the ranking knobs.
|
|
16
|
+
*
|
|
17
|
+
* DEFAULT IS `'off'`, unlike gating. An ungated hot choice is a cliff with no other
|
|
18
|
+
* symptom; a duplicated subtree is a maintenance cost the author may have chosen.
|
|
19
|
+
* More to the point, most findings here are CANDIDATES that need an AST check
|
|
20
|
+
* before they are applied — a diagnostic that prints "candidate, verify" on every
|
|
21
|
+
* build teaches people to stop reading it. Run it deliberately (a lint script, a
|
|
22
|
+
* review pass, `PARSEMAN_DUPLICATION=warn`), not on every compile.
|
|
23
|
+
*/
|
|
24
|
+
export type DuplicationOption =
|
|
25
|
+
| DuplicationWarnLevel
|
|
26
|
+
| { level?: DuplicationWarnLevel; accept?: Iterable<string>; minSize?: number; maxFindings?: number; entryName?: string }
|
|
27
|
+
|
|
28
|
+
function resolveDuplicationLevel(opt: DuplicationOption | undefined): DuplicationWarnLevel {
|
|
29
|
+
const explicit = typeof opt === 'string' ? opt : opt?.level
|
|
30
|
+
if (explicit !== undefined) return explicit
|
|
31
|
+
const env = typeof process !== 'undefined' ? (process.env?.PARSEMAN_DUPLICATION as DuplicationWarnLevel | undefined) : undefined
|
|
32
|
+
if (env === 'off' || env === 'warn' || env === 'error') return env
|
|
33
|
+
return 'off'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Run the duplication diagnostic and surface it per the resolved level. Never
|
|
38
|
+
* throws from the analysis itself — only `'error'` deliberately throws on a real
|
|
39
|
+
* finding. Shared by ALL THREE lowering paths (`compile`, `compileRuleMap`,
|
|
40
|
+
* `compileLinkable`): the macro build never calls `compile()`, and a diagnostic
|
|
41
|
+
* wired only there is a diagnostic that reports zero findings forever — which is
|
|
42
|
+
* exactly what happened to the gating diagnostic for two minor versions.
|
|
43
|
+
*/
|
|
44
|
+
function reportDuplication(
|
|
45
|
+
opt: DuplicationOption | undefined,
|
|
46
|
+
analyze: (o: { accept?: Iterable<string>; minSize?: number; maxFindings?: number; entryName?: string } | undefined) => DuplicationReport,
|
|
47
|
+
): DuplicationReport | undefined {
|
|
48
|
+
const level = resolveDuplicationLevel(opt)
|
|
49
|
+
if (level === 'off') return undefined
|
|
50
|
+
const obj = opt !== null && typeof opt === 'object' ? opt : undefined
|
|
51
|
+
const analyzeOpts = obj === undefined ? undefined : {
|
|
52
|
+
...(obj.accept !== undefined ? { accept: obj.accept } : {}),
|
|
53
|
+
...(obj.minSize !== undefined ? { minSize: obj.minSize } : {}),
|
|
54
|
+
...(obj.maxFindings !== undefined ? { maxFindings: obj.maxFindings } : {}),
|
|
55
|
+
...(obj.entryName !== undefined ? { entryName: obj.entryName } : {}),
|
|
56
|
+
}
|
|
57
|
+
let report: DuplicationReport
|
|
58
|
+
try { report = analyze(analyzeOpts) }
|
|
59
|
+
catch (err) {
|
|
60
|
+
// The analysis is ADVISORY — it must never break a compile that is otherwise
|
|
61
|
+
// correct, so this does not rethrow even at `'error'`. But it must not be
|
|
62
|
+
// SILENT either: `assertAnalyzable` throws a deliberately actionable TypeError
|
|
63
|
+
// when handed a composed (already-fused) map, and swallowing that reported the
|
|
64
|
+
// same "no findings" as a genuinely clean grammar. That is the exact failure
|
|
65
|
+
// the gating diagnostic shipped for two minor versions.
|
|
66
|
+
console.warn(
|
|
67
|
+
`parseman: the duplication diagnostic could not run, so NOTHING was checked — `
|
|
68
|
+
+ `this is not a clean result.\n ${err instanceof Error ? err.message : String(err)}`,
|
|
69
|
+
)
|
|
70
|
+
return undefined
|
|
71
|
+
}
|
|
72
|
+
const lines = formatDuplicationFindings(report)
|
|
73
|
+
if (lines.length > 0) {
|
|
74
|
+
if (level === 'error') throw new Error(`parseman: ${duplicationFindingCount(report)} duplication/overlap finding(s)\n${lines.join('\n')}`)
|
|
75
|
+
for (const l of lines) console.warn(l)
|
|
76
|
+
}
|
|
77
|
+
return report
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function runDuplicationDiagnostic<T>(combinator: Combinator<T>, opt: DuplicationOption | undefined): DuplicationReport | undefined {
|
|
81
|
+
return reportDuplication(opt, o => analyzeDuplication(combinator as Combinator<unknown>, o))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function runDuplicationDiagnosticRules(
|
|
85
|
+
ruleMap: ReadonlyArray<readonly [string, Combinator<unknown>]>,
|
|
86
|
+
opt: DuplicationOption | undefined,
|
|
87
|
+
): DuplicationReport | undefined {
|
|
88
|
+
return reportDuplication(opt, o => analyzeDuplicationRules(ruleMap, o))
|
|
89
|
+
}
|