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/src/plugin/index.ts
CHANGED
|
@@ -24,7 +24,7 @@ import { createUnplugin } from 'unplugin'
|
|
|
24
24
|
import { parseSync } from 'oxc-parser'
|
|
25
25
|
import { ResolverFactory } from 'oxc-resolver'
|
|
26
26
|
import MagicString from 'magic-string'
|
|
27
|
-
import { evaluateExpr, evaluateCombinatorArray, evaluateParserFactory, externalAccesses, evaluateStaticValue, evaluateWordFactory, evaluateWhenFactory, evaluateRefDeclaration, applyDefineStatement, referencesAny, setReducerResolver, setBuilderImportResolver, propName, ModuleScope, type FreeNameResolution, type Scope, type ScopeEntry } from './evaluator.ts'
|
|
27
|
+
import { evaluateExpr, evaluateCombinatorArray, evaluateParserFactory, externalAccesses, ruleMapRecipes, evaluateStaticValue, evaluateWordFactory, evaluateWhenFactory, evaluateRefDeclaration, applyDefineStatement, referencesAny, setReducerResolver, setBuilderImportResolver, propName, ModuleScope, type FreeNameResolution, type Scope, type ScopeEntry } from './evaluator.ts'
|
|
28
28
|
import { classifyRuleMap } from '../analysis/commitment.ts'
|
|
29
29
|
import { compile } from '../table/compile.ts'
|
|
30
30
|
import { compileRuleMap, type CompiledRuleMapTable } from '../table/compile-rule-map.ts'
|
|
@@ -223,12 +223,6 @@ const DEFAULT_ASSEMBLY_BUDGET = 0.25
|
|
|
223
223
|
/** A build failure the lowering must not soften into a warning and a fallback. */
|
|
224
224
|
class LinkRefusal extends Error {}
|
|
225
225
|
|
|
226
|
-
/** Whether a rule-map entry has a definition (a `g.X` merely read does not). */
|
|
227
|
-
function isResolved(rule: Combinator<unknown>): boolean {
|
|
228
|
-
if (rule._def.tag !== 'lazy') return true
|
|
229
|
-
try { rule._def.thunk(); return true } catch { return false }
|
|
230
|
-
}
|
|
231
|
-
|
|
232
226
|
function reasonSuffix(runtimeOnly: readonly string[] | undefined): string {
|
|
233
227
|
return runtimeOnly === undefined || runtimeOnly.length === 0
|
|
234
228
|
? ''
|
|
@@ -2061,7 +2055,7 @@ function transformMacroImpl(
|
|
|
2061
2055
|
/**
|
|
2062
2056
|
* Evaluate a carried list back to combinators, one entry per piece: its rule map
|
|
2063
2057
|
* and the `rules()` recipe it was built from. `compose()` links the recipes into
|
|
2064
|
-
* one namespace (`mergedCarriedRules`)
|
|
2058
|
+
* one namespace (`mergedCarriedRules`), and `composeLeaf()` does the same.
|
|
2065
2059
|
* Either way the merged grammar is encoded ONCE by `compileRuleMap` — no relocation
|
|
2066
2060
|
* of code offsets and no merging of const / fn / expected pools between two
|
|
2067
2061
|
* already-encoded programs.
|
|
@@ -2097,38 +2091,6 @@ function transformMacroImpl(
|
|
|
2097
2091
|
return { pieces, trackLines }
|
|
2098
2092
|
}
|
|
2099
2093
|
|
|
2100
|
-
/**
|
|
2101
|
-
* Fold ordered rule maps into the composed map: a later piece's name WINS, and
|
|
2102
|
-
* `Map` keeps each name at its first-sighting position so the encoded rule order
|
|
2103
|
-
* tracks the source lowering's rather than drifting per compose.
|
|
2104
|
-
*
|
|
2105
|
-
* A REFERENCE IS NOT A DEFINITION. A `rules(g => …)` cache also holds every `g.X`
|
|
2106
|
-
* that was merely ACCESSED, so a delta that calls `g.Pair` carries a `Pair` entry
|
|
2107
|
-
* that is an unresolved named lazy. Merged in name order that entry lands LAST and
|
|
2108
|
-
* shadows the base piece which actually defines `Pair` — the encoder then finds a
|
|
2109
|
-
* hole where the winner should be and refuses the whole grammar with "ref() used
|
|
2110
|
-
* before .define()". The same filter guards `compileLinkableTable`'s `isLocal`
|
|
2111
|
-
* (`compile-linkable-table.ts`) and `recoverComposedRules`; this site needs it
|
|
2112
|
-
* for the same reason, and skipping it is why cross-piece composition looked blocked.
|
|
2113
|
-
*
|
|
2114
|
-
* The reference itself is not lost: it stays inside the delta's own rule bodies and
|
|
2115
|
-
* `enc.winners` binds it BY NAME to whichever piece supplies the definition.
|
|
2116
|
-
*/
|
|
2117
|
-
const mergeRuleMaps = (
|
|
2118
|
-
maps: ReadonlyArray<ReadonlyArray<readonly [string, Combinator<unknown>]>>,
|
|
2119
|
-
): Array<[string, Combinator<unknown>]> => {
|
|
2120
|
-
const winners = new Map<string, Combinator<unknown>>()
|
|
2121
|
-
for (const map of maps) {
|
|
2122
|
-
for (const [name, rule] of map) {
|
|
2123
|
-
if (rule._def.tag === 'lazy') {
|
|
2124
|
-
try { rule._def.thunk() } catch { continue }
|
|
2125
|
-
}
|
|
2126
|
-
winners.set(name, rule)
|
|
2127
|
-
}
|
|
2128
|
-
}
|
|
2129
|
-
return [...winners]
|
|
2130
|
-
}
|
|
2131
|
-
|
|
2132
2094
|
/**
|
|
2133
2095
|
* LINK a carried list into the composed map, exactly as runtime `compose()` does:
|
|
2134
2096
|
* every piece's carried `rules()` recipe runs again against ONE namespace
|
|
@@ -2156,25 +2118,6 @@ function transformMacroImpl(
|
|
|
2156
2118
|
return { rules: Object.entries(linked), trackLines: carriedMaps.trackLines, dangling }
|
|
2157
2119
|
}
|
|
2158
2120
|
|
|
2159
|
-
/**
|
|
2160
|
-
* COMPOSING-WINS for `composeLeaf()`'s carried pieces, as an OVERRIDE rather than
|
|
2161
|
-
* a gap-fill: `compileRuleMap`'s `applyAmbient` only fills a rule that carries no
|
|
2162
|
-
* trivia of its own. (`compose()` gets the same override from `linkRules`.)
|
|
2163
|
-
*
|
|
2164
|
-
* Safe to mutate: every rule here came from `evalRuleMapIR`, which constructs FRESH
|
|
2165
|
-
* combinators per piece per compose, so this cannot leak into another compose of the
|
|
2166
|
-
* same base grammar.
|
|
2167
|
-
*/
|
|
2168
|
-
const applyComposingTrivia = (
|
|
2169
|
-
ruleMap: ReadonlyArray<readonly [string, Combinator<unknown>]>,
|
|
2170
|
-
composing: Combinator<unknown>,
|
|
2171
|
-
): void => {
|
|
2172
|
-
for (const [, rule] of ruleMap) {
|
|
2173
|
-
if (rule._meta.isTrivia) continue
|
|
2174
|
-
;(rule._meta as { grammarTrivia?: Combinator<unknown> }).grammarTrivia = composing
|
|
2175
|
-
}
|
|
2176
|
-
}
|
|
2177
|
-
|
|
2178
2121
|
/** Resolve one `compose([...])` argument to its RE-LOWERABLE carried items (IR
|
|
2179
2122
|
* pieces / import spreads / baked-pieces fallback). The composing grammar's trivia
|
|
2180
2123
|
* is applied later, uniformly, in materializeCarried — never per-piece here. */
|
|
@@ -2340,10 +2283,17 @@ function transformMacroImpl(
|
|
|
2340
2283
|
// does — one encode, one `tableRules(…)` expression, no linker. `carried` is
|
|
2341
2284
|
// unchanged either way: it is the re-lowerable IR list, not a lowering artifact,
|
|
2342
2285
|
// so a downstream re-compose behaves identically whichever engine emitted here.
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2286
|
+
let merged: ReturnType<typeof mergedCarriedRules>
|
|
2287
|
+
try {
|
|
2288
|
+
merged = mergedCarriedRules(carried, {
|
|
2289
|
+
trivia: composing,
|
|
2290
|
+
...(cHostMode ? { hostMode: cHostMode as HostMode } : {}),
|
|
2291
|
+
})
|
|
2292
|
+
} catch (e) {
|
|
2293
|
+
// Linking refuses the composition (a `prior.X` no earlier piece defines): a
|
|
2294
|
+
// runtime `compose()` would refuse it the same way, so the build fails, here.
|
|
2295
|
+
throw new Error(`${id}:${lineOf(init.start)} — ${(e as Error).message}`, { cause: e })
|
|
2296
|
+
}
|
|
2347
2297
|
if (merged?.dangling !== undefined) throw new Error(`${id}:${lineOf(init.start)} — compose: ${merged.dangling}`)
|
|
2348
2298
|
if (merged !== null) {
|
|
2349
2299
|
// Harvest the direct-builder import provenance carried on the merged graph so
|
|
@@ -2475,22 +2425,15 @@ function transformMacroImpl(
|
|
|
2475
2425
|
return null
|
|
2476
2426
|
}
|
|
2477
2427
|
const localArg = elements[elements.length - 1]!
|
|
2478
|
-
let localRules:
|
|
2479
|
-
// The LOCAL leaf grammar's own ambient scanSkip (from its `rules({ scanSkip })`)
|
|
2480
|
-
// governs the scanTo/balanced sites IT defines — unlike trivia (composing-wins),
|
|
2481
|
-
// opaque units are dialect-specific, so the local declaration is threaded to the
|
|
2482
|
-
// local piece's compile.
|
|
2483
|
-
let localScanSkip: Combinator<unknown>[] | undefined
|
|
2428
|
+
let localRules: ReadonlyMap<string, Combinator<unknown>> | null = null
|
|
2484
2429
|
const importedFactories: string[] = []
|
|
2485
2430
|
if (isRulesCall(localArg)) {
|
|
2486
2431
|
const evaluated = evaluateRulesFactory(localArg, `composeLeaf${init.start}`)
|
|
2487
2432
|
localRules = evaluated?.ruleMap ?? null
|
|
2488
|
-
localScanSkip = evaluated?.scanSkip
|
|
2489
2433
|
if (evaluated?.importedFactory) importedFactories.push(evaluated.importedFactory)
|
|
2490
2434
|
} else if (localArg.type === 'Identifier') {
|
|
2491
2435
|
const name = (localArg as unknown as { name: string }).name
|
|
2492
2436
|
localRules = localRuleMaps.get(name) ?? null
|
|
2493
|
-
localScanSkip = localGrammarScanSkip.get(name)
|
|
2494
2437
|
}
|
|
2495
2438
|
if (!localRules) {
|
|
2496
2439
|
// Distinguish "the argument is the wrong SHAPE" from "the argument is the right
|
|
@@ -2525,13 +2468,6 @@ function transformMacroImpl(
|
|
|
2525
2468
|
// may place one beneath a direct node(). Re-lower their terminals with
|
|
2526
2469
|
// capture enabled so that node receives the imported token values in its
|
|
2527
2470
|
// normal child collector; the pieces still contain no semantic callback.
|
|
2528
|
-
const localNs = nsFor(`composeLeaf${init.start}`)
|
|
2529
|
-
const localEntries = [...localRules] as Array<[string, Combinator<unknown>]>
|
|
2530
|
-
|
|
2531
|
-
// TABLE FIRST — same merge as `compose()`, with the local leaf map appended LAST
|
|
2532
|
-
// so it wins every name and binds the imported shapes' holes (`enc.winners`
|
|
2533
|
-
// resolves those by name; see `mergedCarriedRules`).
|
|
2534
|
-
//
|
|
2535
2471
|
// The recognition-only gate is preserved EXACTLY, and is the reason this path
|
|
2536
2472
|
// needs no `compileLinkable`: `hasDirectBuilders` / `isRecognitionOnly` are
|
|
2537
2473
|
// predicates over the combinator graph, not products of lowering it, so
|
|
@@ -2554,36 +2490,23 @@ function transformMacroImpl(
|
|
|
2554
2490
|
warn(init.start, 'composeLeaf(): every pre-final grammar must explicitly prove recognition-only')
|
|
2555
2491
|
return null
|
|
2556
2492
|
}
|
|
2557
|
-
// The
|
|
2558
|
-
//
|
|
2559
|
-
//
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
}
|
|
2493
|
+
// The same link as `compose()` and runtime `composeLeaf()`: the leaf is the last
|
|
2494
|
+
// recipe, so it wins every name, binds the imported shapes' holes and fills its
|
|
2495
|
+
// `prior.X`. Each recipe keeps its own scanSkip; the composing trivia governs all.
|
|
2496
|
+
let linked: Record<string, Combinator<unknown>>
|
|
2497
|
+
try {
|
|
2498
|
+
linked = linkRules(
|
|
2499
|
+
[...carriedMaps.pieces.flatMap(p => p.recipes), ...ruleMapRecipes.get(localRules)!],
|
|
2500
|
+
{ trivia: composing },
|
|
2501
|
+
)
|
|
2502
|
+
} catch (e) {
|
|
2503
|
+
throw new LinkRefusal(`${id}:${lineOf(init.start)} — composeLeaf: ${(e as Error).message.replace(/^compose: /, '')}`, { cause: e })
|
|
2568
2504
|
}
|
|
2569
|
-
const
|
|
2570
|
-
// The same refusal as `compose()`, over a merge by name: a name some piece only
|
|
2571
|
-
// reads (an unresolved entry of a carried map, or a local factory's external
|
|
2572
|
-
// access) that no piece defines.
|
|
2573
|
-
const accessed = [
|
|
2574
|
-
...carriedMaps.pieces.flatMap(p => p.rules).filter(([, rule]) => !isResolved(rule)).map(([name]) => name),
|
|
2575
|
-
...(localRules instanceof Map ? externalAccesses.get(localRules) ?? [] : []),
|
|
2576
|
-
]
|
|
2577
|
-
const dangling = danglingReference(Object.fromEntries(leafMerged), accessed)
|
|
2505
|
+
const dangling = danglingReference(linked)
|
|
2578
2506
|
if (dangling !== undefined) throw new LinkRefusal(`${id}:${lineOf(init.start)} — composeLeaf: ${dangling}`)
|
|
2579
|
-
// Composing-wins governs the leaf fuse too. The local entries are NOT freshly
|
|
2580
|
-
// evaluated (they come from this module's own `rules()` factory), so they are
|
|
2581
|
-
// excluded from the override — their declared trivia IS the composing candidate,
|
|
2582
|
-
// and rewriting `_meta` on them would mutate the module-level grammar object.
|
|
2583
|
-
if (composing) applyComposingTrivia(carriedMaps.pieces.flatMap(p => p.rules), composing)
|
|
2584
2507
|
const refusals: string[] = []
|
|
2585
2508
|
const compiled = compileRuleMap(
|
|
2586
|
-
|
|
2509
|
+
Object.entries(linked),
|
|
2587
2510
|
{
|
|
2588
2511
|
...(composing ? { trivia: composing } : {}),
|
|
2589
2512
|
...(carriedMaps.trackLines ? { trackLines: true } : {}),
|
package/src/spec/model.ts
CHANGED
|
@@ -507,7 +507,9 @@ class Builder {
|
|
|
507
507
|
|
|
508
508
|
case 'lazy': {
|
|
509
509
|
const inner = resolveLazy(self)
|
|
510
|
-
|
|
510
|
+
if (inner) return this.walk(inner)
|
|
511
|
+
const prior = (self as { _priorOf?: string })._priorOf
|
|
512
|
+
return { kind: 'annotation', text: prior === undefined ? '?' : `prior.${prior}` }
|
|
511
513
|
}
|
|
512
514
|
|
|
513
515
|
case 'unknown':
|
package/src/table/assemble.ts
CHANGED
|
@@ -88,7 +88,7 @@ import { PlainBuf, PlainField, PlainLeaf, PlainNode, PlainSpan } from '../cst/pl
|
|
|
88
88
|
import {
|
|
89
89
|
OP_CHOICE, OP_EMPTY, OP_GATE, OP_LEAF, OP_SOURCE_LEAF, OP_LIT, OP_NODE, OP_NOT, OP_OPT,
|
|
90
90
|
OP_PEEK, OP_REP, OP_REPV, OP_RULE, OP_RX, OP_SEQ, OP_SEQV, OP_XFORM,
|
|
91
|
-
OP_LIT_TRACK, OP_RX_TRACK, OP_NODE_TRACK, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, OP_EXPECT, OP_SEQX, OP_SCAN,
|
|
91
|
+
OP_LIT_TRACK, OP_RX_TRACK, OP_NODE_TRACK, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, SCOPE_INHERIT, OP_EXPECT, OP_SEQX, OP_SCAN,
|
|
92
92
|
OP_LIVE,
|
|
93
93
|
OP_FIELD, OP_DISPATCH, OP_ROUTED, OP_LIT_CI, OP_LIT_CI_TRACK, OP_TOKEN, OP_WITHCTX, OP_GUARD, OP_ATTEMPT, OP_LABEL,
|
|
94
94
|
OP_COV, OP_GAPGATE, OP_STRICT,
|
|
@@ -2109,6 +2109,16 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2109
2109
|
case OP_SCOPE_CAP:
|
|
2110
2110
|
case OP_SCOPE_PLAIN: {
|
|
2111
2111
|
const ki = code[ip + 1]!
|
|
2112
|
+
if (ki === SCOPE_INHERIT) {
|
|
2113
|
+
const body = link(code[ip + 2]!)
|
|
2114
|
+
return (input, pos, ctx) => {
|
|
2115
|
+
const savedCap = ctx.captureTrivia
|
|
2116
|
+
ctx.captureTrivia = true
|
|
2117
|
+
const v = body(input, pos, ctx)
|
|
2118
|
+
ctx.captureTrivia = savedCap
|
|
2119
|
+
return v
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2112
2122
|
const scopeTrivia = ki < 0 ? undefined : (trivia[ki] as ParseContext['trivia'])
|
|
2113
2123
|
const scopeLabels = scopeTrivia?._meta.triviaKindLabels
|
|
2114
2124
|
// THE SWAP, RESOLVED AT ASSEMBLY. `exec.ts` computed
|
|
@@ -50,7 +50,7 @@ import {
|
|
|
50
50
|
OP_ADJ, OP_ARMGATE, OP_RGATE, OP_RGATE2, OP_ATTEMPT, OP_CHOICE, OP_DISPATCH, OP_EMPTY, OP_EXPECT, OP_FIELD, OP_GATE,
|
|
51
51
|
OP_LABEL, OP_LEAF, OP_SOURCE_LEAF, OP_LIT, OP_LIT_CI, OP_LIT_CI_TRACK, OP_LIT_TRACK, OP_NAMES,
|
|
52
52
|
OP_NODE, OP_NODE_TRACK, OP_NOT, OP_OPT, OP_PEEK, OP_REP, OP_REPV, OP_ROUTED, OP_RULE, OP_RX,
|
|
53
|
-
OP_RX_TRACK, OP_SCAN, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, OP_SEQ, OP_SEQV, OP_SEQX, OP_TOKEN, OP_WITHCTX, OP_XFORM,
|
|
53
|
+
OP_RX_TRACK, OP_SCAN, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, SCOPE_INHERIT, OP_SEQ, OP_SEQV, OP_SEQX, OP_TOKEN, OP_WITHCTX, OP_XFORM,
|
|
54
54
|
OP_LEX_BODY, OP_LEX_PROGRAM, OP_GAPGATE, OP_STRICT,
|
|
55
55
|
} from './ops.ts'
|
|
56
56
|
import {
|
|
@@ -1625,6 +1625,15 @@ return r.value
|
|
|
1625
1625
|
const cap = op === OP_SCOPE_CAP
|
|
1626
1626
|
const flags = op === OP_SCOPE_PLAIN ? 0 : code[ip + 3]!
|
|
1627
1627
|
const child = link(code[ip + 2]!)
|
|
1628
|
+
if (ki === SCOPE_INHERIT) {
|
|
1629
|
+
return `${head}
|
|
1630
|
+
const sC=ctx.captureTrivia
|
|
1631
|
+
ctx.captureTrivia=true
|
|
1632
|
+
const v=${child}(input,pos,ctx)
|
|
1633
|
+
ctx.captureTrivia=sC
|
|
1634
|
+
return v
|
|
1635
|
+
}`
|
|
1636
|
+
}
|
|
1628
1637
|
// THE SWAP, RESOLVED AT EMIT. `swapLegal` is `!trackLines`, an option;
|
|
1629
1638
|
// the other two are table data. All three are known here, so the body
|
|
1630
1639
|
// holds the scanner it installs as a hoisted const.
|
package/src/table/encode.ts
CHANGED
|
@@ -15,7 +15,7 @@ import { asciiFoldKey, branchUsesRouted, parserUsesRouted } from '../combinators
|
|
|
15
15
|
import {
|
|
16
16
|
OP_CHOICE, OP_EMPTY, OP_GATE, OP_LEAF, OP_SOURCE_LEAF, OP_LIT, OP_NODE, OP_NOT, OP_OPT,
|
|
17
17
|
OP_PEEK, OP_REP, OP_REPV, OP_RULE, OP_RX, OP_SEQ, OP_SEQV, OP_XFORM,
|
|
18
|
-
OP_LIT_TRACK, OP_RX_TRACK, OP_NODE_TRACK, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, OP_EXPECT, OP_SEQX, OP_SCAN,
|
|
18
|
+
OP_LIT_TRACK, OP_RX_TRACK, OP_NODE_TRACK, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, SCOPE_INHERIT, OP_EXPECT, OP_SEQX, OP_SCAN,
|
|
19
19
|
OP_LIVE, OP_ATTEMPT, OP_LABEL,
|
|
20
20
|
OP_FIELD, OP_DISPATCH, OP_ROUTED, OP_LIT_CI, OP_LIT_CI_TRACK, OP_TOKEN, OP_WITHCTX, OP_GUARD,
|
|
21
21
|
OP_ADJ, OP_GREEDY, OP_REJECT, OP_ARMGATE, OP_RGATE, OP_COV, OP_GAPGATE, OP_RGATE2, OP_STRICT,
|
|
@@ -2182,13 +2182,19 @@ class Encoder {
|
|
|
2182
2182
|
// this scope's trivia is opaque to root capture, which `run()` consumes.
|
|
2183
2183
|
// Nothing in the parse changes, so there is nothing for the table to
|
|
2184
2184
|
// lower — accepting it is not a gap, it is the correct lowering.
|
|
2185
|
-
// ONLY `true` IS MEANINGFUL HERE. `
|
|
2186
|
-
//
|
|
2187
|
-
//
|
|
2188
|
-
//
|
|
2189
|
-
//
|
|
2190
|
-
//
|
|
2191
|
-
//
|
|
2185
|
+
// ONLY `true` IS MEANINGFUL HERE. `def.trackLines` is `opts.trackLines ??
|
|
2186
|
+
// false`, so an unset scope and an explicitly-false one read the same on that
|
|
2187
|
+
// field (`def.constructionTrackLines` keeps the difference, and the encoder
|
|
2188
|
+
// does not read it). An unset scope inherits, so stored-false cannot be read
|
|
2189
|
+
// as "force off", and comparing it against the setting refused every inner
|
|
2190
|
+
// scope of a trackLines:true grammar — all four `*PositionsGrammar`s.
|
|
2191
|
+
//
|
|
2192
|
+
// THIS IS NOT THE INTERPRETER'S SCOPING, AND A REDUCER CAN SEE IT (#258). A
|
|
2193
|
+
// table has one line setting. A node outside a `trackLines: true` scope gets
|
|
2194
|
+
// line fields from the table and none from the interpreter; a
|
|
2195
|
+
// `trackLines: false` scope under a tracked grammar keeps them in the table
|
|
2196
|
+
// and drops them in the interpreter; and a `trackLines: true` scope in a rule
|
|
2197
|
+
// the entry cannot reach still turns them on for `encodeTable`.
|
|
2192
2198
|
//
|
|
2193
2199
|
// A SCOPE THAT ASKS FOR TRACKING NO LONGER REFUSES. `encodeTable` folds
|
|
2194
2200
|
// every such scope into the table's own `track` before encoding begins
|
|
@@ -2237,8 +2243,9 @@ class Encoder {
|
|
|
2237
2243
|
this.activeTrivia = savedTrivia
|
|
2238
2244
|
this.triviaKnown = savedKnown
|
|
2239
2245
|
if (d.clearTrivia === true) return this.emit(op, -1, bodyIp, flags)
|
|
2246
|
+
// No trivia named: `flags` is zero (`rootCapture: 'opaque'` requires one).
|
|
2240
2247
|
if (d.triviaParser === undefined) {
|
|
2241
|
-
return cap
|
|
2248
|
+
return cap ? this.emit(op, SCOPE_INHERIT, bodyIp, flags) : bodyIp
|
|
2242
2249
|
}
|
|
2243
2250
|
return this.emit(op, this.triviaSlot(d.triviaParser), bodyIp, flags)
|
|
2244
2251
|
}
|
package/src/table/exec.ts
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
import {
|
|
19
19
|
OP_CHOICE, OP_EMPTY, OP_GATE, OP_LEAF, OP_SOURCE_LEAF, OP_LIT, OP_NODE, OP_NOT, OP_OPT,
|
|
20
20
|
OP_PEEK, OP_REP, OP_REPV, OP_RULE, OP_RX, OP_SEQ, OP_SEQV, OP_XFORM,
|
|
21
|
-
OP_LIT_TRACK, OP_RX_TRACK, OP_NODE_TRACK, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, OP_EXPECT, OP_SEQX, OP_SCAN,
|
|
21
|
+
OP_LIT_TRACK, OP_RX_TRACK, OP_NODE_TRACK, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, SCOPE_INHERIT, OP_EXPECT, OP_SEQX, OP_SCAN,
|
|
22
22
|
OP_LIVE,
|
|
23
23
|
OP_FIELD, OP_DISPATCH, OP_ROUTED, OP_LIT_CI, OP_LIT_CI_TRACK, OP_TOKEN, OP_WITHCTX, OP_GUARD, OP_ATTEMPT, OP_LABEL,
|
|
24
24
|
OP_COV, OP_GAPGATE, OP_STRICT,
|
|
@@ -779,14 +779,16 @@ function makeDriver(
|
|
|
779
779
|
// labelled trivia is excluded here rather than tested later: `scanTrivia`
|
|
780
780
|
// suppresses its own fast path when labels are present, so a swap there
|
|
781
781
|
// would drop the labelled records silently.
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
782
|
+
if (ki !== SCOPE_INHERIT) {
|
|
783
|
+
SCAN = FAST && ki >= 0 && !triviaLabelled[ki]! ? triviaScan[ki]! : null
|
|
784
|
+
ctx.trivia = scopeTrivia
|
|
785
|
+
// A scope installs its trivia's KIND LABELS too. Root-trivia rows are
|
|
786
|
+
// only ever written on the labelled scan path (trivia-skip.ts:212) — the
|
|
787
|
+
// unlabelled fast scanner returns before any root logging and does not
|
|
788
|
+
// even test `_rootTriviaLog` — so a scope that sets trivia without its
|
|
789
|
+
// labels captures NOTHING at the root, silently.
|
|
790
|
+
ctx.triviaKindLabels = scopeTrivia?._meta.triviaKindLabels
|
|
791
|
+
}
|
|
790
792
|
// SCOPE_CAP additionally turns capture ON for the child. It is an OR with
|
|
791
793
|
// whatever the enclosing context already asked for (`grammar.ts:129`), so
|
|
792
794
|
// the RESTORE puts the saved value back rather than writing `false` — an
|
package/src/table/ops.ts
CHANGED
|
@@ -211,6 +211,12 @@ export const OP_TOKEN = 29
|
|
|
211
211
|
* is why this restores the saved value rather than writing `false`.
|
|
212
212
|
*/
|
|
213
213
|
export const OP_SCOPE_CAP = 30
|
|
214
|
+
/**
|
|
215
|
+
* `k` of a `SCOPE_CAP` row whose `parser({ captureTrivia: true })` names no trivia.
|
|
216
|
+
* It raises capture and leaves the trivia the scope was entered with in force,
|
|
217
|
+
* where `k = -1` clears it (`trivia: null`).
|
|
218
|
+
*/
|
|
219
|
+
export const SCOPE_INHERIT = -2
|
|
214
220
|
/**
|
|
215
221
|
* `WITHCTX k c` — `withCtx(extra, c)`. `k >= 0` is `extra` in the const pool
|
|
216
222
|
* (the replace form); `k < 0` is the derive form, `~k` indexing the author's
|
package/src/table/site-labels.ts
CHANGED
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
import {
|
|
63
63
|
OP_ATTEMPT, OP_CHOICE, OP_DISPATCH, OP_EXPECT, OP_FIELD, OP_GATE, OP_LABEL, OP_LEAF, OP_LIT, OP_SOURCE_LEAF,
|
|
64
64
|
OP_NODE, OP_NODE_TRACK, OP_NOT, OP_OPT, OP_PEEK, OP_REP, OP_REPV, OP_ROUTED, OP_RULE,
|
|
65
|
-
OP_SCAN, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, OP_SEQ, OP_SEQV, OP_SEQX, OP_TOKEN, OP_XFORM,
|
|
65
|
+
OP_SCAN, OP_SCOPE, OP_SCOPE_CAP, OP_SCOPE_PLAIN, SCOPE_INHERIT, OP_SEQ, OP_SEQV, OP_SEQX, OP_TOKEN, OP_XFORM,
|
|
66
66
|
} from './ops.ts'
|
|
67
67
|
import { childSlots } from './child-slots.ts'
|
|
68
68
|
import { leadRanges } from '../lead-ranges.ts'
|
|
@@ -206,7 +206,7 @@ function transfer(code: Int32Array, ip: number, at: SiteLabel, hostCst: boolean)
|
|
|
206
206
|
const op = code[ip]
|
|
207
207
|
if (op === OP_SCOPE || op === OP_SCOPE_CAP || op === OP_SCOPE_PLAIN) {
|
|
208
208
|
const ki = code[ip + 1]!
|
|
209
|
-
const tri = ki < 0 ? TRI_NONE : ki
|
|
209
|
+
const tri = ki === SCOPE_INHERIT ? at.tri : ki < 0 ? TRI_NONE : ki
|
|
210
210
|
// `OP_SCOPE_CAP` is an OR with the inherited context, never a switch-off
|
|
211
211
|
// (`encode.ts:1153-1158`), so it can only ever raise `cap` to `CAP_ON`.
|
|
212
212
|
const cap = op === OP_SCOPE_CAP ? CAP_ON : at.cap
|
|
@@ -358,7 +358,7 @@ export function scanHandoffNeeded(code: Int32Array, ip: number, labels: SiteLabe
|
|
|
358
358
|
const op = code[at]
|
|
359
359
|
// A scan runs interpreted combinators that reach pieces through `prog.scans`.
|
|
360
360
|
if (op === OP_SCAN) return true
|
|
361
|
-
if (op === OP_TOKEN || op === OP_SCOPE_CAP) continue
|
|
361
|
+
if (op === OP_TOKEN || (op === OP_SCOPE_CAP && code[at + 1] !== SCOPE_INHERIT)) continue
|
|
362
362
|
if ((op === OP_SCOPE_PLAIN || op === OP_SCOPE) && !(code[at + 1]! >= 0 && l.tri === code[at + 1]!
|
|
363
363
|
&& (op === OP_SCOPE_PLAIN || code[at + 3]! === 0))) continue
|
|
364
364
|
kids.length = 0
|
package/src/version.ts
CHANGED