parseman 0.51.2 → 0.53.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/commitment.d.ts.map +1 -1
- package/dist/analysis/diagnostics.js +411 -28
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +6162 -6372
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/attempt.d.ts +11 -1
- package/dist/combinators/attempt.d.ts.map +1 -1
- package/dist/combinators/map.d.ts.map +1 -1
- package/dist/combinators/node.d.ts +6 -2
- package/dist/combinators/node.d.ts.map +1 -1
- package/dist/combinators/parser.d.ts +34 -0
- package/dist/combinators/parser.d.ts.map +1 -1
- package/dist/combinators/ref.d.ts +0 -13
- package/dist/combinators/ref.d.ts.map +1 -1
- package/dist/compiler/borrowed-rules.d.ts +30 -0
- package/dist/compiler/borrowed-rules.d.ts.map +1 -0
- package/dist/compiler/compile-linkable-table.d.ts +1 -1
- package/dist/compiler/grammar-coverage-ids.d.ts.map +1 -1
- package/dist/compiler/ir-serialize.d.ts +10 -4
- package/dist/compiler/ir-serialize.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts +48 -91
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/compiler/token-capability.js +4 -0
- package/dist/compiler/token-capability.js.map +1 -1
- package/dist/coverage.d.ts +1 -1
- package/dist/coverage.d.ts.map +1 -1
- package/dist/cst/reflection.d.ts.map +1 -1
- package/dist/cst/trivia-kinds.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +470 -725
- package/dist/index.js.map +3 -3
- package/dist/language-service/index.js.map +1 -1
- package/dist/plugin/evaluator.d.ts +38 -2
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1127 -544
- package/dist/plugin/index.js.map +3 -3
- package/dist/plugin/ir-eval.d.ts +21 -0
- package/dist/plugin/ir-eval.d.ts.map +1 -0
- package/dist/plugin/reducer-resolver.d.ts +6 -0
- package/dist/plugin/reducer-resolver.d.ts.map +1 -1
- package/dist/spec/index.js +555 -6086
- package/dist/spec/index.js.map +3 -3
- package/dist/spec/railroad-lib.d.ts +7 -2
- package/dist/spec/railroad-lib.d.ts.map +1 -1
- package/dist/spec/railroad.d.ts.map +1 -1
- package/dist/table/assemble.d.ts.map +1 -1
- package/dist/table/compile-rule-map.d.ts +11 -4
- package/dist/table/compile-rule-map.d.ts.map +1 -1
- package/dist/table/compile.d.ts +16 -0
- package/dist/table/compile.d.ts.map +1 -1
- package/dist/table/emit-assembly.d.ts.map +1 -1
- package/dist/table/encode-baseline.d.ts.map +1 -1
- package/dist/table/exec.d.ts.map +1 -1
- package/dist/table/index.js +272 -102
- package/dist/table/index.js.map +2 -2
- package/dist/table/ops.d.ts +7 -5
- package/dist/table/ops.d.ts.map +1 -1
- package/dist/table/site-labels.d.ts.map +1 -1
- package/dist/types.d.ts +8 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/analysis/commitment.ts +3 -1
- package/src/analysis/rebuild.ts +1 -1
- package/src/combinators/attempt.ts +26 -3
- package/src/combinators/first-set.ts +5 -1
- package/src/combinators/map.ts +5 -1
- package/src/combinators/node.ts +12 -3
- package/src/combinators/parser.ts +133 -67
- package/src/combinators/ref.ts +10 -1
- package/src/compiler/borrowed-rules.ts +88 -0
- package/src/compiler/compile-linkable-table.ts +1 -1
- package/src/compiler/grammar-coverage-ids.ts +35 -6
- package/src/compiler/ir-serialize.ts +13 -119
- package/src/compiler/linker.ts +190 -666
- package/src/coverage.ts +7 -5
- package/src/cst/reflection.ts +6 -1
- package/src/cst/trivia-kinds.ts +6 -0
- package/src/index.ts +1 -0
- package/src/plugin/evaluator.ts +114 -14
- package/src/plugin/index.ts +588 -110
- package/src/plugin/ir-eval.ts +165 -0
- package/src/plugin/reducer-resolver.ts +20 -3
- package/src/spec/railroad-lib.ts +18 -8
- package/src/spec/railroad.ts +3 -4
- package/src/table/assemble.ts +22 -8
- package/src/table/compile-rule-map.ts +18 -8
- package/src/table/compile.ts +61 -0
- package/src/table/emit-assembly.ts +8 -3
- package/src/table/encode-baseline.ts +5 -1
- package/src/table/encode.ts +13 -13
- package/src/table/exec.ts +6 -2
- package/src/table/ops.ts +7 -5
- package/src/table/site-labels.ts +3 -1
- package/src/types.ts +8 -1
- package/src/version.ts +1 -1
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The macro plugin's IR evaluator: turns a composed grammar's carried IR back
|
|
3
|
+
* into combinators so the build can re-lower it. Build time only — see below.
|
|
4
|
+
*/
|
|
5
|
+
import type { Combinator } from '../types.ts'
|
|
6
|
+
import { rules } from '../combinators/parser.ts'
|
|
7
|
+
import { ref } from '../combinators/ref.ts'
|
|
8
|
+
import { regex } from '../combinators/regex.ts'
|
|
9
|
+
import { literal } from '../combinators/literal.ts'
|
|
10
|
+
import { keywords } from '../combinators/keywords.ts'
|
|
11
|
+
import { sequence } from '../combinators/sequence.ts'
|
|
12
|
+
import { choice } from '../combinators/choice.ts'
|
|
13
|
+
import { dispatch, endsWith, matches, otherwise, routed, startsWith, when } from '../combinators/dispatch.ts'
|
|
14
|
+
import { attempt } from '../combinators/attempt.ts'
|
|
15
|
+
import { many, oneOrMore, optional, sepBy, keepSeparator } from '../combinators/repeat.ts'
|
|
16
|
+
import { not } from '../combinators/not.ts'
|
|
17
|
+
import { peek } from '../combinators/peek.ts'
|
|
18
|
+
import { node } from '../combinators/node.ts'
|
|
19
|
+
import { parser } from '../combinators/grammar.ts'
|
|
20
|
+
import { scanTo, balanced } from '../combinators/scanTo.ts'
|
|
21
|
+
import { token, leaf, sourceLeaf } from '../combinators/token.ts'
|
|
22
|
+
import { classifiedTrivia, transform, trivia, label, field } from '../combinators/map.ts'
|
|
23
|
+
import { expect as expectC } from '../combinators/expect.ts'
|
|
24
|
+
import { withCtx } from '../combinators/withCtx.ts'
|
|
25
|
+
import { adjacent, notAdjacent } from '../combinators/adjacency.ts'
|
|
26
|
+
|
|
27
|
+
type Comb = Combinator<unknown>
|
|
28
|
+
|
|
29
|
+
/** Reconstruct a rule map from serialized IR (the inverse of `serializeRuleMap`) —
|
|
30
|
+
* evaluate the combinator-construction expression with every constructor in scope.
|
|
31
|
+
*
|
|
32
|
+
* BUILD TIME ONLY. It evaluates source text, so it lives in the plugin, which runs
|
|
33
|
+
* in the bundler; no runtime module imports it
|
|
34
|
+
* (docs/design/runtime-and-size-contract.md, rule 1). */
|
|
35
|
+
export function evalRuleMapIR(ir: string): Array<[string, Comb]> {
|
|
36
|
+
return Object.entries(evalRulesIR(ir))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The `rules()` grammar an IR expression builds — the map plus, non-enumerable,
|
|
40
|
+
* the recipe (`RULES_RECIPE`) a composition links it by. Build time only, as above. */
|
|
41
|
+
export function evalRulesIR(ir: string): Record<string, Comb> {
|
|
42
|
+
return evalIR(ir, rules) as Record<string, Comb>
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The inverse of `serializeCombinator`: the same evaluation, with `rules` reading the
|
|
46
|
+
* one `scanSkip` unit back instead of building a grammar around it. Build time only. */
|
|
47
|
+
export function evalCombinatorIR(ir: string): Comb | null {
|
|
48
|
+
const units = evalIR(ir, (opts: { scanSkip?: Comb[] }) => opts.scanSkip) as Comb[] | undefined
|
|
49
|
+
return units?.length === 1 ? units[0]! : null
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function evalIR(ir: string, rulesImpl: unknown): unknown {
|
|
53
|
+
// `_tf`/`_nd` reconstruct a transform/node AND restore its captured callback
|
|
54
|
+
// source (`_def.fnSrc`/`buildSrc`) so re-lowering inlines it statically. The live
|
|
55
|
+
// fn is only needed for interpreted mode; a self-contained transform source is
|
|
56
|
+
// eval'd, a node build (which may reference imported AST classes) is left to its
|
|
57
|
+
// source only. Macro-only validation metadata is carried as plain data: this
|
|
58
|
+
// runtime module never parses callback source or imports a compiler frontend.
|
|
59
|
+
const _tf = (child: Comb, src: string, recognitionOnly = false): Comb => {
|
|
60
|
+
let fn: (...a: unknown[]) => unknown
|
|
61
|
+
// eslint-disable-next-line no-eval
|
|
62
|
+
try { fn = (0, eval)(`(${src})`) } catch { fn = () => { throw new Error('IR transform fn not materialized') } }
|
|
63
|
+
const t = transform(child as never, fn as never)
|
|
64
|
+
;(t._def as { fnSrc?: string }).fnSrc = src
|
|
65
|
+
if (recognitionOnly) (t._def as { recognitionOnly?: boolean }).recognitionOnly = true
|
|
66
|
+
return t as Comb
|
|
67
|
+
}
|
|
68
|
+
const _lf = (child: Comb, src: string, source = false): Comb => {
|
|
69
|
+
let fn: (...a: unknown[]) => unknown
|
|
70
|
+
// eslint-disable-next-line no-eval
|
|
71
|
+
try { fn = (0, eval)(`(${src})`) } catch { fn = () => { throw new Error('IR leaf fn not materialized') } }
|
|
72
|
+
const l = source ? sourceLeaf(child as never, fn as never) : leaf(child as never, fn as never)
|
|
73
|
+
;(l._def as { fnSrc?: string }).fnSrc = src
|
|
74
|
+
return l as Comb
|
|
75
|
+
}
|
|
76
|
+
const _nd = (type: string, child: Comb, src: string, opts?: unknown, staticError?: readonly string[], sigSrc?: string, buildImports?: ReadonlyArray<{ local: string; source: string; imported: string }>, rawUnused?: true): Comb => {
|
|
77
|
+
if (staticError !== undefined && staticError.length > 0) {
|
|
78
|
+
// Fail closed: a builder with an un-rescuable binding is NOT fused. The
|
|
79
|
+
// plugin catches this and leaves the runtime compose() in place rather than
|
|
80
|
+
// shipping a fused table that would ReferenceError on import. Import-rescued
|
|
81
|
+
// free names never reach here — they ride in `buildImports`, not `staticError`.
|
|
82
|
+
throw new Error(`IR direct node builder for ${type} must be macro-static and self-contained; unsupported binding(s): ${staticError.join(', ')}`)
|
|
83
|
+
}
|
|
84
|
+
// A serialized direct builder needs an inert sentinel as well as buildSrc.
|
|
85
|
+
// `node(..., undefined)` is structural, so re-lowering a composed artifact
|
|
86
|
+
// silently routes it through ctx.build/default CST even though the IR still
|
|
87
|
+
// carries the callback source. The compiler is the only consumer that may
|
|
88
|
+
// materialize `buildSrc`; raw IR interpretation deliberately rejects direct
|
|
89
|
+
// builders rather than evaluating arbitrary captured source at runtime.
|
|
90
|
+
const n = node(type, child as never, (() => { throw new Error('IR node build requires static re-lowering') }) as never, opts as never)
|
|
91
|
+
;(n._def as { buildSrc?: string; buildStaticError?: readonly string[] }).buildSrc = src
|
|
92
|
+
if (staticError !== undefined) (n._def as { buildStaticError?: readonly string[] }).buildStaticError = staticError
|
|
93
|
+
// Analysis-only resolved reducer signature (see `buildAnalysisSrc`). Without it a
|
|
94
|
+
// re-lowered composed artifact silently re-acquires the fail-open capture cost the
|
|
95
|
+
// authoring module had already resolved away.
|
|
96
|
+
if (sigSrc !== undefined) (n._def as { buildSigSrc?: string }).buildSigSrc = sigSrc
|
|
97
|
+
if (rawUnused === true) (n._def as { buildRawUnused?: true }).buildRawUnused = true
|
|
98
|
+
// Re-attach the direct-builder import provenance so the plugin's re-lower pass
|
|
99
|
+
// can re-emit the imports into the consuming module. Plain data — this runtime
|
|
100
|
+
// module never resolves or emits imports itself.
|
|
101
|
+
if (buildImports !== undefined) (n._def as { buildImports?: ReadonlyArray<{ local: string; source: string; imported: string }> }).buildImports = buildImports
|
|
102
|
+
return n as Comb
|
|
103
|
+
}
|
|
104
|
+
// `_gch` rebuilds a GATED choice AND restores its `_def.gateSrcs` (parallel to the
|
|
105
|
+
// arms) — the gate mirror of `_tf`/`_nd`. Each item is either a plain arm or a
|
|
106
|
+
// `[gateSource, arm]` tuple: the source is eval'd to the live predicate (interpreted
|
|
107
|
+
// mode) and recorded as the arm's gateSrcs entry so re-lowering INLINES the gate
|
|
108
|
+
// statically (keeping the artifact fusible via `emitFusedSource`). A missing/failed
|
|
109
|
+
// source falls back to a throwing predicate — the same contract as `_tf`.
|
|
110
|
+
const _gch = (items: Array<Comb | [string, Comb]>): Comb => {
|
|
111
|
+
const gateSrcs: (string | null)[] = []
|
|
112
|
+
const arms = items.map(it => {
|
|
113
|
+
if (Array.isArray(it)) {
|
|
114
|
+
const [src, comb] = it
|
|
115
|
+
gateSrcs.push(src)
|
|
116
|
+
let gate: (s: unknown) => boolean
|
|
117
|
+
// eslint-disable-next-line no-eval
|
|
118
|
+
try { gate = (0, eval)(`(${src})`) } catch { gate = () => { throw new Error('IR gate fn not materialized') } }
|
|
119
|
+
return { gate, combinator: comb }
|
|
120
|
+
}
|
|
121
|
+
gateSrcs.push(null)
|
|
122
|
+
return it
|
|
123
|
+
})
|
|
124
|
+
const c = (choice as (...a: unknown[]) => Comb)(...arms)
|
|
125
|
+
// `choice` is the authority on gate alignment; `gateSrcs` must line up 1:1 with
|
|
126
|
+
// the constructed `_def.gates`. They always do on the normal path (both from the
|
|
127
|
+
// same `items.map`), but assert it so a future change in `choice` that coalesces
|
|
128
|
+
// or drops arms can't silently ship a mis-aligned gate-source array.
|
|
129
|
+
const gates = (c._def as { gates?: unknown[] }).gates
|
|
130
|
+
if (gates && gates.length !== gateSrcs.length) {
|
|
131
|
+
throw new Error(`_gch: gateSrcs length ${gateSrcs.length} != choice gates length ${gates.length}`)
|
|
132
|
+
}
|
|
133
|
+
;(c._def as { gateSrcs?: (string | null)[] }).gateSrcs = gateSrcs
|
|
134
|
+
return c
|
|
135
|
+
}
|
|
136
|
+
// `_wc` rebuilds a `withCtx` AND restores its `_def.extraSrc` (the source of the
|
|
137
|
+
// `extra`/state value) — the withCtx mirror of `_tf`/`_nd`/`_gch`. The source is
|
|
138
|
+
// eval'd to the live `extra` value (interpreted mode) and recorded on the def so
|
|
139
|
+
// re-lowering INLINES the state getter statically (`() => (extraSrc)`), keeping the
|
|
140
|
+
// artifact fusible via `emitFusedSource`. A plain `withCtx(value, inner)` would
|
|
141
|
+
// leave `extraSrc` unset → codegen emits a source-less runtime closure (a non-static
|
|
142
|
+
// callback) → `emitFusedSource` fails and a downstream `compose()` silently falls
|
|
143
|
+
// back to a runtime fuse. A missing/failed source falls back to `undefined` state —
|
|
144
|
+
// the same best-effort contract as `_tf`.
|
|
145
|
+
const _wc = (src: string, inner: Comb): Comb => {
|
|
146
|
+
let extra: unknown
|
|
147
|
+
// eslint-disable-next-line no-eval
|
|
148
|
+
try { extra = (0, eval)(`(${src})`) } catch { extra = undefined }
|
|
149
|
+
const w = (withCtx as (e: unknown, c: Comb) => Comb)(extra, inner)
|
|
150
|
+
;(w._def as { extraSrc?: string }).extraSrc = src
|
|
151
|
+
return w
|
|
152
|
+
}
|
|
153
|
+
// eslint-disable-next-line no-new-func
|
|
154
|
+
const fn = new Function(
|
|
155
|
+
'rules', 'ref', 'regex', 'literal', 'keywords', 'sequence', 'choice', 'dispatch', 'when', 'startsWith', 'endsWith', 'matches', 'otherwise', 'routed', 'attempt',
|
|
156
|
+
'many', 'oneOrMore', 'optional', 'sepBy', 'keepSeparator', 'not', 'peek', 'node', 'parser',
|
|
157
|
+
'scanTo', 'balanced', 'token', 'leaf', 'transform', 'trivia', 'classifiedTrivia', 'label', 'field', 'expect', 'adjacent', 'notAdjacent', '_tf', '_lf', '_nd', '_gch', '_wc',
|
|
158
|
+
`return (${ir})`,
|
|
159
|
+
)
|
|
160
|
+
return fn(
|
|
161
|
+
rulesImpl, ref, regex, literal, keywords, sequence, choice, dispatch, when, startsWith, endsWith, matches, otherwise, routed, attempt,
|
|
162
|
+
many, oneOrMore, optional, sepBy, keepSeparator, not, peek, node, parser,
|
|
163
|
+
scanTo, balanced, token, leaf, transform, trivia, classifiedTrivia, label, field, expectC, adjacent, notAdjacent, _tf, _lf, _nd, _gch, _wc,
|
|
164
|
+
)
|
|
165
|
+
}
|
|
@@ -118,8 +118,11 @@ function patternNames(pat: unknown, out: string[]): void {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
function declare(scope: ScopeNode, name: string, b: Binding): void {
|
|
121
|
-
// First declaration wins
|
|
122
|
-
|
|
121
|
+
// First declaration wins the shape, but a second one (`var x = a; … var x = b`, a
|
|
122
|
+
// repeated function) can rebind the name, so it counts as a reassignment.
|
|
123
|
+
const prev = scope.bindings.get(name)
|
|
124
|
+
if (prev) prev.reassigned = true
|
|
125
|
+
else scope.bindings.set(name, b)
|
|
123
126
|
}
|
|
124
127
|
|
|
125
128
|
/** `var` and function declarations hoist to the nearest FUNCTION scope, not the block. */
|
|
@@ -238,7 +241,10 @@ function markReassignments(body: unknown[], root: ScopeNode): void {
|
|
|
238
241
|
if (Array.isArray(n)) { for (const item of n) walk(item); return }
|
|
239
242
|
const node = n as AnyNode
|
|
240
243
|
if (typeof node.type !== 'string') return
|
|
241
|
-
|
|
244
|
+
// `for (x of …)` / `for (x in …)` writes `x` too when its head declares nothing.
|
|
245
|
+
const loopWrite = (node.type === 'ForOfStatement' || node.type === 'ForInStatement')
|
|
246
|
+
&& (node.left as AnyNode | undefined)?.type !== 'VariableDeclaration'
|
|
247
|
+
if (node.type === 'AssignmentExpression' || node.type === 'UpdateExpression' || loopWrite) {
|
|
242
248
|
const target = (node.type === 'UpdateExpression' ? node.argument : node.left) as AnyNode | undefined
|
|
243
249
|
const names: string[] = []
|
|
244
250
|
if (target) patternNames(target, names)
|
|
@@ -255,6 +261,17 @@ function markReassignments(body: unknown[], root: ScopeNode): void {
|
|
|
255
261
|
for (const stmt of body) walk(stmt)
|
|
256
262
|
}
|
|
257
263
|
|
|
264
|
+
/**
|
|
265
|
+
* Whether a top-level binding of a module is never reassigned or redeclared, so a `let` or
|
|
266
|
+
* `var` holds its initializer exactly as a `const` would. Bundlers emit top-level `const`
|
|
267
|
+
* as `var` (esbuild does), which is how a published entry's exports usually arrive.
|
|
268
|
+
*/
|
|
269
|
+
export function topLevelIsStable(body: unknown[], moduleEnd: number): (name: string) => boolean {
|
|
270
|
+
const root = buildScopeTree(body, moduleEnd)
|
|
271
|
+
markReassignments(body, root)
|
|
272
|
+
return name => root.bindings.get(name)?.reassigned !== true
|
|
273
|
+
}
|
|
274
|
+
|
|
258
275
|
// ---------------------------------------------------------------------------
|
|
259
276
|
// Parameter list → arity, from the AST
|
|
260
277
|
// ---------------------------------------------------------------------------
|
package/src/spec/railroad-lib.ts
CHANGED
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
* Vendored copy of tabatkins railroad-diagrams (v1.0.0), license CC0-1.0.
|
|
3
3
|
* https://github.com/tabatkins/railroad-diagrams
|
|
4
4
|
*
|
|
5
|
-
* Public domain (CC0)
|
|
6
|
-
*
|
|
5
|
+
* Public domain (CC0). The library is held ONCE, as real code: `railroadLibrary`
|
|
6
|
+
* renders SVG in Node, and `RAILROAD_JS` embeds the same function's own source in
|
|
7
|
+
* the generated HTML page (no CDN, no runtime dep). Nothing evaluates a string —
|
|
8
|
+
* docs/design/runtime-and-size-contract.md, rule 1.
|
|
7
9
|
* Do not edit by hand; regenerate from the npm package if bumping versions.
|
|
8
10
|
*/
|
|
9
11
|
/* eslint-disable */
|
|
10
|
-
|
|
12
|
+
// @ts-nocheck — vendored third-party JavaScript, kept verbatim.
|
|
13
|
+
|
|
14
|
+
/** Install the railroad-diagrams builders onto `exports`. */
|
|
15
|
+
export function railroadLibrary(exports) {
|
|
16
|
+
/*
|
|
11
17
|
Railroad Diagrams
|
|
12
18
|
by Tab Atkins Jr. (and others)
|
|
13
19
|
http://xanthir.com
|
|
@@ -90,7 +96,7 @@ At runtime, these constants can be found on the Diagram class.
|
|
|
90
96
|
};
|
|
91
97
|
FakeSVG.prototype.escapeString = function(string) {
|
|
92
98
|
// Escape markdown and HTML special characters
|
|
93
|
-
return string.replace(/[*_
|
|
99
|
+
return string.replace(/[*_\`\[\]<&]/g, function(charString) {
|
|
94
100
|
return '&#' + charString.charCodeAt(0) + ';';
|
|
95
101
|
});
|
|
96
102
|
};
|
|
@@ -112,7 +118,7 @@ At runtime, these constants can be found on the Diagram class.
|
|
|
112
118
|
str += ' ' + attr + '="' + (this.attrs[attr]+'').replace(/&/g, '&').replace(/"/g, '"') + '"';
|
|
113
119
|
}
|
|
114
120
|
str += '>';
|
|
115
|
-
if(group) str += "
|
|
121
|
+
if(group) str += "\n";
|
|
116
122
|
if(typeof this.children == 'string') {
|
|
117
123
|
str += FakeSVG.prototype.escapeString(this.children);
|
|
118
124
|
} else {
|
|
@@ -120,7 +126,7 @@ At runtime, these constants can be found on the Diagram class.
|
|
|
120
126
|
str += e;
|
|
121
127
|
});
|
|
122
128
|
}
|
|
123
|
-
str += '</' + this.tagName + '
|
|
129
|
+
str += '</' + this.tagName + '>\n';
|
|
124
130
|
return str;
|
|
125
131
|
}
|
|
126
132
|
|
|
@@ -522,7 +528,7 @@ At runtime, these constants can be found on the Diagram class.
|
|
|
522
528
|
*/
|
|
523
529
|
['Diagram', 'ComplexDiagram', 'Sequence', 'Choice', 'Optional', 'OneOrMore', 'ZeroOrMore', 'Terminal', 'NonTerminal', 'Comment', 'Skip']
|
|
524
530
|
.forEach(function(e,i) { root[e] = temp[i]; });
|
|
525
|
-
}).call(
|
|
531
|
+
}).call(undefined,
|
|
526
532
|
{
|
|
527
533
|
VERTICAL_SEPARATION: 8,
|
|
528
534
|
ARC_RADIUS: 10,
|
|
@@ -531,7 +537,11 @@ At runtime, these constants can be found on the Diagram class.
|
|
|
531
537
|
INTERNAL_ALIGNMENT: 'center',
|
|
532
538
|
}
|
|
533
539
|
);
|
|
534
|
-
|
|
540
|
+
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/** The same library as page script: installs its builders as browser globals. */
|
|
544
|
+
export const RAILROAD_JS: string = `(${railroadLibrary.toString()})(window);`
|
|
535
545
|
|
|
536
546
|
export const RAILROAD_CSS: string = `svg.railroad-diagram {
|
|
537
547
|
background-color: hsl(30,20%,95%);
|
package/src/spec/railroad.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Production, SpecModel, SpecNode } from './model.ts'
|
|
10
10
|
import { renderExpr } from './ebnf.ts'
|
|
11
|
-
import { RAILROAD_CSS, RAILROAD_JS } from './railroad-lib.ts'
|
|
11
|
+
import { RAILROAD_CSS, RAILROAD_JS, railroadLibrary } from './railroad-lib.ts'
|
|
12
12
|
|
|
13
13
|
const j = (s: string): string => JSON.stringify(s)
|
|
14
14
|
|
|
@@ -69,7 +69,7 @@ function toDsl(node: SpecNode): string {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
// Vendored railroad-diagrams builders,
|
|
72
|
+
// Vendored railroad-diagrams builders, installed once. `.toString()` on the
|
|
73
73
|
// result walks an in-memory FakeSVG tree and emits SVG markup with no DOM — so a
|
|
74
74
|
// diagram can be rendered to a static SVG string at build time (Node), not just
|
|
75
75
|
// in the browser. The library exports onto `exports` when it's an object.
|
|
@@ -89,8 +89,7 @@ let _builders: RailroadBuilders | null = null
|
|
|
89
89
|
function builders(): RailroadBuilders {
|
|
90
90
|
if (_builders) return _builders
|
|
91
91
|
const mod = {} as Record<string, unknown>
|
|
92
|
-
|
|
93
|
-
new Function('exports', RAILROAD_JS)(mod)
|
|
92
|
+
railroadLibrary(mod)
|
|
94
93
|
_builders = mod as unknown as RailroadBuilders
|
|
95
94
|
return _builders
|
|
96
95
|
}
|
package/src/table/assemble.ts
CHANGED
|
@@ -523,8 +523,13 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
523
523
|
* exact AST workloads execute the three shapes below 42k/66k/205k times per
|
|
524
524
|
* CSS/Less/generated parse. Keep their bodies scalar and keep every other
|
|
525
525
|
* shape on the generic oracle below.
|
|
526
|
+
*
|
|
527
|
+
* `trailing` is the node's `trailingTrivia` bit: consume the active trivia
|
|
528
|
+
* after a successful body, inside the node's span, exactly where the generic
|
|
529
|
+
* body does. It opens no capture: these shapes are the reducers that read no
|
|
530
|
+
* trivia log.
|
|
526
531
|
*/
|
|
527
|
-
function plainBuildNode(child: Piece, build: NodeBuilder): Piece {
|
|
532
|
+
function plainBuildNode(child: Piece, build: NodeBuilder, trailing: boolean): Piece {
|
|
528
533
|
return (input, pos, ctx) => {
|
|
529
534
|
const sCh = ctx._cstChildren
|
|
530
535
|
const sLv = ctx._cstLeaves
|
|
@@ -542,6 +547,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
542
547
|
ctx.captureTrivia = false
|
|
543
548
|
ctx._fields = undefined
|
|
544
549
|
const value = child(input, pos, ctx)
|
|
550
|
+
if (trailing && value !== FAIL && ctx.trivia !== undefined) EC.e = consumeTrivia(input, EC.e, ctx)
|
|
545
551
|
const kids = buf.ch ?? (buf.single !== undefined ? [buf.single] : EMPTY_CH)
|
|
546
552
|
const rawKids = buf.raw ?? (buf.rawSingle !== undefined ? [buf.rawSingle] : EMPTY_CH)
|
|
547
553
|
ctx._fields = savedFields
|
|
@@ -578,7 +584,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
578
584
|
/** Direct reducer whose declared arity proves `rawChildren` is unobservable.
|
|
579
585
|
* Use the existing split children/leaves collector instead of opening a
|
|
580
586
|
* duplicate raw capture buffer. The body shape is selected while linking. */
|
|
581
|
-
function childrenOnlyBuildNode(child: Piece, build: NodeBuilder): Piece {
|
|
587
|
+
function childrenOnlyBuildNode(child: Piece, build: NodeBuilder, trailing: boolean): Piece {
|
|
582
588
|
return (input, pos, ctx) => {
|
|
583
589
|
const sCh = ctx._cstChildren
|
|
584
590
|
const sLv = ctx._cstLeaves
|
|
@@ -596,6 +602,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
596
602
|
ctx.captureTrivia = false
|
|
597
603
|
ctx._fields = undefined
|
|
598
604
|
const value = child(input, pos, ctx)
|
|
605
|
+
if (trailing && value !== FAIL && ctx.trivia !== undefined) EC.e = consumeTrivia(input, EC.e, ctx)
|
|
599
606
|
const captured = capturedFlatChildren(kids)
|
|
600
607
|
ctx._fields = savedFields
|
|
601
608
|
ctx._cstBuf = sBuf
|
|
@@ -1912,6 +1919,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1912
1919
|
|
|
1913
1920
|
case OP_ATTEMPT: {
|
|
1914
1921
|
const child = link(code[ip + 1]!)
|
|
1922
|
+
const contain = code[ip + 2] === 1
|
|
1915
1923
|
return (input, pos, ctx) => {
|
|
1916
1924
|
const need = markCst(ctx)
|
|
1917
1925
|
const mRaw = MRAW
|
|
@@ -1924,8 +1932,12 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1924
1932
|
const v = child(input, pos, ctx)
|
|
1925
1933
|
if (v !== FAIL) return v
|
|
1926
1934
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1927
|
-
// A committed failure propagates VERBATIM — rolled back, not re-anchored
|
|
1928
|
-
|
|
1935
|
+
// A committed failure propagates VERBATIM — rolled back, not re-anchored —
|
|
1936
|
+
// unless the row contains commitment, where it is reported like any other.
|
|
1937
|
+
if (committed(ctx)) {
|
|
1938
|
+
if (!contain) return FAIL
|
|
1939
|
+
ctx._fc = false
|
|
1940
|
+
}
|
|
1929
1941
|
ctx._fe = pos
|
|
1930
1942
|
return FAIL
|
|
1931
1943
|
}
|
|
@@ -3462,11 +3474,13 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3462
3474
|
// data plus this assembly's fixed option set; direct/custom contexts and
|
|
3463
3475
|
// every richer node shape retain the generic implementation below.
|
|
3464
3476
|
if (!hostCst && !tracked) {
|
|
3465
|
-
|
|
3466
|
-
|
|
3477
|
+
// The trailing-trivia bit (128) rides on the two leanest shapes; it adds
|
|
3478
|
+
// a consume, not a capture.
|
|
3479
|
+
if (build !== undefined && proj < 0 && (flags & ~128) === 0) {
|
|
3480
|
+
return plainBuildNode(child, build, trailingTrivia)
|
|
3467
3481
|
}
|
|
3468
|
-
if (build !== undefined && proj < 0 && flags === 2) {
|
|
3469
|
-
return childrenOnlyBuildNode(child, build)
|
|
3482
|
+
if (build !== undefined && proj < 0 && (flags & ~128) === 2) {
|
|
3483
|
+
return childrenOnlyBuildNode(child, build, trailingTrivia)
|
|
3470
3484
|
}
|
|
3471
3485
|
if (build !== undefined && proj < 0 && flags === 18) {
|
|
3472
3486
|
return childrenOnlyFieldsBuildNode(child, build)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Combinator } from '../types.ts'
|
|
2
2
|
import { hasDirectBuildDef } from '../analysis/commitment.ts'
|
|
3
3
|
import type { HostMode } from '../cst/host-mode.ts'
|
|
4
|
-
import { collectGrammarReflection, type GrammarReflection } from '../cst/reflection.ts'
|
|
4
|
+
import { collectGrammarReflection, GRAMMAR_REFLECTION, type GrammarReflection } from '../cst/reflection.ts'
|
|
5
5
|
import { encodeTableProgram, type TableSettings } from './encode.ts'
|
|
6
6
|
import { defaultAssemblyCfgs, emitTableExpression } from './emit.ts'
|
|
7
7
|
import { tableRules } from './assemble.ts'
|
|
@@ -54,6 +54,12 @@ const DEFAULT_PRECOMPILE_MIN_WORDS = 1_024
|
|
|
54
54
|
export type TableRuleMapOptions = {
|
|
55
55
|
/** Grammar-level ambient trivia, as `rules({ trivia }, …)` declares it. */
|
|
56
56
|
readonly trivia?: Combinator<unknown>
|
|
57
|
+
/**
|
|
58
|
+
* `compileRuleMapRunnable` only: specialise the runnable table once with generated
|
|
59
|
+
* code (falling back to closures under CSP). `compile()` alone passes it — no other
|
|
60
|
+
* runtime path may construct code.
|
|
61
|
+
*/
|
|
62
|
+
readonly specialise?: boolean
|
|
57
63
|
/** Grammar-level ambient scan-skip, as `rules({ scanSkip }, …)` declares it. */
|
|
58
64
|
readonly scanSkip?: Combinator<unknown>[]
|
|
59
65
|
readonly recovery?: boolean
|
|
@@ -120,10 +126,11 @@ export type CompiledRuleMapTable = {
|
|
|
120
126
|
* hands back text and nothing else, because its artifact only exists once the
|
|
121
127
|
* emitted source is evaluated. A table exists as data before it is printed, so
|
|
122
128
|
* the same call that produces the replacement can also hand back the parser.
|
|
123
|
-
* It
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
129
|
+
* It runs the CLOSURE artifact — the engine printed output runs — so building
|
|
130
|
+
* it never constructs code: only `compile()` specialises (`specialise`), per
|
|
131
|
+
* docs/design/runtime-and-size-contract.md, rule 1. That is what makes a
|
|
132
|
+
* differential against the interpreter possible without `eval`, and it is why the
|
|
133
|
+
* table-vs-interpreter test can compare `expected` sets rather than only accept/reject.
|
|
127
134
|
*/
|
|
128
135
|
rules: Record<string, TableRule>
|
|
129
136
|
/** The closure-stamped wire program, for a caller that wants to fold or inspect it. */
|
|
@@ -232,13 +239,16 @@ export function compileRuleMapRunnable(
|
|
|
232
239
|
if (encoded === null) return null
|
|
233
240
|
const { prog, hostMode, plan } = encoded
|
|
234
241
|
const artifact = closureArtifact(prog)
|
|
242
|
+
const reflection = collectGrammarReflection(ruleMap)
|
|
235
243
|
return {
|
|
236
244
|
keys: ruleMap.map(([key]) => key),
|
|
237
245
|
hostMode,
|
|
238
246
|
hostBranchElided: hostMode === 'ast' && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
|
|
239
|
-
reflection
|
|
247
|
+
reflection,
|
|
240
248
|
...(plan === undefined ? {} : { coverageDefinitions: plan.definitions }),
|
|
241
|
-
|
|
249
|
+
// Reflection rides on the map's metadata prototype from birth. Only `compile()`
|
|
250
|
+
// asks to specialise; every other caller runs the closure artifact.
|
|
251
|
+
rules: tableRules(opts.specialise === true ? prog : artifact, { [GRAMMAR_REFLECTION]: reflection }),
|
|
242
252
|
prog: artifact,
|
|
243
253
|
}
|
|
244
254
|
}
|
|
@@ -349,7 +359,7 @@ export function compileRuleMap(
|
|
|
349
359
|
hostBranchElided: hostMode === 'ast' && ruleMap.some(([, rule]) => hasDirectBuildDef(rule)),
|
|
350
360
|
reflection: collectGrammarReflection(ruleMap),
|
|
351
361
|
...(plan === undefined ? {} : { coverageDefinitions: plan.definitions }),
|
|
352
|
-
rules: tableRules(
|
|
362
|
+
rules: tableRules(artifact),
|
|
353
363
|
prog: artifact,
|
|
354
364
|
}
|
|
355
365
|
}
|
package/src/table/compile.ts
CHANGED
|
@@ -8,6 +8,9 @@ import { closureArtifact } from './program.ts'
|
|
|
8
8
|
import { buildGrammarPlan } from '../compiler/grammar-coverage-ids.ts'
|
|
9
9
|
import { runDuplicationDiagnostic, type DuplicationOption } from './duplication-hook.ts'
|
|
10
10
|
import { beginCompileDegradationDrain } from '../compiler/degradation.ts'
|
|
11
|
+
import { compileRuleMapRunnable } from './compile-rule-map.ts'
|
|
12
|
+
import { borrowedRuleMessage, borrowedRuleReference } from '../compiler/borrowed-rules.ts'
|
|
13
|
+
import type { TableRule } from './program.ts'
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
16
|
* `compile()` FOR THE TABLE LOWERING — same contract, different artifact.
|
|
@@ -110,7 +113,65 @@ export type TableCompileOptions = {
|
|
|
110
113
|
readonly fnSources?: readonly string[]
|
|
111
114
|
}
|
|
112
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Compile a whole RULE MAP — a `rules()` grammar or a runtime `compose()` result —
|
|
118
|
+
* into a map of runnable rules that `run()` / `parseDoc()` take like any other.
|
|
119
|
+
*
|
|
120
|
+
* This is how a caller opts a runtime composition into speed: `compose()` only
|
|
121
|
+
* links an interpreter grammar, and `compile()` encodes it to one table and
|
|
122
|
+
* specialises that table once. Where the environment forbids generated code (a
|
|
123
|
+
* Content-Security-Policy without `'unsafe-eval'`) the specialisation throws
|
|
124
|
+
* `EvalError` and the same table runs on the closure assembler instead.
|
|
125
|
+
*/
|
|
126
|
+
export function compile<T>(combinator: Combinator<T>, mapFnSources?: readonly string[], opts?: TableCompileOptions): CompiledParser<T>
|
|
127
|
+
export function compile(grammar: Record<string, unknown> & { readonly _def?: never }, opts?: { readonly hostMode?: HostMode }): Record<string, TableRule>
|
|
113
128
|
export function compile<T>(
|
|
129
|
+
target: Combinator<T> | Record<string, unknown>,
|
|
130
|
+
second?: readonly string[] | { readonly hostMode?: HostMode },
|
|
131
|
+
third?: TableCompileOptions,
|
|
132
|
+
): CompiledParser<T> | Record<string, TableRule> {
|
|
133
|
+
if (!isCombinator(target)) return compileMap(target, second as { readonly hostMode?: HostMode } | undefined)
|
|
134
|
+
return compileRoot(target as Combinator<T>, second as readonly string[] | undefined, third)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function isCombinator(x: unknown): x is Combinator<unknown> {
|
|
138
|
+
return typeof x === 'object' && x !== null
|
|
139
|
+
&& typeof (x as { parse?: unknown }).parse === 'function' && '_def' in x
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function compileMap(grammar: Record<string, unknown>, opts: { readonly hostMode?: HostMode } = {}): Record<string, TableRule> {
|
|
143
|
+
const entries: Array<[string, Combinator<unknown>]> = []
|
|
144
|
+
for (const [key, value] of Object.entries(grammar)) {
|
|
145
|
+
if (!isCombinator(value)) throw new TypeError(`compile: grammar entry "${key}" is not a parser`)
|
|
146
|
+
// A referenced-but-undefined `g.X` is a hole, not a rule: it has nothing to encode.
|
|
147
|
+
if (value._def.tag === 'lazy') {
|
|
148
|
+
try { value._def.thunk() } catch { continue }
|
|
149
|
+
}
|
|
150
|
+
entries.push([key, value])
|
|
151
|
+
}
|
|
152
|
+
// A runtime composition was already checked when it linked; a plain map is checked
|
|
153
|
+
// here, because this encoder binds references by name.
|
|
154
|
+
const borrowed = borrowedRuleReference(Object.fromEntries(entries))
|
|
155
|
+
if (borrowed !== undefined) throw new Error(borrowedRuleMessage('compile', borrowed))
|
|
156
|
+
// One aggregated degradation block per compile, exactly as `compileRoot` drains it.
|
|
157
|
+
const drain = beginCompileDegradationDrain()
|
|
158
|
+
let done = false
|
|
159
|
+
try {
|
|
160
|
+
const refusals: string[] = []
|
|
161
|
+
const compiled = compileRuleMapRunnable(entries, {
|
|
162
|
+
...(opts.hostMode ? { hostMode: opts.hostMode } : {}),
|
|
163
|
+
refusals,
|
|
164
|
+
specialise: true,
|
|
165
|
+
})
|
|
166
|
+
if (compiled === null) {
|
|
167
|
+
throw new Error(`compile: this grammar could not be encoded to a table${refusals.length ? ` — ${refusals.join('; ')}` : ''}`)
|
|
168
|
+
}
|
|
169
|
+
done = true
|
|
170
|
+
return compiled.rules
|
|
171
|
+
} finally { drain(done) }
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function compileRoot<T>(
|
|
114
175
|
combinator: Combinator<T>,
|
|
115
176
|
mapFnSources?: readonly string[],
|
|
116
177
|
opts: TableCompileOptions = {},
|
|
@@ -1552,7 +1552,7 @@ ${clean ? '' : emitMark(p, L.buf, L.raw, sinks)}
|
|
|
1552
1552
|
const v=${child}(input,pos,ctx)
|
|
1553
1553
|
if(v!==FAIL)return v
|
|
1554
1554
|
${clean ? '' : emitRollback(p, L.buf, L.raw, sinks)}
|
|
1555
|
-
if(ctx._fc===true)return FAIL
|
|
1555
|
+
${code[ip + 2] === 1 ? 'ctx._fc=false' : 'if(ctx._fc===true)return FAIL'}
|
|
1556
1556
|
ctx._fe=pos
|
|
1557
1557
|
return FAIL
|
|
1558
1558
|
}`
|
|
@@ -2334,8 +2334,11 @@ return nd
|
|
|
2334
2334
|
// not closure-only semantics. Print the same body for a precompiled
|
|
2335
2335
|
// assembly so a large composeLeaf artifact does not fall back to opening
|
|
2336
2336
|
// the generic raw/trivia buffer that its reducer arity proved unread.
|
|
2337
|
+
// The trailing-trivia bit (128) rides on the children-only shape as it
|
|
2338
|
+
// does in the closure assembler: a consume after the body, no capture.
|
|
2339
|
+
const leanFlags = flags & ~128
|
|
2337
2340
|
if (!hostCst && !tracked && build !== undefined && proj < 0
|
|
2338
|
-
&& (
|
|
2341
|
+
&& (leanFlags === 2 || (flags === 18 || flags === 34))) {
|
|
2339
2342
|
const fields = flags === 18
|
|
2340
2343
|
const collapseChildren = flags === 34
|
|
2341
2344
|
const publish = L.buf && L.raw === RAW_OMIT
|
|
@@ -2382,7 +2385,9 @@ ctx._cstTriviaLog=undefined
|
|
|
2382
2385
|
ctx.captureTrivia=false
|
|
2383
2386
|
ctx._fields=${fields ? '[]' : 'undefined'}
|
|
2384
2387
|
const v=${child}(input,pos,ctx)
|
|
2385
|
-
|
|
2388
|
+
${trailingTrivia && L.tri !== TRI_NONE
|
|
2389
|
+
? `if(v!==FAIL${L.tri === TRI_UNKNOWN ? '&&ctx.trivia!==undefined' : ''})EC.e=consumeTrivia(input,EC.e,ctx)\n`
|
|
2390
|
+
: ''}const captured=_capturedFlatChildren(flat)
|
|
2386
2391
|
${fields ? 'const fieldMap=buildFieldMap(ctx._fields)\n' : ''}ctx._fields=sFields
|
|
2387
2392
|
ctx._cstBuf=sBuf
|
|
2388
2393
|
ctx._cstChildren=sCh
|
|
@@ -295,9 +295,13 @@ class Encoder {
|
|
|
295
295
|
return this.emit(OP_NOT, this.node(d.parser).ip)
|
|
296
296
|
case 'peek':
|
|
297
297
|
return this.emit(OP_PEEK, this.node(d.parser).ip)
|
|
298
|
+
// A contained transaction changes what an enclosing choice does next, so
|
|
299
|
+
// it cannot be transparent.
|
|
300
|
+
case 'attempt':
|
|
301
|
+
if (d.contain === true) throw new UnsupportedConstructBaseline('attempt(contain)')
|
|
302
|
+
return this.node(d.parser).ip
|
|
298
303
|
// Transparent wrappers: no row of their own, no dispatch at run time.
|
|
299
304
|
case 'token':
|
|
300
|
-
case 'attempt':
|
|
301
305
|
case 'label':
|
|
302
306
|
case 'trivia':
|
|
303
307
|
return this.node(d.parser).ip
|
package/src/table/encode.ts
CHANGED
|
@@ -1344,24 +1344,24 @@ class Encoder {
|
|
|
1344
1344
|
// 'cst'` forces them on, exactly as the emitted `cstOut` path does. The
|
|
1345
1345
|
// driver reads a bit; it re-derives nothing and sees no setting.
|
|
1346
1346
|
const cstOut = this.settings.hostMode === 'cst'
|
|
1347
|
-
// THE THREE DERIVED CAPTURE BITS, mirroring `node.ts
|
|
1347
|
+
// THE THREE DERIVED CAPTURE BITS, mirroring `node.ts` term for term:
|
|
1348
1348
|
//
|
|
1349
|
-
// capturesTrivia = captureTrivia
|
|
1349
|
+
// capturesTrivia = captureTrivia
|
|
1350
1350
|
// || (build ? buildReadsTrivia(def) : project === undefined)
|
|
1351
1351
|
//
|
|
1352
1352
|
// `cstOut` is the static stand-in for "a CST host is coming", which the
|
|
1353
1353
|
// interpreter reaches dynamically off `ctx.build`.
|
|
1354
1354
|
//
|
|
1355
|
-
//
|
|
1355
|
+
// `captureTrivia` was once MISSING here and was a real divergence: an
|
|
1356
|
+
// explicit request the arity analysis cannot express (an author can ask
|
|
1357
|
+
// for capture on a 3-argument reducer). This used to REFUSE rather than
|
|
1358
|
+
// lower, so a documented option no grammar could use through the table.
|
|
1356
1359
|
//
|
|
1357
|
-
//
|
|
1358
|
-
//
|
|
1359
|
-
//
|
|
1360
|
-
//
|
|
1361
|
-
//
|
|
1362
|
-
// lands in THIS node's log. The interpreter counts it; the table did
|
|
1363
|
-
// not, so a node with `trailingTrivia` and a non-trivia-reading
|
|
1364
|
-
// reducer captured under the interpreter and dropped under the table.
|
|
1360
|
+
// `trailingTrivia` is NOT a capture term (bit 128 only). It moves the
|
|
1361
|
+
// node's end past the trivia after the body; that trivia reaches the root
|
|
1362
|
+
// table with or without a node log, and only a log the reducer reads is
|
|
1363
|
+
// worth opening. Forcing bit 4 for it put every match of a
|
|
1364
|
+
// non-trivia-reading node on the generic capture path.
|
|
1365
1365
|
//
|
|
1366
1366
|
// The `build ? … : project === undefined` split matters now that a
|
|
1367
1367
|
// STRUCTURAL node lowers: with no builder the interpreter captures unless
|
|
@@ -1374,7 +1374,7 @@ class Encoder {
|
|
|
1374
1374
|
// Field capture additionally requires the body to CONTAIN `field()`
|
|
1375
1375
|
// captures: a node that reads fields but has none allocates nothing.
|
|
1376
1376
|
const wantsFields = parserHasOwnFields(d.parser) && (cstOut || derivedFields)
|
|
1377
|
-
const flags = (cstOut || d.captureTrivia === true ||
|
|
1377
|
+
const flags = (cstOut || d.captureTrivia === true || derivedTrivia ? 4 : 0)
|
|
1378
1378
|
| (!cstOut && omitsRaw ? 2 : 0)
|
|
1379
1379
|
| (cstOut || derivedState ? 8 : 0)
|
|
1380
1380
|
| (wantsFields ? 16 : 0)
|
|
@@ -1596,7 +1596,7 @@ class Encoder {
|
|
|
1596
1596
|
// A TRANSACTION IS A ROW. See `OP_ATTEMPT` for why the transparent
|
|
1597
1597
|
// lowering was correct only for a choice arm.
|
|
1598
1598
|
case 'attempt': {
|
|
1599
|
-
const inner = this.emit(OP_ATTEMPT, this.node(d.parser).ip)
|
|
1599
|
+
const inner = this.emit(OP_ATTEMPT, this.node(d.parser).ip, d.contain === true ? 1 : 0)
|
|
1600
1600
|
if (!this.failureNeedsRollback(d.parser, undefined)) this.failureRollbackCleanSites.add(inner)
|
|
1601
1601
|
// THE FIRST-SET FAIL-FAST GUARD, lowered as the `OP_GATE` row the `node()`
|
|
1602
1602
|
// case already uses — it is the same guard, written twice in the
|