parseman 0.59.3 → 0.60.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/diagnostics.js +874 -188
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +874 -188
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/dispatch.d.ts +13 -1
- package/dist/combinators/dispatch.d.ts.map +1 -1
- package/dist/combinators/first-set.d.ts +35 -2
- package/dist/combinators/first-set.d.ts.map +1 -1
- package/dist/combinators/regex.d.ts.map +1 -1
- package/dist/combinators/trivia-boundary.d.ts +11 -0
- package/dist/combinators/trivia-boundary.d.ts.map +1 -1
- package/dist/combinators/trivia-skip.d.ts +1 -1
- package/dist/combinators/trivia-skip.d.ts.map +1 -1
- package/dist/compiler/token-alphabet.d.ts +2 -0
- package/dist/compiler/token-alphabet.d.ts.map +1 -1
- package/dist/compiler/token-capability.js +65 -53
- package/dist/compiler/token-capability.js.map +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +894 -197
- package/dist/index.js.map +3 -3
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.js +901 -199
- package/dist/plugin/index.js.map +3 -3
- package/dist/regex/first-set.d.ts +19 -0
- package/dist/regex/first-set.d.ts.map +1 -1
- package/dist/table/assemble.d.ts +14 -0
- package/dist/table/assemble.d.ts.map +1 -1
- package/dist/table/child-slots.d.ts.map +1 -1
- package/dist/table/compile-rule-map.d.ts.map +1 -1
- package/dist/table/emit-assembly.d.ts +8 -2
- package/dist/table/emit-assembly.d.ts.map +1 -1
- package/dist/table/emit.d.ts +4 -3
- package/dist/table/emit.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/factor-choice.d.ts +27 -0
- package/dist/table/factor-choice.d.ts.map +1 -1
- package/dist/table/gap-gate.d.ts +11 -0
- package/dist/table/gap-gate.d.ts.map +1 -0
- package/dist/table/index.js +924 -199
- package/dist/table/index.js.map +3 -3
- package/dist/table/lex-program.d.ts.map +1 -1
- package/dist/table/ops.d.ts +34 -6
- package/dist/table/ops.d.ts.map +1 -1
- package/dist/table/program.d.ts +6 -1
- package/dist/table/program.d.ts.map +1 -1
- package/dist/types.d.ts +16 -4
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/analysis/duplication.ts +1 -1
- package/src/combinators/attempt.ts +1 -1
- package/src/combinators/dispatch.ts +30 -6
- package/src/combinators/first-set.ts +55 -12
- package/src/combinators/node.ts +1 -1
- package/src/combinators/regex.ts +9 -4
- package/src/combinators/repeat.ts +6 -6
- package/src/combinators/sequence.ts +1 -1
- package/src/combinators/trivia-boundary.ts +18 -0
- package/src/combinators/trivia-skip.ts +1 -1
- package/src/compiler/ir-serialize.ts +1 -1
- package/src/compiler/token-alphabet.ts +3 -1
- package/src/coverage.ts +1 -1
- package/src/index.ts +1 -1
- package/src/plugin/evaluator.ts +8 -3
- package/src/regex/first-set.ts +37 -3
- package/src/table/assemble.ts +171 -25
- package/src/table/child-slots.ts +8 -3
- package/src/table/compile-rule-map.ts +6 -3
- package/src/table/emit-assembly.ts +182 -31
- package/src/table/emit.ts +6 -4
- package/src/table/encode.ts +468 -73
- package/src/table/exec.ts +38 -13
- package/src/table/factor-choice.ts +62 -0
- package/src/table/fail-clean.ts +2 -2
- package/src/table/gap-gate.ts +55 -0
- package/src/table/hold-gap.ts +2 -2
- package/src/table/lex-program.ts +15 -6
- package/src/table/ops.ts +37 -6
- package/src/table/program.ts +7 -2
- package/src/table/select-hot.ts +7 -2
- package/src/types.ts +12 -5
- package/src/version.ts +1 -1
|
@@ -154,11 +154,11 @@ export function many<T>(combinator: Combinator<T>, opts: RepeatOptions = {}): Co
|
|
|
154
154
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
155
155
|
isTrivia: false,
|
|
156
156
|
}
|
|
157
|
-
const def: { tag: 'many'; parser: Combinator<unknown>; min: 0; max?: number; valueUnused?: boolean } =
|
|
158
|
-
{ tag: 'many', parser: combinator as Combinator<unknown>, min: 0, ...(max === Infinity ? {} : { max }) }
|
|
159
|
-
let expected: string[] | undefined
|
|
160
157
|
// A non-nullable body can be first-set-gated per loop iteration (see repItem).
|
|
161
158
|
const guardable = combinator._meta.firstSet.kind !== 'any' && !matchesEmpty(combinator)
|
|
159
|
+
const def: { tag: 'many'; parser: Combinator<unknown>; min: 0; max?: number; valueUnused?: boolean; guarded?: true } =
|
|
160
|
+
{ tag: 'many', parser: combinator as Combinator<unknown>, min: 0, ...(max === Infinity ? {} : { max }), ...(guardable ? { guarded: true } : {}) }
|
|
161
|
+
let expected: string[] | undefined
|
|
162
162
|
const scalarChild = scalarOf(combinator)
|
|
163
163
|
const parseScalar = (input: string, pos: number, ctx: ParseContext): number =>
|
|
164
164
|
repeatScalarTail(combinator, scalarChild, input, pos, ctx, guardable, def.valueUnused ? undefined : [], max)
|
|
@@ -229,11 +229,11 @@ function atLeast<T>(combinator: Combinator<T>, min: number, max: number): Combin
|
|
|
229
229
|
canMatchNewline: combinator._meta.canMatchNewline,
|
|
230
230
|
isTrivia: false,
|
|
231
231
|
}
|
|
232
|
-
const def: { tag: 'oneOrMore'; parser: Combinator<unknown>; min: number; max?: number; valueUnused?: boolean } =
|
|
233
|
-
{ tag: 'oneOrMore', parser: combinator as Combinator<unknown>, min, ...(max === Infinity ? {} : { max }) }
|
|
234
|
-
let expected: string[] | undefined
|
|
235
232
|
// A non-nullable body can be first-set-gated per loop iteration (see repItem).
|
|
236
233
|
const guardable = combinator._meta.firstSet.kind !== 'any' && !matchesEmpty(combinator)
|
|
234
|
+
const def: { tag: 'oneOrMore'; parser: Combinator<unknown>; min: number; max?: number; valueUnused?: boolean; guarded?: true } =
|
|
235
|
+
{ tag: 'oneOrMore', parser: combinator as Combinator<unknown>, min, ...(max === Infinity ? {} : { max }), ...(guardable ? { guarded: true } : {}) }
|
|
236
|
+
let expected: string[] | undefined
|
|
237
237
|
const scalarChild = scalarOf(combinator)
|
|
238
238
|
const parseScalar = (input: string, pos: number, ctx: ParseContext): number => {
|
|
239
239
|
let cur = scalarChild(input, pos, ctx)
|
|
@@ -108,7 +108,7 @@ export function sequence<T extends [Combinator<unknown>, ...Combinator<unknown>[
|
|
|
108
108
|
if (tolerant) {
|
|
109
109
|
followSentinels ??= parsers.map((_, i) => firstSetSentinel(
|
|
110
110
|
parsers.slice(i + 1).reduce<ReturnType<typeof firstSetOf>>(
|
|
111
|
-
(set, parser) => union(set, firstSetOf(parser)), { kind: 'empty' },
|
|
111
|
+
(set, parser) => union(set, firstSetOf(parser, new Set(), undefined, undefined, false)), { kind: 'empty' },
|
|
112
112
|
),
|
|
113
113
|
))
|
|
114
114
|
}
|
|
@@ -46,3 +46,21 @@ export function hasOwnTriviaBoundary(p: Combinator<unknown>, depth: number = MAX
|
|
|
46
46
|
default: return false
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The trivia `target` runs under when it is reached through the reference `ref`
|
|
52
|
+
* from a scope whose live trivia is `trivia` (`null` or `undefined`: none).
|
|
53
|
+
*
|
|
54
|
+
* A reference made from a region that cleared trivia re-installs the rule's own
|
|
55
|
+
* ambient trivia, where the rule has a boundary for it to repair (`ref.ts`,
|
|
56
|
+
* `Encoder.scopedRef`). Anything derived statically through a reference — a
|
|
57
|
+
* first set, a gate, a gap — must take the same scope, or it describes the
|
|
58
|
+
* caller's scope where the body runs under its own.
|
|
59
|
+
*/
|
|
60
|
+
export function triviaThroughRef(
|
|
61
|
+
ref: Combinator<unknown>, target: Combinator<unknown>, trivia: Combinator<unknown> | null | undefined,
|
|
62
|
+
): Combinator<unknown> | null | undefined {
|
|
63
|
+
if (trivia !== undefined && trivia !== null) return trivia
|
|
64
|
+
const amb = ref._meta.grammarTrivia ?? target._meta.grammarTrivia
|
|
65
|
+
return amb !== undefined && hasOwnTriviaBoundary(target) ? amb : trivia
|
|
66
|
+
}
|
|
@@ -413,7 +413,7 @@ export function scanTriviaCompact(input: string, cur: number, ctx: ParseContext)
|
|
|
413
413
|
return { end: tr.ok ? tr.span.end : cur, commit: NOOP_COMMIT }
|
|
414
414
|
}
|
|
415
415
|
|
|
416
|
-
/**
|
|
416
|
+
/** The object-shaped form of `scanTriviaCompact`. No engine calls it; the trivia-skip tests compare the two. */
|
|
417
417
|
export function scanTrivia(input: string, cur: number, ctx: ParseContext): TriviaScan {
|
|
418
418
|
const scan = scanTriviaCompact(input, cur, ctx)
|
|
419
419
|
return typeof scan === 'number' ? { end: scan, commit: NOOP_COMMIT } : scan
|
|
@@ -461,7 +461,7 @@ class Serializer {
|
|
|
461
461
|
arms.push(`when(${matcherExpr(entry)}, ${kid(entry.parser)}${entry.caseInsensitive ? ', { caseInsensitive: true }' : ''})`)
|
|
462
462
|
}
|
|
463
463
|
}
|
|
464
|
-
if (def.otherwise) arms.push(`otherwise(${kid(def.otherwise)})`)
|
|
464
|
+
if (def.otherwise) arms.push(`otherwise(${kid(def.otherwise)}${def.otherwiseCommit === false ? ', { commit: false }' : ''})`)
|
|
465
465
|
return `dispatch(${kid(def.selector)}${arms.length === 0 ? '' : `, ${arms.join(', ')}`})`
|
|
466
466
|
}
|
|
467
467
|
case 'many': return `many(${kid(def.parser)}${repeatOpts(def.min, def.max)})`
|
|
@@ -140,6 +140,8 @@ export type ExecutableLexNode =
|
|
|
140
140
|
readonly parsers: readonly Combinator<unknown>[]
|
|
141
141
|
/** Inside a no-trivia scope of the token, where the encoder's trivia is none. */
|
|
142
142
|
readonly cleared: boolean
|
|
143
|
+
/** The authored choice's own `disjoint`, which decides how the encoder classifies its arms. */
|
|
144
|
+
readonly disjoint: boolean
|
|
143
145
|
readonly armExpected: readonly (readonly string[])[]
|
|
144
146
|
readonly expected: readonly string[]
|
|
145
147
|
}
|
|
@@ -509,7 +511,7 @@ function directFixedLexNode(
|
|
|
509
511
|
if (arms.some(arm => arm === undefined)) return undefined
|
|
510
512
|
return {
|
|
511
513
|
kind: 'choice', arms: arms as ExecutableLexNode[],
|
|
512
|
-
parsers: def.parsers, cleared,
|
|
514
|
+
parsers: def.parsers, cleared, disjoint: def.disjoint,
|
|
513
515
|
armExpected: def.parsers.map(deriveExpected),
|
|
514
516
|
expected: def.parsers.flatMap(deriveExpected),
|
|
515
517
|
}
|
package/src/coverage.ts
CHANGED
|
@@ -309,7 +309,7 @@ function coverageEntry(entry: Combinator<unknown>, collector: GrammarCoverageCol
|
|
|
309
309
|
arms.push(when(dispatchMatcher(entry), wrapArm(entry.parser, ids[armIndex++]), entry.caseInsensitive ? { caseInsensitive: true } : undefined))
|
|
310
310
|
}
|
|
311
311
|
}
|
|
312
|
-
if (def.otherwise) arms.push(otherwise(wrapArm(def.otherwise, ids[armIndex++])))
|
|
312
|
+
if (def.otherwise) arms.push(otherwise(wrapArm(def.otherwise, ids[armIndex++]), def.otherwiseCommit === false ? { commit: false } : undefined))
|
|
313
313
|
const unsafeDispatch = dispatch as (selector: Combinator<string>, ...items: DispatchArm<unknown>[]) => Combinator<unknown>
|
|
314
314
|
const base = unsafeDispatch(build(def.selector) as Combinator<string>, ...arms)
|
|
315
315
|
return {
|
package/src/index.ts
CHANGED
|
@@ -17,7 +17,7 @@ export type { KeywordsOptions, WordOptions } from './combinators/keywords.ts'
|
|
|
17
17
|
export { sequence } from './combinators/sequence.ts'
|
|
18
18
|
export { choice } from './combinators/choice.ts'
|
|
19
19
|
export { dispatch, endsWith, makeWhen, matches, otherwise, routed, startsWith, when } from './combinators/dispatch.ts'
|
|
20
|
-
export type { DispatchArm, DispatchOtherwise, DispatchStringMatcher, DispatchWhen, DispatchWhenFactory, DispatchWhenOptions, DispatchWhenMatcher } from './combinators/dispatch.ts'
|
|
20
|
+
export type { DispatchArm, DispatchOtherwise, DispatchOtherwiseOptions, DispatchStringMatcher, DispatchWhen, DispatchWhenFactory, DispatchWhenOptions, DispatchWhenMatcher } from './combinators/dispatch.ts'
|
|
21
21
|
export { attempt } from './combinators/attempt.ts'
|
|
22
22
|
export type { AttemptOptions } from './combinators/attempt.ts'
|
|
23
23
|
export { many, oneOrMore, optional, sepBy, oneOrMoreSep, keepSeparator } from './combinators/repeat.ts'
|
package/src/plugin/evaluator.ts
CHANGED
|
@@ -431,11 +431,16 @@ function dispatchArmValue(node: Expression, scope: XScope, code?: string, mfs?:
|
|
|
431
431
|
}
|
|
432
432
|
|
|
433
433
|
if (callee.name === 'otherwise') {
|
|
434
|
-
const [parserArg] = node.arguments
|
|
435
|
-
if (!parserArg || parserArg.type === 'SpreadElement') return null
|
|
434
|
+
const [parserArg, optsArg] = node.arguments
|
|
435
|
+
if (!parserArg || parserArg.type === 'SpreadElement' || optsArg?.type === 'SpreadElement') return null
|
|
436
436
|
const parserValue = anyValue(parserArg as Expression, scope, code, mfs)
|
|
437
437
|
if (!isCombinator(parserValue)) return null
|
|
438
|
-
return parseman.otherwise(parserValue)
|
|
438
|
+
if (optsArg === undefined) return parseman.otherwise(parserValue)
|
|
439
|
+
const opts = anyValue(optsArg as Expression, scope, code, mfs)
|
|
440
|
+
if (typeof opts !== 'object' || opts === null) return null
|
|
441
|
+
const commit = (opts as { commit?: unknown }).commit
|
|
442
|
+
if (commit !== undefined && typeof commit !== 'boolean') return null
|
|
443
|
+
return parseman.otherwise(parserValue, commit === undefined ? {} : { commit })
|
|
439
444
|
}
|
|
440
445
|
|
|
441
446
|
if (callee.name === 'when') {
|
package/src/regex/first-set.ts
CHANGED
|
@@ -32,7 +32,7 @@ import { shorthandRanges, parseClassRanges, readUnicodeEscape } from './classes.
|
|
|
32
32
|
type Node =
|
|
33
33
|
| { t: 'char'; set: FirstSet; nl: boolean } // matches exactly one char from `set`
|
|
34
34
|
| { t: 'any' } // unknown 1-char-ish construct: could start with anything
|
|
35
|
-
| { t: 'empty' }
|
|
35
|
+
| { t: 'empty'; look?: Node } // zero-width: anchors, lookaround, word boundary (`look`: a positive lookahead's body)
|
|
36
36
|
| { t: 'seq'; parts: Node[] }
|
|
37
37
|
| { t: 'alt'; arms: Node[] }
|
|
38
38
|
| { t: 'rep'; node: Node; min: number } // quantified; min 0 ⇒ nullable
|
|
@@ -142,10 +142,11 @@ function parseRegex(src: string, unicode = false, sets = false): Node {
|
|
|
142
142
|
i++ // consume '('
|
|
143
143
|
// Group prefixes: (?:…) (?=…) (?!…) (?<=…) (?<!…) (?<name>…)
|
|
144
144
|
let lookaround = false
|
|
145
|
+
let lookahead = false
|
|
145
146
|
if (src[i] === '?') {
|
|
146
147
|
const c1 = src[i + 1]
|
|
147
148
|
if (c1 === ':') i += 2
|
|
148
|
-
else if (c1 === '=' || c1 === '!') { i += 2; lookaround = true }
|
|
149
|
+
else if (c1 === '=' || c1 === '!') { lookahead = c1 === '='; i += 2; lookaround = true }
|
|
149
150
|
else if (c1 === '<') {
|
|
150
151
|
const c2 = src[i + 2]
|
|
151
152
|
if (c2 === '=' || c2 === '!') { i += 3; lookaround = true } // lookbehind
|
|
@@ -159,7 +160,10 @@ function parseRegex(src: string, unicode = false, sets = false): Node {
|
|
|
159
160
|
const inner = parseAlt()
|
|
160
161
|
if (src[i] !== ')') throw BAIL
|
|
161
162
|
i++ // consume ')'
|
|
162
|
-
|
|
163
|
+
// Lookaround is zero-width. A positive LOOKAHEAD keeps its body for
|
|
164
|
+
// `regexFirstTwo`; every other walker reads it as `EMPTY`.
|
|
165
|
+
if (lookaround) return lookahead ? { t: 'empty', look: inner } : EMPTY
|
|
166
|
+
return inner
|
|
163
167
|
}
|
|
164
168
|
|
|
165
169
|
const parseClass = (): Node => {
|
|
@@ -348,6 +352,17 @@ export function regexCanMatchEmpty(source: string): boolean {
|
|
|
348
352
|
try { return nullable(parseRegex(source)) } catch { return true }
|
|
349
353
|
}
|
|
350
354
|
|
|
355
|
+
/**
|
|
356
|
+
* The first chars of a NON-EMPTY match of `source`, nullable or not: the set
|
|
357
|
+
* `firstSetFromRegex` reports for a pattern that must consume, without its
|
|
358
|
+
* widening of a nullable one to `any()`. Only a caller that accounts for the
|
|
359
|
+
* empty match itself may use it (`sequenceSet`). `any()` when unparseable.
|
|
360
|
+
* `flags` reads atoms as `firstSetFromRegex` does.
|
|
361
|
+
*/
|
|
362
|
+
export function consumingFirstSetFromRegex(source: string, flags = ''): FirstSet {
|
|
363
|
+
try { return firstSet(parseRegex(source, flags.includes('u') || flags.includes('v'), flags.includes('v'))) } catch { return any() }
|
|
364
|
+
}
|
|
365
|
+
|
|
351
366
|
/**
|
|
352
367
|
* `flags` decides how atoms read: under `u`/`v` each is one code point, else one
|
|
353
368
|
* code unit, so a non-`u` set holds a high surrogate where an astral character
|
|
@@ -369,3 +384,22 @@ export function firstSetFromRegex(source: string, flags = ''): { firstSet: First
|
|
|
369
384
|
return { firstSet: firstSet(ast), canMatchNewline: canConsumeNewline(ast) }
|
|
370
385
|
}
|
|
371
386
|
|
|
387
|
+
/**
|
|
388
|
+
* The first TWO characters every match of `source` begins with, where the
|
|
389
|
+
* pattern says them outright: a one-character class and then another, or a
|
|
390
|
+
* positive lookahead over one (`:(?=[ \t])`). `undefined` otherwise. Every
|
|
391
|
+
* match that starts with a character of `first` has a second character, and it
|
|
392
|
+
* is in `second`. `flags` reads atoms as `firstSetFromRegex` does.
|
|
393
|
+
*/
|
|
394
|
+
export function regexFirstTwo(source: string, flags = ''): { first: FirstSet; second: FirstSet } | undefined {
|
|
395
|
+
let ast: Node
|
|
396
|
+
try { ast = parseRegex(source, flags.includes('u') || flags.includes('v'), flags.includes('v')) } catch { return undefined }
|
|
397
|
+
const parts = ast.t === 'seq' ? ast.parts : [ast]
|
|
398
|
+
const a = parts[0]
|
|
399
|
+
const b = parts[1]
|
|
400
|
+
if (a === undefined || a.t !== 'char' || a.set.kind !== 'ranges' || b === undefined) return undefined
|
|
401
|
+
const second = b.t === 'char'
|
|
402
|
+
? b.set
|
|
403
|
+
: b.t === 'empty' && b.look !== undefined && !nullable(b.look) ? firstSet(b.look) : undefined
|
|
404
|
+
return second !== undefined && second.kind === 'ranges' ? { first: a.set, second } : undefined
|
|
405
|
+
}
|
package/src/table/assemble.ts
CHANGED
|
@@ -73,10 +73,11 @@ import { skipDeferredTrivia, triviaLeadTable } from './trivia-gap.ts'
|
|
|
73
73
|
import { asciiFoldKey } from '../combinators/dispatch.ts'
|
|
74
74
|
import { projectChild, unwrapChild } from '../combinators/node.ts'
|
|
75
75
|
import { cstOutputHost } from '../compiler/build-arity.ts'
|
|
76
|
+
import { PARSEMAN_VERSION } from '../version.ts'
|
|
76
77
|
import { consumeTrivia } from '../combinators/trivia-skip.ts'
|
|
77
78
|
import {
|
|
78
|
-
advanceTrivia,
|
|
79
|
-
|
|
79
|
+
advanceTrivia, needsDeferredTriviaCommit, rollbackScannedTriviaAt, rollbackTriviaAt,
|
|
80
|
+
skipTriviaScanned, type FastTriviaScanner,
|
|
80
81
|
} from '../combinators/trivia-skip.ts'
|
|
81
82
|
import {
|
|
82
83
|
cstCaptureActive, cstLeavesLen, cstTlLen,
|
|
@@ -90,8 +91,8 @@ import {
|
|
|
90
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
92
|
OP_LIVE,
|
|
92
93
|
OP_FIELD, OP_DISPATCH, OP_ROUTED, OP_LIT_CI, OP_LIT_CI_TRACK, OP_TOKEN, OP_WITHCTX, OP_GUARD, OP_ATTEMPT, OP_LABEL,
|
|
93
|
-
OP_COV,
|
|
94
|
-
OP_ADJ, OP_GREEDY, OP_REJECT, OP_ARMGATE, OP_RGATE,
|
|
94
|
+
OP_COV, OP_GAPGATE, OP_STRICT,
|
|
95
|
+
OP_ADJ, OP_GREEDY, OP_REJECT, OP_ARMGATE, OP_RGATE, OP_RGATE2,
|
|
95
96
|
OP_LEX_BODY, OP_LEX_PROGRAM,
|
|
96
97
|
} from './ops.ts'
|
|
97
98
|
import { adjacencyHolds, adjacencyMisuse } from '../combinators/adjacency.ts'
|
|
@@ -110,6 +111,7 @@ import {
|
|
|
110
111
|
} from './emit-assembly.ts'
|
|
111
112
|
import { collapseEmpty, lead, rawEntry, spanLines } from './run-support.ts'
|
|
112
113
|
import { holdsGap, rowNullable } from './hold-gap.ts'
|
|
114
|
+
import { gapRows, newGapSlot } from './gap-gate.ts'
|
|
113
115
|
import { failCleanAnalysis } from './fail-clean.ts'
|
|
114
116
|
/**
|
|
115
117
|
* THE COMPLETIONS PROBE, at the terminal fail sites and nowhere else.
|
|
@@ -412,6 +414,28 @@ export type Assembly = {
|
|
|
412
414
|
* ONE walk. Each site is lowered at most once and memoised by its code offset,
|
|
413
415
|
* so a subtree shared by two parents is one piece with two references to it.
|
|
414
416
|
*/
|
|
417
|
+
/**
|
|
418
|
+
* A printed factory takes its helpers BY POSITION (`EMITTED_PARAMS`), so one printed
|
|
419
|
+
* against a different helper list would bind the wrong function to a name and run
|
|
420
|
+
* wrong rather than fail. A factory printed by 0.59.x guards only against FEWER
|
|
421
|
+
* helpers, and one printed before 0.59.0 has no guard at all. So the runtime checks
|
|
422
|
+
* too, before calling it: the assembly's printed `arity` must be exactly the list it
|
|
423
|
+
* passes. It reads that printed number, never `factory.length`, which a minifier that
|
|
424
|
+
* drops unused trailing parameters lowers on a valid artifact. An assembly printed
|
|
425
|
+
* before 0.60.0 carries no `arity` and is refused. A factory this version prints also
|
|
426
|
+
* checks the count it is called with (`FACTORY_ARITY_GUARD`, `emit.ts`).
|
|
427
|
+
*/
|
|
428
|
+
export function assertFactoryArity(pre: { readonly arity?: number }): void {
|
|
429
|
+
if (pre.arity === EMITTED_PARAMS.length) return
|
|
430
|
+
const printed = pre.arity === undefined
|
|
431
|
+
? 'carries no helper count, so it was printed before parseman 0.60.0'
|
|
432
|
+
: `takes ${pre.arity} helpers, but parseman ${PARSEMAN_VERSION} passes ${EMITTED_PARAMS.length}: it was printed by an older or newer parseman`
|
|
433
|
+
throw new Error(
|
|
434
|
+
`parseman: this parser's printed assembly ${printed}. Reprint the parser with `
|
|
435
|
+
+ `parseman ${PARSEMAN_VERSION}, or run it on the parseman version that printed it.`,
|
|
436
|
+
)
|
|
437
|
+
}
|
|
438
|
+
|
|
415
439
|
export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Assembly {
|
|
416
440
|
const { code, k, fns, cc, fx, disp, dsp, trivia, triviaLabelled } = t
|
|
417
441
|
// The leaf swap is only legal when line tracking is off, and that is a property
|
|
@@ -502,6 +526,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
502
526
|
}
|
|
503
527
|
|
|
504
528
|
const labelExtraIps = assemblyExtraIps(prog)
|
|
529
|
+
// The regex rows an optional repeat hands a scanned gap to (`gap-gate.ts`),
|
|
530
|
+
// and the one slot it hands it through, shared with the emitted pieces.
|
|
531
|
+
const GAP_ROWS = gapRows(code, [...Object.values(prog.rules), ...labelExtraIps])
|
|
532
|
+
const GAP = newGapSlot()
|
|
505
533
|
const closureLabels = computeSiteLabels(
|
|
506
534
|
code, [...Object.values(prog.rules), ...labelExtraIps], hostCst,
|
|
507
535
|
)
|
|
@@ -772,6 +800,32 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
772
800
|
}
|
|
773
801
|
}
|
|
774
802
|
|
|
803
|
+
/**
|
|
804
|
+
* A regex row a repeat may hand a scanned gap to (`gap-gate.ts`): the end comes
|
|
805
|
+
* from the slot when the slot is this row's, at this position, in this input,
|
|
806
|
+
* and from the regex otherwise. Everything after the end is the row's own.
|
|
807
|
+
*/
|
|
808
|
+
function gapRegexPiece(row: number, re: RegExp, xf: string[], track: boolean): Piece {
|
|
809
|
+
return (input, pos, ctx) => {
|
|
810
|
+
let e: number
|
|
811
|
+
if (GAP.row === row && GAP.at === pos && GAP.input === input) e = GAP.end
|
|
812
|
+
else {
|
|
813
|
+
re.lastIndex = pos
|
|
814
|
+
e = re.test(input) ? re.lastIndex : -1
|
|
815
|
+
}
|
|
816
|
+
if (e >= 0) {
|
|
817
|
+
const v = input.slice(pos, e)
|
|
818
|
+
if (cstCaptureActive(ctx)) pushLeaf(ctx, v, pos, e)
|
|
819
|
+
if (track) trackLinesInto(ctx, input, e)
|
|
820
|
+
EC.e = e
|
|
821
|
+
return v
|
|
822
|
+
}
|
|
823
|
+
ctx._fe = pos; ctx._fx = xf
|
|
824
|
+
if (ctx._probe !== undefined) failAt(ctx, xf, pos)
|
|
825
|
+
return FAIL
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
775
829
|
/**
|
|
776
830
|
* A collapse over one node is that node — `run-support.ts` `collapseEmpty` has
|
|
777
831
|
* the contract, and the one case the node's value cannot stand for itself.
|
|
@@ -1353,7 +1407,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1353
1407
|
}
|
|
1354
1408
|
}
|
|
1355
1409
|
|
|
1356
|
-
function dispatchBranch(child: Piece, usesRouted: boolean): DispatchBranch {
|
|
1410
|
+
function dispatchBranch(child: Piece, usesRouted: boolean, commits = true): DispatchBranch {
|
|
1357
1411
|
if (!usesRouted) {
|
|
1358
1412
|
// The arm runs after the selector's match, so it marks where it starts.
|
|
1359
1413
|
return (input, _pos, selEnd, key, ctx) => {
|
|
@@ -1368,7 +1422,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1368
1422
|
const v = child(input, selEnd, ctx)
|
|
1369
1423
|
if (v === FAIL) {
|
|
1370
1424
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1371
|
-
ctx._fc = true
|
|
1425
|
+
if (commits) ctx._fc = true
|
|
1372
1426
|
return FAIL
|
|
1373
1427
|
}
|
|
1374
1428
|
return [key, v]
|
|
@@ -1388,7 +1442,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1388
1442
|
}
|
|
1389
1443
|
if (v === FAIL) {
|
|
1390
1444
|
if (need) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
1391
|
-
ctx._fc = true
|
|
1445
|
+
if (commits) ctx._fc = true
|
|
1392
1446
|
return FAIL
|
|
1393
1447
|
}
|
|
1394
1448
|
return [key, v]
|
|
@@ -1662,6 +1716,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1662
1716
|
case OP_RX: {
|
|
1663
1717
|
const re = k[code[ip + 1]!] as RegExp
|
|
1664
1718
|
const xf = fx[code[ip + 2]!] as string[]
|
|
1719
|
+
if (GAP_ROWS.has(ip)) return gapRegexPiece(ip, re, xf, false)
|
|
1665
1720
|
const shared = rawScalarSpecs.has(code[ip + 1]!) ? scalarFor(ip) : undefined
|
|
1666
1721
|
if (shared !== undefined) {
|
|
1667
1722
|
return (input, pos, ctx) => {
|
|
@@ -1700,6 +1755,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1700
1755
|
case OP_RX_TRACK: {
|
|
1701
1756
|
const re = k[code[ip + 1]!] as RegExp
|
|
1702
1757
|
const xf = fx[code[ip + 2]!] as string[]
|
|
1758
|
+
if (GAP_ROWS.has(ip)) return gapRegexPiece(ip, re, xf, true)
|
|
1703
1759
|
return (input, pos, ctx) => {
|
|
1704
1760
|
re.lastIndex = pos
|
|
1705
1761
|
if (re.test(input)) {
|
|
@@ -1877,6 +1933,14 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
1877
1933
|
}
|
|
1878
1934
|
}
|
|
1879
1935
|
|
|
1936
|
+
// Transparent: only the repeat holding it reads its operands.
|
|
1937
|
+
case OP_GAPGATE:
|
|
1938
|
+
return link(code[ip + 1]!)
|
|
1939
|
+
|
|
1940
|
+
// One encoding per assembly: strict, or the construct as written.
|
|
1941
|
+
case OP_STRICT:
|
|
1942
|
+
return link(code[cfg.tolerant || cfg.probe ? ip + 2 : ip + 1]!)
|
|
1943
|
+
|
|
1880
1944
|
case OP_RULE:
|
|
1881
1945
|
// Pure indirection in the tape; in the assembled graph the parent simply
|
|
1882
1946
|
// holds the target's piece. The trampoline row disappears entirely — this
|
|
@@ -2949,7 +3013,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2949
3013
|
// `arms[]` merely to rediscover the same two or three children at
|
|
2950
3014
|
// every dispatch. The classifier remains table data; only its result
|
|
2951
3015
|
// is turned into a direct captured call.
|
|
2952
|
-
if (n === 2) {
|
|
3016
|
+
if (n === 2 && !cfg.probe) {
|
|
2953
3017
|
const a0 = link(code[base]!), a1 = link(code[base + 1]!)
|
|
2954
3018
|
return (input, pos, ctx) => {
|
|
2955
3019
|
const c = lead(input, pos)
|
|
@@ -2974,7 +3038,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
2974
3038
|
return FAIL
|
|
2975
3039
|
}
|
|
2976
3040
|
}
|
|
2977
|
-
if (n === 3) {
|
|
3041
|
+
if (n === 3 && !cfg.probe) {
|
|
2978
3042
|
const a0 = link(code[base]!), a1 = link(code[base + 1]!), a2 = link(code[base + 2]!)
|
|
2979
3043
|
return (input, pos, ctx) => {
|
|
2980
3044
|
const c = lead(input, pos)
|
|
@@ -3007,6 +3071,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3007
3071
|
)
|
|
3008
3072
|
}
|
|
3009
3073
|
const selected = bindExclusive(0)
|
|
3074
|
+
// Under a completions probe the interpreter runs every arm on a miss
|
|
3075
|
+
// (`choice.ts`), so each opener reaches the probe.
|
|
3076
|
+
const probeArms = cfg.probe ? Array.from({ length: n }, (_, i) => link(code[base + i]!)) : undefined
|
|
3010
3077
|
// THERE ARE NO OPEN ARMS HERE, and the piece is written to say so. An
|
|
3011
3078
|
// ungated arm is one whose class is −1, and `resolveDispatch` clears
|
|
3012
3079
|
// `exclusive` for exactly those arms (program.ts) — so `table.open` is
|
|
@@ -3038,6 +3105,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3038
3105
|
// THE UNION IS REPORTED ONLY ON A DISPATCH MISS, where the interpreter's
|
|
3039
3106
|
// `parsers.flatMap` over non-nullable, char-excluded arms yields exactly
|
|
3040
3107
|
// each arm's own opener — this set, without running the arms.
|
|
3108
|
+
if (arm < 0 && probeArms !== undefined) for (const a of probeArms) a(input, pos, ctx)
|
|
3041
3109
|
ctx._fe = pos; ctx._fx = choiceFx
|
|
3042
3110
|
return FAIL
|
|
3043
3111
|
}
|
|
@@ -3045,8 +3113,9 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3045
3113
|
|
|
3046
3114
|
// THE PER-ARM GATE. Arms in source order, each skipped when its own class
|
|
3047
3115
|
// excludes the char at `pos`. `null` means nullable or unmappable, and
|
|
3048
|
-
// those arms are always entered.
|
|
3049
|
-
|
|
3116
|
+
// those arms are always entered. A probe assembly enters every arm, as
|
|
3117
|
+
// the interpreter's `firstMatch` does, so each opener reaches `failAt`.
|
|
3118
|
+
const armCls = cfg.probe ? table.armCls.map(() => null) : table.armCls
|
|
3050
3119
|
if (n === 2 || n === 3) {
|
|
3051
3120
|
const a0 = link(code[base]!), a1 = link(code[base + 1]!)
|
|
3052
3121
|
const a2 = n === 3 ? link(code[base + 2]!) : undefined
|
|
@@ -3354,6 +3423,43 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3354
3423
|
}
|
|
3355
3424
|
}
|
|
3356
3425
|
|
|
3426
|
+
case OP_RGATE2: {
|
|
3427
|
+
const cls = cc[code[ip + 1]!]!
|
|
3428
|
+
const xf = fx[code[ip + 3]!] as string[]
|
|
3429
|
+
const first = cc[code[ip + 4]!]!
|
|
3430
|
+
const second = cc[code[ip + 5]!]!
|
|
3431
|
+
const child = link(code[ip + 2]!)
|
|
3432
|
+
if (cfg.tolerant || cfg.probe) return child
|
|
3433
|
+
// ASCII-only classes are read as code units against their tables.
|
|
3434
|
+
if (cls.hi.length === 0 && first.hi.length === 0 && second.hi.length === 0) {
|
|
3435
|
+
const a0 = cls.ascii, a1 = first.ascii, a2 = second.ascii
|
|
3436
|
+
return (input, pos, ctx) => {
|
|
3437
|
+
const item = ctx._routed
|
|
3438
|
+
if (item !== undefined && item.span.start === pos) {
|
|
3439
|
+
const e = item.span.end
|
|
3440
|
+
const c = input.charCodeAt(e)
|
|
3441
|
+
if (a0[c] !== 1 || (a1[c] === 1 && a2[input.charCodeAt(e + 1)] !== 1)) {
|
|
3442
|
+
ctx._fe = e; ctx._fx = xf
|
|
3443
|
+
return FAIL
|
|
3444
|
+
}
|
|
3445
|
+
}
|
|
3446
|
+
return child(input, pos, ctx)
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
return (input, pos, ctx) => {
|
|
3450
|
+
const item = ctx._routed
|
|
3451
|
+
if (item !== undefined && item.span.start === pos) {
|
|
3452
|
+
const e = item.span.end
|
|
3453
|
+
const c = lead(input, e)
|
|
3454
|
+
if (!classHas(cls, c) || (classHas(first, c) && !classHas(second, lead(input, e + 1)))) {
|
|
3455
|
+
ctx._fe = e; ctx._fx = xf
|
|
3456
|
+
return FAIL
|
|
3457
|
+
}
|
|
3458
|
+
}
|
|
3459
|
+
return child(input, pos, ctx)
|
|
3460
|
+
}
|
|
3461
|
+
}
|
|
3462
|
+
|
|
3357
3463
|
case OP_ARMGATE: {
|
|
3358
3464
|
// Predicate, arm and expected set all bound HERE — the body reads no
|
|
3359
3465
|
// opcode, decodes no operand and tests no setting.
|
|
@@ -3450,6 +3556,19 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3450
3556
|
// `many()` — the min-0, separator-less repeat — is the only shape that runs
|
|
3451
3557
|
// its FIRST item through `repItem` and so skips leading trivia. The shape
|
|
3452
3558
|
// identifies itself, and it is table data, so it is decided here.
|
|
3559
|
+
// A separated list's ip+8 (flag bit 8) is its separator's first set where the encoder
|
|
3560
|
+
// found the test cheaper than entering it (`Encoder.sepGate`). Where the
|
|
3561
|
+
// separator cannot match empty and reads no pre-scan cursor, a lead
|
|
3562
|
+
// outside it stops the list at the separator, exactly as the strict
|
|
3563
|
+
// optional item stops at its own class above.
|
|
3564
|
+
const sepClassIndex = sepIp >= 0 && !sepLeads && (flags & 8) !== 0 ? code[ip + 8]! : -1
|
|
3565
|
+
const sepClass = !REC && sepClassIndex >= 0 && !rowNullable(code, k, sepIp, NULLABLE) ? cc[sepClassIndex]! : undefined
|
|
3566
|
+
// THE GAP GATE (`gap-gate.ts`): read the item's leading gap once, here.
|
|
3567
|
+
const gapIp = sepIp < 0 && min === 0 && !REC && code[code[ip + 1]!] === OP_GAPGATE ? code[ip + 1]! : -1
|
|
3568
|
+
const gapRow = gapIp >= 0 ? code[gapIp + 2]! : -1
|
|
3569
|
+
const gapRe = gapIp >= 0 ? k[code[gapRow + 1]!] as RegExp : undefined
|
|
3570
|
+
const gapFollow = gapIp >= 0 ? cc[code[gapIp + 3]!]! : undefined
|
|
3571
|
+
const gapOther = gapIp >= 0 && code[gapIp + 4]! >= 0 ? cc[code[gapIp + 4]!]! : undefined
|
|
3453
3572
|
const skipBeforeFirst = sepIp < 0 && min === 0
|
|
3454
3573
|
const itemClean = sepIp < 0 && CLEAN(code[ip + 1]!) && !rowNullable(code, k, code[ip + 1]!, NULLABLE)
|
|
3455
3574
|
|
|
@@ -3486,6 +3605,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3486
3605
|
*/
|
|
3487
3606
|
if (REC) {
|
|
3488
3607
|
const itemFx = fx[code[ip + 6]!] as string[]
|
|
3608
|
+
// A resync error carries the item's set as derived (bit 16: empty).
|
|
3609
|
+
const recFx = (flags & 16) !== 0 ? EMPTY_FX : itemFx
|
|
3489
3610
|
const sepSent = sentinelFor(code[ip + 7]!)
|
|
3490
3611
|
return (input, pos, ctx) => {
|
|
3491
3612
|
const out: unknown[] | undefined = collect ? [] : undefined
|
|
@@ -3546,7 +3667,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3546
3667
|
const rr = recoverScan(
|
|
3547
3668
|
input, itemStart, ctx,
|
|
3548
3669
|
sep === undefined ? mySync : orSentinel(sepSent ?? mySync, sepSent === undefined ? undefined : mySync),
|
|
3549
|
-
|
|
3670
|
+
recFx,
|
|
3550
3671
|
)
|
|
3551
3672
|
if (out !== undefined) out.push(rr.error)
|
|
3552
3673
|
captureError(ctx, rr.error)
|
|
@@ -3586,6 +3707,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3586
3707
|
// Keep the ordinary child path while one is active so this structural
|
|
3587
3708
|
// fast stop cannot erase an item completion at the cursor.
|
|
3588
3709
|
const gateItems = itemMayStart !== undefined && ctx._probe === undefined
|
|
3710
|
+
const gateSep = sepClass !== undefined && ctx._probe === undefined
|
|
3589
3711
|
let cur = pos
|
|
3590
3712
|
let count = 0
|
|
3591
3713
|
for (;;) {
|
|
@@ -3617,6 +3739,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3617
3739
|
const leavesBefore = cstLeavesLen(ctx)
|
|
3618
3740
|
let sp = cur
|
|
3619
3741
|
if (hasTrivia) sp = skipTrivia(input, sp, ctx)
|
|
3742
|
+
if (count >= min && gateSep && !classHas(sepClass!, lead(input, sp))) {
|
|
3743
|
+
if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3744
|
+
break
|
|
3745
|
+
}
|
|
3620
3746
|
ctx._fc = false
|
|
3621
3747
|
if (sepLeads) LEAD.at = cur
|
|
3622
3748
|
const sv = sep(input, sp, ctx)
|
|
@@ -3636,13 +3762,33 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3636
3762
|
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
3637
3763
|
break
|
|
3638
3764
|
}
|
|
3639
|
-
|
|
3765
|
+
// Without a separator or trivia in front, the loop head already
|
|
3766
|
+
// tested this same position.
|
|
3767
|
+
if (count >= min && gateItems && (sep !== undefined || hasTrivia) && !itemMayStart!(input, itemStart)) {
|
|
3640
3768
|
if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3641
3769
|
if (trailingAllowed && sepEnd >= 0) cur = sepEnd
|
|
3642
3770
|
break
|
|
3643
3771
|
}
|
|
3772
|
+
let gapped = false
|
|
3773
|
+
if (gapRe !== undefined && ctx._probe === undefined) {
|
|
3774
|
+
gapRe.lastIndex = itemStart
|
|
3775
|
+
const q = gapRe.test(input) ? gapRe.lastIndex : -1
|
|
3776
|
+
// No arm with the gap can match where the gap fails, or where the
|
|
3777
|
+
// character after it opens nothing; an arm without it needs its
|
|
3778
|
+
// own lead at the item's start.
|
|
3779
|
+
if ((q < 0 || !classHas(gapFollow!, lead(input, q)))
|
|
3780
|
+
&& (gapOther === undefined || !classHas(gapOther, lead(input, itemStart)))) {
|
|
3781
|
+
if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3782
|
+
break
|
|
3783
|
+
}
|
|
3784
|
+
if (q >= 0) {
|
|
3785
|
+
GAP.row = gapRow; GAP.at = itemStart; GAP.end = q; GAP.input = input
|
|
3786
|
+
gapped = true
|
|
3787
|
+
}
|
|
3788
|
+
}
|
|
3644
3789
|
ctx._fc = false
|
|
3645
3790
|
const v = child(input, itemStart, ctx)
|
|
3791
|
+
if (gapped) GAP.at = -1
|
|
3646
3792
|
if (v === FAIL) {
|
|
3647
3793
|
if (markIt) rollbackTriviaAt(ctx, mRaw, mTl, mLv, mFl, mEr, mLog, mRoot)
|
|
3648
3794
|
if (committed(ctx)) return FAIL
|
|
@@ -3675,7 +3821,8 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3675
3821
|
const selector = link(code[ip + 1]!)
|
|
3676
3822
|
const otherIp = code[ip + 3]!
|
|
3677
3823
|
const other = otherIp >= 0 ? link(otherIp) : undefined
|
|
3678
|
-
const otherRouted = code[ip + 4]! === 1
|
|
3824
|
+
const otherRouted = (code[ip + 4]! & 1) === 1
|
|
3825
|
+
const otherCommits = (code[ip + 4]! & 2) === 0
|
|
3679
3826
|
const n = code[ip + 5]!
|
|
3680
3827
|
const armBase = ip + 6
|
|
3681
3828
|
const byKey = spec.byKey
|
|
@@ -3722,7 +3869,7 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
3722
3869
|
)
|
|
3723
3870
|
}
|
|
3724
3871
|
const runArm = n === 0 ? undefined : bindArms(0)
|
|
3725
|
-
const runOther = other === undefined ? undefined : dispatchBranch(other, otherRouted)
|
|
3872
|
+
const runOther = other === undefined ? undefined : dispatchBranch(other, otherRouted, otherCommits)
|
|
3726
3873
|
|
|
3727
3874
|
return (input, pos, ctx) => {
|
|
3728
3875
|
const need = markCst(ctx)
|
|
@@ -4099,22 +4246,22 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
4099
4246
|
? prog.asm?.find(a => a.key === cfgKey(cfg))
|
|
4100
4247
|
: undefined
|
|
4101
4248
|
if (pre !== undefined) {
|
|
4249
|
+
assertFactoryArity(pre)
|
|
4102
4250
|
const pools = rebuildPools(t.cc, t.fx, t.disp, pre.plan)
|
|
4103
4251
|
emitted = pre.factory(
|
|
4104
|
-
EC, FAIL, k, fx, fns, pools.masks, pools.classes, pools.armExpected, trivia,
|
|
4105
|
-
trivia.map(tv => tv?._meta.triviaKindLabels), triviaScan,
|
|
4252
|
+
EC, FAIL, k, fx, fns, pools.masks, pools.classes, pools.armExpected, trivia, triviaScan,
|
|
4106
4253
|
scansArr, disp, dsp, EMPTY_FX, EMPTY_CH, EMPTY_TLOG, EMPTY_TL,
|
|
4107
4254
|
cstCaptureActive, pushCstLeaf, pushCstChild, rollbackTriviaAt, rollbackScannedTriviaAt, failAt,
|
|
4108
4255
|
classHas, consumeTrivia, buildFieldMap, projectChild, unwrapChild,
|
|
4109
4256
|
demoteCapturedToRaw, cstLeavesLen, skipTriviaScanned, needsDeferredTriviaCommit,
|
|
4110
|
-
|
|
4257
|
+
advanceTrivia, refuseUnclassifiedRootScope, spanLines, rawEntry, lead,
|
|
4111
4258
|
asciiFoldKey, ROUTED_FX,
|
|
4112
4259
|
REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
|
|
4113
4260
|
matchesAt, recoverScan, orSentinel, captureError,
|
|
4114
|
-
scalarRecognizers,
|
|
4261
|
+
scalarRecognizers, t.lex, adjacencyHolds, t.lexPrograms,
|
|
4115
4262
|
LEAD, triviaFieldCapture, skipDeferredTrivia,
|
|
4116
4263
|
collapseEmpty,
|
|
4117
|
-
PlainSpan, PlainLeaf, PlainNode, PlainField, PlainBuf,
|
|
4264
|
+
PlainSpan, PlainLeaf, PlainNode, PlainField, PlainBuf, GAP,
|
|
4118
4265
|
)
|
|
4119
4266
|
emitReached = new Set(pre.reached)
|
|
4120
4267
|
if (emitted.ext !== undefined) HYBRID = emitted
|
|
@@ -4181,13 +4328,12 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
4181
4328
|
throw new Unemittable(`the Function constructor (${String(e)})`)
|
|
4182
4329
|
}
|
|
4183
4330
|
emitted = factory(
|
|
4184
|
-
EC, FAIL, k, fx, fns, em.masks, em.classes, em.armExpected, trivia,
|
|
4185
|
-
trivia.map(tv => tv?._meta.triviaKindLabels), triviaScan,
|
|
4331
|
+
EC, FAIL, k, fx, fns, em.masks, em.classes, em.armExpected, trivia, triviaScan,
|
|
4186
4332
|
scansArr, disp, dsp, EMPTY_FX, EMPTY_CH, EMPTY_TLOG, EMPTY_TL,
|
|
4187
4333
|
cstCaptureActive, pushCstLeaf, pushCstChild, rollbackTriviaAt, rollbackScannedTriviaAt, failAt,
|
|
4188
4334
|
classHas, consumeTrivia, buildFieldMap, projectChild, unwrapChild,
|
|
4189
4335
|
demoteCapturedToRaw, cstLeavesLen, skipTriviaScanned, needsDeferredTriviaCommit,
|
|
4190
|
-
|
|
4336
|
+
advanceTrivia, refuseUnclassifiedRootScope, spanLines, rawEntry, lead,
|
|
4191
4337
|
asciiFoldKey, ROUTED_FX,
|
|
4192
4338
|
// THE SENTINEL POOL, dense over char-class indices so the emitted text can
|
|
4193
4339
|
// index it the way it indexes `SCANS`. Built only for a recovery assembly
|
|
@@ -4196,10 +4342,10 @@ export function assemble(t: ResolvedTable, prog: TableProgram, cfg: RunCfg): Ass
|
|
|
4196
4342
|
// object rather than to two separately constructed ones.
|
|
4197
4343
|
REC ? prog.cc.map((_, i) => sentinelFor(i)) : EMPTY_SENTS,
|
|
4198
4344
|
matchesAt, recoverScan, orSentinel, captureError,
|
|
4199
|
-
scalarRecognizers,
|
|
4345
|
+
scalarRecognizers, t.lex, adjacencyHolds, t.lexPrograms,
|
|
4200
4346
|
LEAD, triviaFieldCapture, skipDeferredTrivia,
|
|
4201
4347
|
collapseEmpty,
|
|
4202
|
-
PlainSpan, PlainLeaf, PlainNode, PlainField, PlainBuf,
|
|
4348
|
+
PlainSpan, PlainLeaf, PlainNode, PlainField, PlainBuf, GAP,
|
|
4203
4349
|
)
|
|
4204
4350
|
emitReached = em.reached
|
|
4205
4351
|
} catch (e) {
|