parseman 0.60.4 → 0.60.6
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 +31 -5
- package/dist/analysis/diagnostics.js.map +1 -1
- package/dist/cli/index.js +639 -613
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/parser.d.ts +34 -3
- package/dist/combinators/parser.d.ts.map +1 -1
- package/dist/combinators/trivia-boundary.d.ts +5 -3
- package/dist/combinators/trivia-boundary.d.ts.map +1 -1
- package/dist/compiler/compile-linkable-table.d.ts.map +1 -1
- package/dist/compiler/linker.d.ts +11 -3
- package/dist/compiler/linker.d.ts.map +1 -1
- package/dist/compiler/token-capability.js +6 -0
- package/dist/compiler/token-capability.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +93 -25
- package/dist/index.js.map +1 -1
- package/dist/plugin/evaluator.d.ts +3 -1
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +116 -75
- package/dist/plugin/index.js.map +1 -1
- package/dist/spec/index.js +3 -1
- package/dist/spec/index.js.map +1 -1
- package/dist/spec/model.d.ts.map +1 -1
- package/dist/table/assemble.d.ts.map +1 -1
- package/dist/table/emit-assembly.d.ts.map +1 -1
- package/dist/table/encode.d.ts.map +1 -1
- package/dist/table/exec.d.ts.map +1 -1
- package/dist/table/index.js +31 -5
- package/dist/table/index.js.map +1 -1
- package/dist/table/ops.d.ts +6 -0
- package/dist/table/ops.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/combinators/grammar.ts +1 -1
- package/src/combinators/parser.ts +82 -9
- package/src/combinators/trivia-boundary.ts +11 -3
- package/src/compiler/compile-linkable-table.ts +4 -1
- package/src/compiler/ir-serialize.ts +10 -4
- package/src/compiler/linker.ts +22 -12
- package/src/index.ts +2 -1
- package/src/plugin/evaluator.ts +16 -5
- package/src/plugin/index.ts +27 -104
- package/src/spec/model.ts +3 -1
- package/src/table/assemble.ts +11 -1
- package/src/table/emit-assembly.ts +10 -1
- package/src/table/encode.ts +16 -9
- package/src/table/exec.ts +11 -9
- package/src/table/ops.ts +6 -0
- package/src/table/site-labels.ts +3 -3
- package/src/version.ts +1 -1
package/dist/table/ops.d.ts
CHANGED
|
@@ -210,6 +210,12 @@ export declare const OP_TOKEN = 29;
|
|
|
210
210
|
* is why this restores the saved value rather than writing `false`.
|
|
211
211
|
*/
|
|
212
212
|
export declare const OP_SCOPE_CAP = 30;
|
|
213
|
+
/**
|
|
214
|
+
* `k` of a `SCOPE_CAP` row whose `parser({ captureTrivia: true })` names no trivia.
|
|
215
|
+
* It raises capture and leaves the trivia the scope was entered with in force,
|
|
216
|
+
* where `k = -1` clears it (`trivia: null`).
|
|
217
|
+
*/
|
|
218
|
+
export declare const SCOPE_INHERIT = -2;
|
|
213
219
|
/**
|
|
214
220
|
* `WITHCTX k c` — `withCtx(extra, c)`. `k >= 0` is `extra` in the const pool
|
|
215
221
|
* (the replace form); `k < 0` is the derive form, `~k` indexing the author's
|
package/dist/table/ops.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../../src/table/ops.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ops.d.ts","sourceRoot":"","sources":["../../src/table/ops.ts"],"names":[],"mappings":"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"}
|
package/dist/version.d.ts
CHANGED
|
@@ -13,5 +13,5 @@
|
|
|
13
13
|
* Kept in sync with `package.json` by `test/unit/version-sync.test.ts`, which fails
|
|
14
14
|
* the build if this constant drifts from the published version.
|
|
15
15
|
*/
|
|
16
|
-
export declare const PARSEMAN_VERSION = "0.60.
|
|
16
|
+
export declare const PARSEMAN_VERSION = "0.60.6";
|
|
17
17
|
//# sourceMappingURL=version.d.ts.map
|
package/package.json
CHANGED
|
@@ -676,7 +676,9 @@ export function classifyRuleMap(
|
|
|
676
676
|
let resolved: Combinator<unknown> | undefined
|
|
677
677
|
try { resolved = def.thunk() } catch { resolved = undefined }
|
|
678
678
|
if (resolved === undefined) {
|
|
679
|
-
|
|
679
|
+
// A `prior.X` placeholder is a hole too: the composition fills it by name.
|
|
680
|
+
const hole = p as unknown as { _ruleName?: string; _priorOf?: string }
|
|
681
|
+
if (hole._ruleName !== undefined || hole._priorOf !== undefined) externalRefs.add(p)
|
|
680
682
|
return
|
|
681
683
|
}
|
|
682
684
|
scanExternal(resolved)
|
|
@@ -215,7 +215,7 @@ export function parser<T>(opts: ParserOptions, root: Combinator<T>): ParsemanPar
|
|
|
215
215
|
ctx.triviaKindLabels = undefined
|
|
216
216
|
} else if (scopeTrivia !== undefined) {
|
|
217
217
|
ctx.trivia = scopeTrivia
|
|
218
|
-
|
|
218
|
+
ctx.triviaKindLabels = scopeLabels
|
|
219
219
|
}
|
|
220
220
|
if (forceCaptureTrivia || _ctx?.captureTrivia) ctx.captureTrivia = true
|
|
221
221
|
// Kind-filter for per-node capture. Resolve against this scope's trivia
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Combinator } from '../types.ts'
|
|
2
2
|
import type { HostMode } from '../cst/host-mode.ts'
|
|
3
|
+
import type { Runnable } from '../functional/run.ts'
|
|
4
|
+
import { any } from './first-set.ts'
|
|
3
5
|
import { ref } from './ref.ts'
|
|
4
6
|
import { markUnusedValues } from '../compiler/value-usage.ts'
|
|
5
7
|
import { parser as grammarParser } from './grammar.ts'
|
|
@@ -28,12 +30,20 @@ function isDefinableRef(v: unknown): v is DefinableRef {
|
|
|
28
30
|
&& typeof (v as { define?: unknown }).define === 'function'
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
/** The rule name a `prior.X` placeholder stands for (see `linkRules`), or `undefined`. */
|
|
34
|
+
export function priorNameOf(r: Combinator<unknown>): string | undefined {
|
|
35
|
+
return (r as unknown as { _priorOf?: string })._priorOf
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
function ruleNameOf(r: Combinator<unknown>): string | undefined {
|
|
32
39
|
return (r as unknown as { _ruleName?: string })._ruleName
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
function isNamedRuleRefForAnotherRule(r: Combinator<unknown>, key: string): boolean {
|
|
36
|
-
|
|
43
|
+
if (r._def.tag !== 'lazy') return false
|
|
44
|
+
// A `prior.X` hole stands for an earlier piece's rule, never for this key.
|
|
45
|
+
if (priorNameOf(r) !== undefined) return true
|
|
46
|
+
const name = ruleNameOf(r)
|
|
37
47
|
return name !== undefined && name !== key
|
|
38
48
|
}
|
|
39
49
|
|
|
@@ -157,13 +167,58 @@ type RuleMapNodeTag<T extends Record<string, Combinator<unknown>>> = {
|
|
|
157
167
|
type RulesResult<T extends Record<string, Combinator<unknown>>> =
|
|
158
168
|
T & GrammarWithReflection<RuleMapNodeType<T>, RuleMapNodeTag<T>>
|
|
159
169
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
170
|
+
/**
|
|
171
|
+
* The second parameter of a `rules((g, prior) => …)` factory. `prior.X` is the
|
|
172
|
+
* definition of `X` that the grammars composed BEFORE this one gave it, with a
|
|
173
|
+
* top-level `node()` unwrapped to its body, so a composing grammar can re-reduce,
|
|
174
|
+
* wrap or extend an inherited rule without re-spelling it:
|
|
175
|
+
*
|
|
176
|
+
* rules((g, prior: PriorRules<typeof css>) => ({
|
|
177
|
+
* Value: node('Value', prior.Value, reduceValue),
|
|
178
|
+
* }))
|
|
179
|
+
*
|
|
180
|
+
* Naming the base grammar types `prior.X`, and a misspelled rule fails to compile.
|
|
181
|
+
*
|
|
182
|
+
* Its `g.X` references stay bound to the composition (open recursion). A rule's value
|
|
183
|
+
* is `unknown` here, because unwrapping a `node()` changes it. Unnamed, the parameter
|
|
184
|
+
* answers any string, which `noUncheckedIndexedAccess` reads as possibly undefined.
|
|
185
|
+
* See `docs/guide/extending.md`.
|
|
186
|
+
*/
|
|
187
|
+
export type PriorRules<Base = Record<string, Combinator<unknown>>> =
|
|
188
|
+
[KnownRuleNames<Base>] extends [never]
|
|
189
|
+
? { readonly [name: string]: Combinator<unknown> }
|
|
190
|
+
: { readonly [K in KnownRuleNames<Base>]: Combinator<unknown> }
|
|
191
|
+
|
|
192
|
+
/** The rule names a grammar's type spells out: not its index signature, if it has one. */
|
|
193
|
+
export type KnownRuleNames<Base> = keyof {
|
|
194
|
+
[K in keyof Base as string extends K ? never : number extends K ? never : Base[K] extends Combinator<unknown> | Runnable ? K : never]: unknown
|
|
195
|
+
} & string
|
|
196
|
+
|
|
197
|
+
const priorUnfilled = (key: string): Error =>
|
|
198
|
+
new Error(`compose: prior.${key} is read, but no grammar composed before it defines "${key}"`)
|
|
199
|
+
|
|
200
|
+
/** The placeholder for a `prior.X` no earlier piece has defined yet. Composition
|
|
201
|
+
* replaces it; running it, or analysing what it leads to, reports the name. */
|
|
202
|
+
function priorHole(key: string): Combinator<unknown> {
|
|
203
|
+
const unfilled = priorUnfilled(key)
|
|
204
|
+
const fail = (): never => { throw unfilled }
|
|
205
|
+
return {
|
|
206
|
+
_tag: 'lazy',
|
|
207
|
+
_meta: { firstSet: any(), canMatchNewline: true, isTrivia: false },
|
|
208
|
+
_def: { tag: 'lazy', thunk: fail },
|
|
209
|
+
parse: fail,
|
|
210
|
+
_priorOf: key,
|
|
211
|
+
} as Combinator<unknown>
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export function rules<T extends Record<string, Combinator<unknown>>, P extends object = PriorRules>(factory: (self: any, prior: P) => T): RulesResult<T>
|
|
215
|
+
export function rules<T extends Record<string, Combinator<unknown>>, P extends object = PriorRules>(options: RulesOptions, factory: (self: any, prior: P) => T): RulesResult<T>
|
|
216
|
+
export function rules<T extends Record<string, Combinator<unknown>>, P extends object = PriorRules>(
|
|
217
|
+
a: ((self: any, prior: P) => T) | RulesOptions,
|
|
218
|
+
b?: (self: any, prior: P) => T,
|
|
165
219
|
): RulesResult<T> {
|
|
166
|
-
|
|
220
|
+
// The proxy answers every name, so it satisfies whatever rules `P` names.
|
|
221
|
+
const factory = (typeof a === 'function' ? a : b) as unknown as RulesRecipe['factory']
|
|
167
222
|
const options = (typeof a === 'function' ? b : a) as RulesOptions | undefined
|
|
168
223
|
const recipe: RulesRecipe = { factory, options }
|
|
169
224
|
const map = linkRules([recipe])
|
|
@@ -180,7 +235,7 @@ export function rules<T extends Record<string, Combinator<unknown>>>(
|
|
|
180
235
|
*/
|
|
181
236
|
export const RULES_RECIPE = Symbol.for('parseman.rulesRecipe')
|
|
182
237
|
export type RulesRecipe = {
|
|
183
|
-
readonly factory: (self: Record<string, Combinator<unknown
|
|
238
|
+
readonly factory: (self: Record<string, Combinator<unknown>>, prior: PriorRules) => Record<string, Combinator<unknown>>
|
|
184
239
|
readonly options: RulesOptions | undefined
|
|
185
240
|
}
|
|
186
241
|
|
|
@@ -234,8 +289,26 @@ export function linkRules(
|
|
|
234
289
|
// before the rule's own parser is built — enabling forward references. A later
|
|
235
290
|
// recipe's definition of a name WINS; the name keeps its first position.
|
|
236
291
|
const winners = new Map<string, { parser: Combinator<unknown>; options: RulesOptions | undefined }>()
|
|
292
|
+
// `prior.X`: the winner for X BEFORE the running recipe, read while that recipe's
|
|
293
|
+
// factory runs (its own definitions join `winners` only after it returns). It is
|
|
294
|
+
// the earlier definition itself, inlined into the new rule's graph, so it costs no
|
|
295
|
+
// hop at parse time. A grammar not yet composed has no earlier piece: `prior.X` is
|
|
296
|
+
// then a placeholder the serializer writes back as `prior.X`, and a composition
|
|
297
|
+
// that still has no earlier X refuses it.
|
|
298
|
+
const priorHoles = new Map<string, Combinator<unknown>>()
|
|
299
|
+
const prior = new Proxy<PriorRules>({}, {
|
|
300
|
+
get(_target, key) {
|
|
301
|
+
if (typeof key !== 'string') return undefined
|
|
302
|
+
const earlier = winners.get(key)?.parser
|
|
303
|
+
if (earlier !== undefined) return earlier._def.tag === 'node' ? earlier._def.parser : earlier
|
|
304
|
+
if (link !== undefined) throw priorUnfilled(key)
|
|
305
|
+
let hole = priorHoles.get(key)
|
|
306
|
+
if (hole === undefined) priorHoles.set(key, hole = priorHole(key))
|
|
307
|
+
return hole
|
|
308
|
+
},
|
|
309
|
+
})
|
|
237
310
|
for (const recipe of recipes) {
|
|
238
|
-
const definitions = recipe.factory(proxy)
|
|
311
|
+
const definitions = recipe.factory(proxy, prior)
|
|
239
312
|
for (const key of Object.keys(definitions)) {
|
|
240
313
|
winners.set(key, { parser: definitions[key]!, options: recipe.options })
|
|
241
314
|
}
|
|
@@ -20,9 +20,11 @@ const MAX_DEPTH = 12
|
|
|
20
20
|
*
|
|
21
21
|
* `lazy` is deliberately false and is never followed: a reference is the
|
|
22
22
|
* delegation this question is about, so following it would both loop and answer
|
|
23
|
-
* for a different rule. `grammar`
|
|
24
|
-
* an outer install never reaches its body
|
|
25
|
-
*
|
|
23
|
+
* for a different rule. A `grammar` that names or clears trivia declares its own
|
|
24
|
+
* scope, so an outer install never reaches its body and the answer is false; one
|
|
25
|
+
* that does not (`parser({ trackLines: true }, …)`, which `rules({ trackLines })`
|
|
26
|
+
* wraps every rule in) inherits, so it is transparent. `dispatch` is false because
|
|
27
|
+
* it is a ROUTE: its arms re-enter from the routed token and spell their own padding.
|
|
26
28
|
*
|
|
27
29
|
* Structural and pure — callers resolve it ONCE, not per parse.
|
|
28
30
|
*/
|
|
@@ -35,6 +37,12 @@ export function hasOwnTriviaBoundary(p: Combinator<unknown>, depth: number = MAX
|
|
|
35
37
|
// Multi-term forms: the gaps BETWEEN terms are the ambient-trivia positions.
|
|
36
38
|
case 'sequence': return d.parsers.length > 1
|
|
37
39
|
case 'many': case 'oneOrMore': case 'sepBy': return true
|
|
40
|
+
// A scope that names or clears trivia keeps an outer install out of its body.
|
|
41
|
+
// Any other inherits, so it falls through at no cost in depth: line tracking
|
|
42
|
+
// wraps every rule in one, and must not change what this walk sees.
|
|
43
|
+
case 'grammar':
|
|
44
|
+
if (d.triviaParser || d.clearTrivia) return false
|
|
45
|
+
depth++
|
|
38
46
|
// Transparent wrappers — they add no position, so ask what they wrap.
|
|
39
47
|
case 'node': case 'transform': case 'leaf': case 'label': case 'field':
|
|
40
48
|
case 'token': case 'attempt': case 'optional': case 'expect': case 'withCtx':
|
|
@@ -4,6 +4,7 @@ import { childrenOf } from '../analysis/gating.ts'
|
|
|
4
4
|
import { collectGrammarReflection, type GrammarReflection } from '../cst/reflection.ts'
|
|
5
5
|
import { classifyRuleMap } from '../analysis/commitment.ts'
|
|
6
6
|
import { serializeRuleMap } from './ir-serialize.ts'
|
|
7
|
+
import { priorNameOf } from '../combinators/parser.ts'
|
|
7
8
|
import { compileRuleMap, compileRuleMapRunnable, type TableRuleMapOptions } from '../table/compile-rule-map.ts'
|
|
8
9
|
import type { TableProgram, TableRule } from '../table/program.ts'
|
|
9
10
|
import { PARSEMAN_VERSION } from '../version.ts'
|
|
@@ -132,7 +133,9 @@ function externalNames(
|
|
|
132
133
|
let resolved: Combinator<unknown> | undefined
|
|
133
134
|
try { resolved = d.thunk() } catch { resolved = undefined }
|
|
134
135
|
if (resolved === undefined) {
|
|
135
|
-
|
|
136
|
+
// A `prior.X` the composition fills from an earlier piece is a hole too.
|
|
137
|
+
const prior = priorNameOf(p)
|
|
138
|
+
const name = prior === undefined ? (p as unknown as { _ruleName?: string })._ruleName : `prior.${prior}`
|
|
136
139
|
if (name !== undefined && !out.includes(name)) out.push(name)
|
|
137
140
|
return
|
|
138
141
|
}
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* source, or an unsupported construct) — the caller then keeps the lowered source.
|
|
19
19
|
*/
|
|
20
20
|
import type { Combinator, DispatchMatcherCase, ParserDef } from '../types.ts'
|
|
21
|
-
import { rules } from '../combinators/parser.ts'
|
|
21
|
+
import { priorNameOf, rules } from '../combinators/parser.ts'
|
|
22
22
|
import { ref } from '../combinators/ref.ts'
|
|
23
23
|
import { regex } from '../combinators/regex.ts'
|
|
24
24
|
import { literal } from '../combinators/literal.ts'
|
|
@@ -191,6 +191,8 @@ class Serializer {
|
|
|
191
191
|
private localRuleNames = new Map<Comb, string>()
|
|
192
192
|
private ruleMap: ReadonlyArray<readonly [string, Comb]>
|
|
193
193
|
private scanSkip: readonly Comb[]
|
|
194
|
+
/** Whether any rule reads a `prior.X` hole, so the factory takes `(g, prior)`. */
|
|
195
|
+
private usesPrior = false
|
|
194
196
|
|
|
195
197
|
private accessed: readonly string[]
|
|
196
198
|
private referenced = new Set<string>()
|
|
@@ -214,7 +216,7 @@ class Serializer {
|
|
|
214
216
|
if (seen.has(c)) return
|
|
215
217
|
seen.add(c)
|
|
216
218
|
if (c._def.tag === 'lazy') {
|
|
217
|
-
if (ruleNameOf(c) !== undefined) throw new Unserializable('scanSkip unit references a grammar rule')
|
|
219
|
+
if (ruleNameOf(c) !== undefined || priorNameOf(c) !== undefined) throw new Unserializable('scanSkip unit references a grammar rule')
|
|
218
220
|
const target = lazyTarget(c)
|
|
219
221
|
if (target) this.assertNoRuleRef(target, seen)
|
|
220
222
|
return
|
|
@@ -307,9 +309,10 @@ class Serializer {
|
|
|
307
309
|
}
|
|
308
310
|
// Shared consts go INSIDE the factory: they can reference `g[name]` rule refs,
|
|
309
311
|
// and `g` only exists in the factory scope.
|
|
312
|
+
const params = this.usesPrior ? '(g, prior)' : '(g)'
|
|
310
313
|
const factory = innerDecls.length === 0
|
|
311
|
-
?
|
|
312
|
-
:
|
|
314
|
+
? `${params} => ({\n${body}\n})`
|
|
315
|
+
: `${params} => {\n${innerDecls.map(d => ' ' + d).join('\n')}\n return ({\n${body}\n})\n}`
|
|
313
316
|
const call = `rules(${optionsArg}${factory})`
|
|
314
317
|
return outerDecls.length === 0
|
|
315
318
|
? call
|
|
@@ -321,6 +324,7 @@ class Serializer {
|
|
|
321
324
|
private analyze(c: Comb, active: Set<Comb>): void {
|
|
322
325
|
const def = c._def
|
|
323
326
|
if (def.tag === 'lazy') {
|
|
327
|
+
if (priorNameOf(c) !== undefined) { this.usesPrior = true; return } // resolved by prior[name]
|
|
324
328
|
if (ruleNameOf(c) !== undefined) return // named rule ref — resolved by g[name]
|
|
325
329
|
const target = lazyTarget(c)
|
|
326
330
|
if (!target) throw new Unserializable('unresolved unnamed ref')
|
|
@@ -422,6 +426,8 @@ class Serializer {
|
|
|
422
426
|
}
|
|
423
427
|
switch (def.tag) {
|
|
424
428
|
case 'lazy': {
|
|
429
|
+
const prior = priorNameOf(c)
|
|
430
|
+
if (prior !== undefined) return `prior[${JSON.stringify(prior)}]`
|
|
425
431
|
const name = this.localRuleNames.get(c) ?? ruleNameOf(c)
|
|
426
432
|
if (name !== undefined) { this.referenced.add(name); return `g[${JSON.stringify(name)}]` }
|
|
427
433
|
const target = lazyTarget(c)!
|
package/src/compiler/linker.ts
CHANGED
|
@@ -16,7 +16,7 @@ import { borrowedRuleMessage, borrowedRuleReference } from './borrowed-rules.ts'
|
|
|
16
16
|
import { FUSED_HOST_MODE, FUSED_HOST_ELIDED, type HostMode } from '../cst/host-mode.ts'
|
|
17
17
|
import { compileLinkableTable, type LinkableTable } from './compile-linkable-table.ts'
|
|
18
18
|
import { GRAMMAR_REFLECTION } from '../cst/reflection.ts'
|
|
19
|
-
import { linkRules, RULE_ORDER, RULES_RECIPE, type LinkOptions, type RulesRecipe } from '../combinators/parser.ts'
|
|
19
|
+
import { linkRules, RULE_ORDER, RULES_RECIPE, type KnownRuleNames, type LinkOptions, type RulesRecipe } from '../combinators/parser.ts'
|
|
20
20
|
import type { BuildHost, Combinator, CstCollapsePredicate, ParseContext, ParseResult } from '../types.ts'
|
|
21
21
|
import type { Runnable } from '../functional/run.ts'
|
|
22
22
|
import { PlainNode, PlainTaggedNode } from '../cst/plain.js'
|
|
@@ -342,7 +342,7 @@ function recipesOf(item: LinkableTable | Record<string, unknown>): readonly Rule
|
|
|
342
342
|
* `rules({ trivia }, …)` map's own, or a runtime composition's composing trivia.
|
|
343
343
|
* Outermost wins over every linked rule, inherited ones included; `parser` /
|
|
344
344
|
* `noTrivia` still override locally. */
|
|
345
|
-
function composingTriviaOf(items:
|
|
345
|
+
function composingTriviaOf(items: ReadonlyArray<LinkableTable | Record<string, unknown>>): Combinator<unknown> | undefined {
|
|
346
346
|
for (let i = items.length - 1; i >= 0; i--) {
|
|
347
347
|
const item = items[i] as Record<symbol, unknown>
|
|
348
348
|
if (item[LINKED] !== undefined) {
|
|
@@ -359,7 +359,7 @@ function composingTriviaOf(items: Array<LinkableTable | Record<string, unknown>>
|
|
|
359
359
|
|
|
360
360
|
/** The rule names a linked composition defines, in first-declaration order —
|
|
361
361
|
* known WITHOUT linking, from each item's own declaration order. */
|
|
362
|
-
function declaredNamesOf(items:
|
|
362
|
+
function declaredNamesOf(items: ReadonlyArray<LinkableTable | Record<string, unknown>>): string[] {
|
|
363
363
|
const names = new Set<string>()
|
|
364
364
|
for (const item of items) {
|
|
365
365
|
const linked = (item as Record<symbol, unknown>)[LINKED] !== undefined
|
|
@@ -419,6 +419,16 @@ function linkComposition(
|
|
|
419
419
|
return map
|
|
420
420
|
}
|
|
421
421
|
|
|
422
|
+
/** The rules each item spells out. An item that spells out none, such as a plain
|
|
423
|
+
* `Record<string, …>`, could hold anything, so the whole result stays open. */
|
|
424
|
+
type ItemRuleNames<Item> = Item extends unknown
|
|
425
|
+
? [KnownRuleNames<Item>] extends [never] ? string : KnownRuleNames<Item>
|
|
426
|
+
: never
|
|
427
|
+
|
|
428
|
+
/** A composition's rules: every name its items spell out, plus lookup by any string. */
|
|
429
|
+
export type ComposedRules<Items extends readonly unknown[]> =
|
|
430
|
+
Record<string, Runnable> & { [K in ItemRuleNames<Items[number]>]: Runnable }
|
|
431
|
+
|
|
422
432
|
/**
|
|
423
433
|
* Compose grammars into ONE parser map — the only public composition entry point.
|
|
424
434
|
* `compose([base, ext, …])`: a later entry's rule WINS by name, and because every
|
|
@@ -435,23 +445,23 @@ function linkComposition(
|
|
|
435
445
|
* costs a walk of the items' declared names. For speed, `compile()` a rule of it;
|
|
436
446
|
* `compile()` specialises when the environment allows and falls back under CSP.
|
|
437
447
|
*/
|
|
438
|
-
export function compose(
|
|
439
|
-
items:
|
|
448
|
+
export function compose<const Items extends ReadonlyArray<LinkableTable | Record<string, unknown>>>(
|
|
449
|
+
items: Items,
|
|
440
450
|
/**
|
|
441
451
|
* Host mode for the composed grammar, same meaning as `compile(g, { hostMode })`.
|
|
442
452
|
* The interpreter routes by host at parse time; this records `'cst'` on every rule
|
|
443
453
|
* so `run()` can refuse a mismatched host instead of building the wrong tree.
|
|
444
454
|
*/
|
|
445
455
|
opts?: { hostMode?: HostMode },
|
|
446
|
-
):
|
|
456
|
+
): ComposedRules<Items> {
|
|
447
457
|
if (items.some(item => (item as Record<symbol, unknown>)[LEAF_COMPOSED] === true)) {
|
|
448
458
|
throw new Error('compose: a composeLeaf() result is terminal and cannot be composed again')
|
|
449
459
|
}
|
|
450
|
-
return linkedMap(items, opts, false)
|
|
460
|
+
return linkedMap(items, opts, false) as ComposedRules<Items>
|
|
451
461
|
}
|
|
452
462
|
|
|
453
463
|
function linkedMap(
|
|
454
|
-
items:
|
|
464
|
+
items: ReadonlyArray<LinkableTable | Record<string, unknown>>,
|
|
455
465
|
opts: { hostMode?: HostMode } | undefined,
|
|
456
466
|
leaf: boolean,
|
|
457
467
|
): Record<string, Runnable> {
|
|
@@ -485,10 +495,10 @@ function linkedMap(
|
|
|
485
495
|
* At runtime it is the same lazy link as `compose()`, marked terminal. The bench and
|
|
486
496
|
* differential harnesses import un-macro'd grammar modules and run this path.
|
|
487
497
|
*/
|
|
488
|
-
export function composeLeaf(
|
|
489
|
-
items:
|
|
490
|
-
):
|
|
491
|
-
return linkedMap(items, undefined, true)
|
|
498
|
+
export function composeLeaf<const Items extends ReadonlyArray<LinkableTable | Record<string, unknown>>>(
|
|
499
|
+
items: Items,
|
|
500
|
+
): ComposedRules<Items> {
|
|
501
|
+
return linkedMap(items, undefined, true) as ComposedRules<Items>
|
|
492
502
|
}
|
|
493
503
|
|
|
494
504
|
/** Whether `map` is a runtime composition (a combinator map) rather than a
|
package/src/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ export type { AttemptOptions } from './combinators/attempt.ts'
|
|
|
23
23
|
export { many, oneOrMore, optional, sepBy, oneOrMoreSep, keepSeparator } from './combinators/repeat.ts'
|
|
24
24
|
export type { RepeatOptions, SepByOptions, TrailingSeparator, KeptSeparator } from './combinators/repeat.ts'
|
|
25
25
|
export { rules } from './combinators/parser.ts'
|
|
26
|
+
export type { PriorRules } from './combinators/parser.ts'
|
|
26
27
|
export { ref } from './combinators/ref.ts'
|
|
27
28
|
export { not } from './combinators/not.ts'
|
|
28
29
|
export { peek } from './combinators/peek.ts'
|
|
@@ -140,7 +141,7 @@ export type {
|
|
|
140
141
|
// back to `FusedRule`, `isInterpretedFuse` would have to be re-exported in the same
|
|
141
142
|
// change — a dual shape with no way to detect it is the worse of the two.
|
|
142
143
|
export { compose, composeLeaf, cstBuildHost } from './compiler/linker.ts'
|
|
143
|
-
export type { CstBuildHostOptions, FusedRule } from './compiler/linker.ts'
|
|
144
|
+
export type { ComposedRules, CstBuildHostOptions, FusedRule } from './compiler/linker.ts'
|
|
144
145
|
|
|
145
146
|
export { buildLineIndex, createLineIndex, recordLineRange, normalizeLineIndex, offsetToLineCol, annotateSpan, annotateTreeSpans } from './line-index.ts'
|
|
146
147
|
export type { LineIndex } from './line-index.ts'
|
package/src/plugin/evaluator.ts
CHANGED
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
import type { Combinator } from '../types.ts'
|
|
17
17
|
import type { DispatchArm } from '../combinators/dispatch.ts'
|
|
18
18
|
import { ref } from '../combinators/ref.ts'
|
|
19
|
-
import { rules, type RulesOptions } from '../combinators/parser.ts'
|
|
19
|
+
import { rules, RULES_RECIPE, type PriorRules, type RulesOptions, type RulesRecipe } from '../combinators/parser.ts'
|
|
20
20
|
import * as parseman from '../index.ts'
|
|
21
21
|
import { confirmedBuildParamUnused } from '../compiler/build-arity.ts'
|
|
22
22
|
import { directBuilderBindings } from './direct-builder-static.ts'
|
|
@@ -1478,11 +1478,17 @@ export function evaluateParserFactory(
|
|
|
1478
1478
|
|
|
1479
1479
|
const factory = factoryNode as unknown as ArrowFunctionExpression | OxcFunction
|
|
1480
1480
|
const params = factory.params
|
|
1481
|
-
if (params.length !== 1) return null
|
|
1482
|
-
const param = params[0] as unknown as { type: string; name?: string }
|
|
1481
|
+
if (params.length !== 1 && params.length !== 2) return null
|
|
1483
1482
|
// FormalParameter is { decorators? } & BindingPattern — BindingIdentifier has type "Identifier"
|
|
1484
|
-
const
|
|
1483
|
+
const paramName = (param: unknown): string | null => {
|
|
1484
|
+
const p = param as { type: string; name?: string }
|
|
1485
|
+
return p.type === 'Identifier' ? p.name ?? null : null
|
|
1486
|
+
}
|
|
1487
|
+
const proxyName = paramName(params[0])
|
|
1485
1488
|
if (!proxyName) return null
|
|
1489
|
+
// `(g, prior)`: the second parameter is the `prior` handle (`PriorRules`).
|
|
1490
|
+
const priorName = params.length === 2 ? paramName(params[1]) : undefined
|
|
1491
|
+
if (priorName === null) return null
|
|
1486
1492
|
|
|
1487
1493
|
const body = factory.body
|
|
1488
1494
|
if (!body) return null
|
|
@@ -1566,11 +1572,12 @@ export function evaluateParserFactory(
|
|
|
1566
1572
|
const ABORT = Symbol('parseman: factory not statically evaluable')
|
|
1567
1573
|
let built: Record<string, Combinator<unknown>>
|
|
1568
1574
|
try {
|
|
1569
|
-
built = rules(options ?? {}, (g: Record<string, Combinator<unknown
|
|
1575
|
+
built = rules(options ?? {}, (g: Record<string, Combinator<unknown>>, prior: PriorRules) => {
|
|
1570
1576
|
// Outer ScopeEntry values carry their mfSrcs and are replayed by scopeGet()
|
|
1571
1577
|
// when body statements or return expressions reference them.
|
|
1572
1578
|
const localScope: XScope = scope instanceof ModuleScope ? scope.fork() : new Map(scope as XScope)
|
|
1573
1579
|
localScope.set(proxyName, g)
|
|
1580
|
+
if (priorName !== undefined) localScope.set(priorName, prior)
|
|
1574
1581
|
|
|
1575
1582
|
// ── Phase 1: the factory's own body statements ────────────────────────
|
|
1576
1583
|
if (!evalBodyStatements(statements, localScope, code, out)) throw ABORT
|
|
@@ -1615,9 +1622,13 @@ export function evaluateParserFactory(
|
|
|
1615
1622
|
for (const key of keys) map.set(key, built[key]!)
|
|
1616
1623
|
const declared = new Set(keys)
|
|
1617
1624
|
externalAccesses.set(map, Object.keys(built).filter(name => !declared.has(name)))
|
|
1625
|
+
ruleMapRecipes.set(map, (built as unknown as Record<symbol, readonly RulesRecipe[]>)[RULES_RECIPE]!)
|
|
1618
1626
|
return map
|
|
1619
1627
|
}
|
|
1620
1628
|
|
|
1629
|
+
/** The recipe an evaluated rule map came from, for a composition that links it. */
|
|
1630
|
+
export const ruleMapRecipes = new WeakMap<ReadonlyMap<string, Combinator<unknown>>, readonly RulesRecipe[]>()
|
|
1631
|
+
|
|
1621
1632
|
/**
|
|
1622
1633
|
* Every EXTERNAL name an evaluated factory read through `g`, keyed by the map
|
|
1623
1634
|
* `evaluateParserFactory` returned. The map holds declared rules only, so a `g.X`
|