parseman 0.59.2 → 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/commitment.d.ts +1 -1
- package/dist/analysis/diagnostics.js +877 -191
- package/dist/analysis/diagnostics.js.map +3 -3
- package/dist/cli/index.js +877 -191
- package/dist/cli/index.js.map +3 -3
- package/dist/combinators/completions.d.ts +3 -3
- package/dist/combinators/completions.d.ts.map +1 -1
- 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/gate.d.ts +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/compile-linkable-table.d.ts +2 -3
- package/dist/compiler/compile-linkable-table.d.ts.map +1 -1
- package/dist/compiler/linker.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/cst/host-mode.d.ts +1 -1
- package/dist/cst/root-trivia-scope.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +897 -200
- package/dist/index.js.map +3 -3
- package/dist/language-service/index.d.ts +1 -1
- package/dist/language-service/index.js.map +1 -1
- package/dist/plugin/entry-supercompiler.d.ts.map +1 -1
- package/dist/plugin/evaluator.d.ts.map +1 -1
- package/dist/plugin/index.d.ts +3 -7
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +904 -202
- 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/spec/index.js.map +1 -1
- package/dist/table/assemble.d.ts +16 -3
- 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 +3 -2
- package/dist/table/compile-rule-map.d.ts.map +1 -1
- package/dist/table/compile.d.ts +2 -2
- 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 +8 -8
- package/dist/table/emit.d.ts.map +1 -1
- package/dist/table/encode.d.ts +6 -7
- 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 +927 -202
- package/dist/table/index.js.map +3 -3
- package/dist/table/lex-program.d.ts.map +1 -1
- package/dist/table/ops.d.ts +48 -20
- package/dist/table/ops.d.ts.map +1 -1
- package/dist/table/program.d.ts +13 -8
- package/dist/table/program.d.ts.map +1 -1
- package/dist/types.d.ts +17 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +6 -6
- package/package.json +1 -1
- package/src/analysis/commitment.ts +1 -1
- package/src/analysis/duplication.ts +1 -1
- package/src/analysis/terminal.ts +1 -1
- package/src/combinators/attempt.ts +1 -1
- package/src/combinators/choice.ts +1 -1
- package/src/combinators/completions.ts +3 -3
- package/src/combinators/dispatch.ts +30 -6
- package/src/combinators/first-set.ts +55 -12
- package/src/combinators/gate.ts +1 -1
- 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/compile-linkable-table.ts +2 -3
- package/src/compiler/degradation.ts +2 -2
- package/src/compiler/ir-serialize.ts +1 -1
- package/src/compiler/linker.ts +5 -3
- package/src/compiler/token-alphabet.ts +3 -1
- package/src/coverage.ts +1 -1
- package/src/cst/capture-buffer.ts +2 -2
- package/src/cst/host-mode.ts +1 -1
- package/src/cst/root-trivia-scope.ts +1 -1
- package/src/index.ts +1 -1
- package/src/language-service/index.ts +1 -1
- package/src/plugin/entry-supercompiler.ts +2 -1
- package/src/plugin/evaluator.ts +8 -3
- package/src/plugin/index.ts +11 -16
- package/src/regex/first-set.ts +37 -3
- package/src/table/assemble.ts +180 -35
- package/src/table/child-slots.ts +8 -3
- package/src/table/compile-rule-map.ts +10 -6
- package/src/table/compile.ts +3 -3
- package/src/table/emit-assembly.ts +186 -35
- package/src/table/emit.ts +19 -18
- package/src/table/encode.ts +482 -88
- package/src/table/exec.ts +42 -18
- 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 +51 -20
- package/src/table/program.ts +14 -9
- package/src/table/select-hot.ts +7 -2
- package/src/table/stamp.ts +1 -1
- package/src/types.ts +13 -6
- package/src/version.ts +6 -6
|
@@ -47,11 +47,11 @@
|
|
|
47
47
|
import type { ParseContext } from '../types.ts'
|
|
48
48
|
import { regexCanMatchEmpty } from '../regex/first-set.ts'
|
|
49
49
|
import {
|
|
50
|
-
OP_ADJ, OP_ARMGATE, OP_RGATE, OP_ATTEMPT, OP_CHOICE, OP_DISPATCH, OP_EMPTY, OP_EXPECT, OP_FIELD, OP_GATE,
|
|
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
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,
|
|
54
|
-
OP_LEX_BODY, OP_LEX_PROGRAM,
|
|
54
|
+
OP_LEX_BODY, OP_LEX_PROGRAM, OP_GAPGATE, OP_STRICT,
|
|
55
55
|
} from './ops.ts'
|
|
56
56
|
import {
|
|
57
57
|
choiceCannotCommit, choiceRollbackMask, failureRollbackClean, validateDispatchSpec,
|
|
@@ -67,10 +67,16 @@ import {
|
|
|
67
67
|
scalarTerminalNodeChild, scalarTerminalNotChild, scalarTerminalPeekChild, type LeadingScalarSequence,
|
|
68
68
|
} from './scalar-terminal.ts'
|
|
69
69
|
import { childSlots } from './child-slots.ts'
|
|
70
|
-
import { holdsGap } from './hold-gap.ts'
|
|
70
|
+
import { holdsGap, rowNullable } from './hold-gap.ts'
|
|
71
|
+
import { gapRows } from './gap-gate.ts'
|
|
71
72
|
import { fieldSpec, leadingTriviaFieldAt } from './field-spec.ts'
|
|
72
73
|
import { FIELD_LEADING_TRIVIA, FIELD_TRAILING_TRIVIA } from '../combinators/field-trivia.ts'
|
|
73
74
|
|
|
75
|
+
/** Does a gap's regex lower to an inline scan (`scanShapeFromRegex`)? */
|
|
76
|
+
function gapRegexLowers(re: unknown): boolean {
|
|
77
|
+
return re instanceof RegExp && scanShapeFromRegex(re.source, re.flags) !== null
|
|
78
|
+
}
|
|
79
|
+
|
|
74
80
|
/** What the compiled factory hands back — the emitted twin of `Assembly`. */
|
|
75
81
|
export type EmittedPiece = (input: string, pos: number, ctx: ParseContext) => unknown
|
|
76
82
|
export type EmittedAssembly = {
|
|
@@ -109,12 +115,12 @@ export class Unemittable extends Error {
|
|
|
109
115
|
* runs and is wrong, which no type in this file would catch.
|
|
110
116
|
*/
|
|
111
117
|
export const EMITTED_PARAMS = [
|
|
112
|
-
'EC', 'FAIL', 'K', 'FX', 'FNS', 'MASK', 'CLS', 'AFX', 'TRIVIA', '
|
|
118
|
+
'EC', 'FAIL', 'K', 'FX', 'FNS', 'MASK', 'CLS', 'AFX', 'TRIVIA', 'TRIVIASCAN',
|
|
113
119
|
'SCANS', 'DISP', 'DSP', 'EMPTY_FX', 'EMPTY_CH', 'EMPTY_TLOG', 'EMPTY_TL',
|
|
114
120
|
'cstCaptureActive', 'pushCstLeaf', 'pushCstChild', 'rollbackTriviaAt', 'rollbackScannedTriviaAt', 'failAt',
|
|
115
121
|
'classHas', 'consumeTrivia', 'buildFieldMap', 'projectChild', 'unwrapChild',
|
|
116
122
|
'demoteCapturedToRaw', 'cstLeavesLen', 'skipTriviaScanned', 'needsDeferredTriviaCommit',
|
|
117
|
-
'
|
|
123
|
+
'advanceTrivia', 'refuseUnclassifiedRootScope', 'spanLines', 'rawEntry', 'lead',
|
|
118
124
|
'asciiFoldKey', 'ROUTED_FX',
|
|
119
125
|
// RECOVERY, bound rather than reimplemented. `SENTS` is the sync sentinel per
|
|
120
126
|
// char-class index — the emitted twin of `assemble.ts`'s `sentinelFor` memo,
|
|
@@ -124,27 +130,24 @@ export const EMITTED_PARAMS = [
|
|
|
124
130
|
// engine and cannot drift.
|
|
125
131
|
'SENTS', 'matchesAt', 'recoverScan', 'orSentinel', 'captureError',
|
|
126
132
|
// Pure scalar terminal recognizers, indexed by the terminal's existing const
|
|
127
|
-
// operand
|
|
128
|
-
'RECOG',
|
|
129
|
-
//
|
|
130
|
-
// positional helper binding. Older factories ignore this trailing argument.
|
|
131
|
-
'commitTriviaScan', 'scanTriviaCompact', 'LEX',
|
|
132
|
-
// Appended for old precompiled-factory ABI: old factories ignore it.
|
|
133
|
-
'adjacencyHolds',
|
|
134
|
-
// Appended for selected composite lexical programs; old factories ignore it.
|
|
133
|
+
// operand; selected lexical bodies; the adjacency test.
|
|
134
|
+
'RECOG', 'LEX', 'adjacencyHolds',
|
|
135
|
+
// Selected composite lexical programs.
|
|
135
136
|
'LEXPROG',
|
|
136
|
-
//
|
|
137
|
-
// old factories ignore them.
|
|
137
|
+
// Trivia-attributing fields (`field(…, { leadingTrivia })`).
|
|
138
138
|
'LEAD', 'triviaFieldCapture',
|
|
139
|
-
//
|
|
140
|
-
// factories ignore it.
|
|
139
|
+
// The deferred-commit trivia skip (`trivia-gap.ts`).
|
|
141
140
|
'skipDeferredTrivia',
|
|
142
|
-
//
|
|
143
|
-
// ignore it.
|
|
141
|
+
// The pass-through collapse (`run-support.ts`).
|
|
144
142
|
'collapseEmpty',
|
|
145
|
-
//
|
|
146
|
-
//
|
|
143
|
+
// Constructor-built tree objects (`cst/plain.d.ts`): a `new` call has no
|
|
144
|
+
// allocation site, so V8 cannot pretenure them.
|
|
147
145
|
'_SP', '_LF', '_ND', '_FC', '_BUF',
|
|
146
|
+
// The gap gate's slot (`gap-gate.ts`).
|
|
147
|
+
'GAP',
|
|
148
|
+
// Artifacts are version-locked (docs/design/artifact-format.md): a factory is
|
|
149
|
+
// only ever called by the runtime that emitted it, so a name nothing emitted
|
|
150
|
+
// reads is removed rather than kept for an older factory's positions.
|
|
148
151
|
] as const
|
|
149
152
|
|
|
150
153
|
/**
|
|
@@ -605,6 +608,12 @@ export function emitAssemblySource(
|
|
|
605
608
|
* then emitted with them.
|
|
606
609
|
*/
|
|
607
610
|
scanSinkNeutral = false,
|
|
611
|
+
/**
|
|
612
|
+
* Lower every gap gate (`gap-gate.ts`) as the item it wraps. A selection is
|
|
613
|
+
* ranked on this lowering, so adding the gates moves no site's place in the
|
|
614
|
+
* order; the fit is judged by the source that ships, gates included.
|
|
615
|
+
*/
|
|
616
|
+
gapNeutral = false,
|
|
608
617
|
): EmitResult {
|
|
609
618
|
const { code, k, fx, disp, dsp, triviaLabelled } = t
|
|
610
619
|
const swapLegal = !cfg.trackLines
|
|
@@ -643,6 +652,10 @@ export function emitAssemblySource(
|
|
|
643
652
|
// emitted scope supplies a context this pass cannot see.
|
|
644
653
|
const roots = [...Object.values(prog.rules), ...extraIps]
|
|
645
654
|
const reachable = [...reachableSites(code, roots)]
|
|
655
|
+
// A gap whose regex lowers to an inline scan is scanned again by its row, for
|
|
656
|
+
// less than reading the slot costs; only a gap matched by its RegExp takes the
|
|
657
|
+
// slot (`gapRegexLowers`).
|
|
658
|
+
const GAP_ROWS = gapNeutral ? new Set<number>() : new Set([...gapRows(code, roots)].filter(row => !gapRegexLowers(k[code[row + 1]!])))
|
|
646
659
|
const labels = computeSiteLabels(code, roots, hostCst)
|
|
647
660
|
// Eligibility is pooled by the terminal's existing constant operand, matching
|
|
648
661
|
// the closure recognizer pool: distinct terminal rows sharing one spec share
|
|
@@ -807,7 +820,7 @@ export function emitAssemblySource(
|
|
|
807
820
|
const armExpectedPlan: number[][] = []
|
|
808
821
|
const maskPlan: number[] = []
|
|
809
822
|
|
|
810
|
-
/**
|
|
823
|
+
/** Hoisted pools, in the source lowering's spelling: `_k<N>` / `_fx<N>` / `_fn<N>`. */
|
|
811
824
|
function hoist(kind: string, expr: string): string {
|
|
812
825
|
const hit = pool.get(expr)
|
|
813
826
|
if (hit !== undefined) return hit
|
|
@@ -1172,7 +1185,8 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)\n` : ''}return FAIL
|
|
|
1172
1185
|
// `OP_GATE` under a probe or a tolerant recovery is a no-op that forwards to
|
|
1173
1186
|
// its child, exactly as `assemble.ts:952` resolves it.
|
|
1174
1187
|
if (op === OP_GATE && (cfg.tolerant || cfg.probe)) return code[ip + 2]!
|
|
1175
|
-
if (op === OP_RULE) return code[ip + 1]!
|
|
1188
|
+
if (op === OP_RULE || op === OP_GAPGATE) return code[ip + 1]!
|
|
1189
|
+
if (op === OP_STRICT) return code[cfg.tolerant || cfg.probe ? ip + 2 : ip + 1]!
|
|
1176
1190
|
// A SCOPE THAT INSTALLS WHAT IS ALREADY INSTALLED. `encode.ts:520` wraps
|
|
1177
1191
|
// EVERY rule of a `rules({ trivia }, …)` map in its own `OP_SCOPE_PLAIN`, so a
|
|
1178
1192
|
// grammar with one ambient trivia re-installs the same slot at every rule
|
|
@@ -1191,6 +1205,7 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)\n` : ''}return FAIL
|
|
|
1191
1205
|
function lower(ip: number, fname: string): string {
|
|
1192
1206
|
const op = code[ip]
|
|
1193
1207
|
const head = `function ${fname}(input,pos,ctx){`
|
|
1208
|
+
const outerHead = head
|
|
1194
1209
|
const L = labels.at(ip)
|
|
1195
1210
|
const sinks = sinksAt(ip)
|
|
1196
1211
|
/**
|
|
@@ -1310,6 +1325,26 @@ ${cfg.probe ? `failAt(ctx,${xf},pos)\n` : ''}return FAIL
|
|
|
1310
1325
|
case OP_RX_TRACK: {
|
|
1311
1326
|
const xf = fxRef(code[ip + 2]!)
|
|
1312
1327
|
const track = op === OP_RX_TRACK
|
|
1328
|
+
// A gap row takes the end a repeat already scanned (`gap-gate.ts`), and
|
|
1329
|
+
// otherwise matches its sticky regex: one compact body, no second copy of
|
|
1330
|
+
// the success path.
|
|
1331
|
+
if (GAP_ROWS.has(ip)) {
|
|
1332
|
+
const re = hoist('gx', `K[${code[ip + 1]!}]`)
|
|
1333
|
+
return `${outerHead}
|
|
1334
|
+
let e
|
|
1335
|
+
if(GAP.row===${ip}&&GAP.at===pos&&GAP.input===input)e=GAP.end
|
|
1336
|
+
else{${re}.lastIndex=pos;e=${re}.test(input)?${re}.lastIndex:-1}
|
|
1337
|
+
if(e>=0){
|
|
1338
|
+
const v=input.slice(pos,e)
|
|
1339
|
+
${captureLeaf('v')}
|
|
1340
|
+
${track ? '_trackLines(ctx,input,e)\n' : ''}EC.e=e
|
|
1341
|
+
return v
|
|
1342
|
+
}
|
|
1343
|
+
ctx._fe=pos;ctx._fx=${xf}
|
|
1344
|
+
${cfg.probe ? `failAt(ctx,${xf},pos)\n` : ''}return FAIL
|
|
1345
|
+
}`
|
|
1346
|
+
}
|
|
1347
|
+
const head = outerHead
|
|
1313
1348
|
if (!track && scalarSpecs.has(code[ip + 1]!)) {
|
|
1314
1349
|
const recognize = recognizerRef(code[ip + 1]!)
|
|
1315
1350
|
return `${head}
|
|
@@ -1401,6 +1436,31 @@ return ${child}(input,pos,ctx)
|
|
|
1401
1436
|
}`
|
|
1402
1437
|
}
|
|
1403
1438
|
|
|
1439
|
+
case OP_RGATE2: {
|
|
1440
|
+
const child = link(code[ip + 2]!)
|
|
1441
|
+
if (cfg.tolerant || cfg.probe) return `${head}
|
|
1442
|
+
return ${child}(input,pos,ctx)
|
|
1443
|
+
}`
|
|
1444
|
+
// ASCII-only classes (the usual case: `:` then whitespace) are read as
|
|
1445
|
+
// code units against their ASCII tables, with no call.
|
|
1446
|
+
const idxs = [code[ip + 1]!, code[ip + 4]!, code[ip + 5]!]
|
|
1447
|
+
const ascii = idxs.every(i => isAsciiOnlyClass(t.cc[i]!))
|
|
1448
|
+
const ref = (idx: number, tag: string): string => {
|
|
1449
|
+
const ci = classes.push([t.cc[idx]!]) - 1
|
|
1450
|
+
classPlan.push([idx])
|
|
1451
|
+
return hoist(tag, `CLS[${ci}][0]`)
|
|
1452
|
+
}
|
|
1453
|
+
const [cls, first, second] = [ref(idxs[0]!, 'cl'), ref(idxs[1]!, 'c1'), ref(idxs[2]!, 'c2')]
|
|
1454
|
+
const has = (c: string, at: string): string => ascii ? `${c}.ascii[${at}]===1` : `classHas(${c},${at})`
|
|
1455
|
+
const xf = fxRef(code[ip + 3]!)
|
|
1456
|
+
return `${head}
|
|
1457
|
+
const it=ctx._routed
|
|
1458
|
+
if(it!==undefined&&it.span.start===pos){const e=it.span.end;const c=${ascii ? 'input.charCodeAt(e)' : 'lead(input,e)'}
|
|
1459
|
+
if(!(${has(cls, 'c')})||(${has(first, 'c')}&&!(${has(second, ascii ? 'input.charCodeAt(e+1)' : 'lead(input,e+1)')}))){ctx._fe=e;ctx._fx=${xf};return FAIL}}
|
|
1460
|
+
return ${child}(input,pos,ctx)
|
|
1461
|
+
}`
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1404
1464
|
case OP_ARMGATE: {
|
|
1405
1465
|
// `choice({ gate, combinator })`, as `assemble.ts` lowers it. A blocked
|
|
1406
1466
|
// arm is SKIPPED, not failed (choice.ts `continue`): it clears the cut,
|
|
@@ -1489,8 +1549,8 @@ return v
|
|
|
1489
1549
|
const xf = fxRef(code[ip + 2]!)
|
|
1490
1550
|
// TWO ARMS, mirroring `assemble.ts:1050`. A TOLERANT `expect()` EMBEDS
|
|
1491
1551
|
// ITS ERROR IN THE TREE and not only in the flat `_errors` side-channel
|
|
1492
|
-
// (`combinators/expect.ts:150`,
|
|
1493
|
-
//
|
|
1552
|
+
// (`combinators/expect.ts:150`, the source lowering's
|
|
1553
|
+
// `_ctx._rec.capture`) — so a tree walk finds every diagnostic and the node
|
|
1494
1554
|
// survives incremental subtree reuse. `captureError` is the shared
|
|
1495
1555
|
// function, called; the embedding is not restated here.
|
|
1496
1556
|
//
|
|
@@ -1914,7 +1974,30 @@ if(ctx._fc===true)return FAIL
|
|
|
1914
1974
|
const af=ctx._fx
|
|
1915
1975
|
if(af!==undefined&&af.length>0){ctx._fe=pos;return FAIL}
|
|
1916
1976
|
}
|
|
1917
|
-
|
|
1977
|
+
${cfg.probe ? `if(arm<0){${arms.map(a => `${a}(input,pos,ctx)`).join(';')}}
|
|
1978
|
+
` : ''}ctx._fe=pos;ctx._fx=${choiceFx}
|
|
1979
|
+
return FAIL
|
|
1980
|
+
}`
|
|
1981
|
+
}
|
|
1982
|
+
|
|
1983
|
+
if (cfg.probe) {
|
|
1984
|
+
// A probe assembly enters every arm in source order, as the
|
|
1985
|
+
// interpreter's `firstMatch` does, so each opener reaches `failAt`.
|
|
1986
|
+
const p = tmp()
|
|
1987
|
+
return `${head}
|
|
1988
|
+
${emitMark(p, L.buf, L.raw, sinks)}
|
|
1989
|
+
let acc
|
|
1990
|
+
let best=pos
|
|
1991
|
+
${arms.map(arm => `{ctx._fc=false
|
|
1992
|
+
const v=${arm}(input,pos,ctx)
|
|
1993
|
+
if(v!==FAIL)return v
|
|
1994
|
+
const at=ctx._fe??pos
|
|
1995
|
+
if(at>best){best=at;acc=undefined}
|
|
1996
|
+
if(at===best)acc=_accSet(ctx._fx,acc)
|
|
1997
|
+
if(ctx._fc===true){if(acc!==undefined)ctx._fx=acc;return FAIL}
|
|
1998
|
+
${emitRollback(p, L.buf, L.raw, sinks)}
|
|
1999
|
+
}`).join('\n')}
|
|
2000
|
+
ctx._fe=pos;ctx._fx=acc??${choiceFx}
|
|
1918
2001
|
return FAIL
|
|
1919
2002
|
}`
|
|
1920
2003
|
}
|
|
@@ -2194,7 +2277,8 @@ return FAIL
|
|
|
2194
2277
|
const selector = link(code[ip + 1]!)
|
|
2195
2278
|
const otherIp = code[ip + 3]!
|
|
2196
2279
|
const other = otherIp >= 0 ? link(otherIp) : undefined
|
|
2197
|
-
const otherRouted = code[ip + 4]! === 1
|
|
2280
|
+
const otherRouted = (code[ip + 4]! & 1) === 1
|
|
2281
|
+
const otherCommits = (code[ip + 4]! & 2) === 0
|
|
2198
2282
|
const n = code[ip + 5]!
|
|
2199
2283
|
validateDispatchSpec(spec, n, code[ip + 4]!)
|
|
2200
2284
|
const armBase = ip + 6
|
|
@@ -2258,7 +2342,7 @@ ${fallbackCase}
|
|
|
2258
2342
|
}
|
|
2259
2343
|
if(v===FAIL){
|
|
2260
2344
|
${emitRollback(m2, L.buf, L.raw, sinks)}
|
|
2261
|
-
ctx._fc=true
|
|
2345
|
+
${other !== undefined && !otherCommits ? 'if(arm!==undefined)' : ''}ctx._fc=true
|
|
2262
2346
|
return FAIL
|
|
2263
2347
|
}
|
|
2264
2348
|
return [key,v]
|
|
@@ -2293,6 +2377,69 @@ return [key,v]
|
|
|
2293
2377
|
const itemMiss = (at: string): string => itemAscii
|
|
2294
2378
|
? `${itemCls}.ascii[input.charCodeAt(${at})]!==1`
|
|
2295
2379
|
: `!classHas(${itemCls},lead(input,${at}))`
|
|
2380
|
+
// The separator's own first set (ip+7, `assemble.ts` `sepClass`).
|
|
2381
|
+
let sepCls: string | undefined
|
|
2382
|
+
let sepAscii = false
|
|
2383
|
+
if (!REC && sepIp >= 0 && (code[ip + 5]! & 8) !== 0 && !leadingTriviaFieldAt(code, k, sepIp) && !rowNullable(code, k, sepIp, nullable)) {
|
|
2384
|
+
const resolved = t.cc[code[ip + 8]!]!
|
|
2385
|
+
const ci = classes.push([resolved]) - 1
|
|
2386
|
+
classPlan.push([code[ip + 8]!])
|
|
2387
|
+
sepCls = hoist('rs', `CLS[${ci}][0]`)
|
|
2388
|
+
sepAscii = isAsciiOnlyClass(resolved)
|
|
2389
|
+
}
|
|
2390
|
+
// THE GAP GATE (`gap-gate.ts`, `assemble.ts`): the item's leading gap is
|
|
2391
|
+
// read once here and handed to its regex row through the slot.
|
|
2392
|
+
const gapIp = !gapNeutral && sepIp < 0 && min === 0 && !REC && code[code[ip + 1]!] === OP_GAPGATE ? code[ip + 1]! : -1
|
|
2393
|
+
let gapCode = (_rb: string): string => ''
|
|
2394
|
+
if (gapIp >= 0) {
|
|
2395
|
+
const row = code[gapIp + 2]!
|
|
2396
|
+
const rx = k[code[row + 1]!]
|
|
2397
|
+
const shape = gapRegexLowers(rx) ? scanShapeFromRegex((rx as RegExp).source, (rx as RegExp).flags) : null
|
|
2398
|
+
const cls = (idx: number): string => {
|
|
2399
|
+
const resolved = t.cc[idx]!
|
|
2400
|
+
const ci = classes.push([resolved]) - 1
|
|
2401
|
+
classPlan.push([idx])
|
|
2402
|
+
const ref = hoist('gc', `CLS[${ci}][0]`)
|
|
2403
|
+
return isAsciiOnlyClass(resolved) ? `${ref}.ascii[input.charCodeAt(${'@'})]!==1` : `!classHas(${ref},lead(input,${'@'}))`
|
|
2404
|
+
}
|
|
2405
|
+
const follow = cls(code[gapIp + 3]!).replaceAll('@', 'gq')
|
|
2406
|
+
const other = code[gapIp + 4]! >= 0 ? cls(code[gapIp + 4]!).replaceAll('@', 'itemStart') : 'true'
|
|
2407
|
+
if (shape !== null) {
|
|
2408
|
+
// Scanned inline, as the row scans it: no slot to fill or read.
|
|
2409
|
+
let n = 0
|
|
2410
|
+
const q = tmp()
|
|
2411
|
+
const m = emitShapeMatch(shape, 'itemStart', (prefix = '_v') => `${q}${prefix}${n++}`, '')
|
|
2412
|
+
gapCode = (rbCode: string): string => `let gapped=false
|
|
2413
|
+
if(ctx._probe===undefined){
|
|
2414
|
+
${m.setup.join('\n')}
|
|
2415
|
+
const gq=${m.ok}?${m.end}:-1
|
|
2416
|
+
if((gq<0||${follow})&&${other}){
|
|
2417
|
+
${rbCode}
|
|
2418
|
+
break
|
|
2419
|
+
}
|
|
2420
|
+
}
|
|
2421
|
+
`
|
|
2422
|
+
} else {
|
|
2423
|
+
const re = hoist('gr', `K[${code[row + 1]!}]`)
|
|
2424
|
+
gapCode = (rbCode: string): string => `let gapped=false
|
|
2425
|
+
if(ctx._probe===undefined){
|
|
2426
|
+
${re}.lastIndex=itemStart
|
|
2427
|
+
const gq=${re}.test(input)?${re}.lastIndex:-1
|
|
2428
|
+
if((gq<0||${follow})&&${other}){
|
|
2429
|
+
${rbCode}
|
|
2430
|
+
break
|
|
2431
|
+
}
|
|
2432
|
+
if(gq>=0){
|
|
2433
|
+
GAP.row=${row};GAP.at=itemStart;GAP.end=gq;GAP.input=input
|
|
2434
|
+
gapped=true
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
`
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
const sepMiss = (at: string): string => sepAscii
|
|
2441
|
+
? `${sepCls}.ascii[input.charCodeAt(${at})]!==1`
|
|
2442
|
+
: `!classHas(${sepCls},lead(input,${at}))`
|
|
2296
2443
|
const skipBeforeFirst = sepIp < 0 && min === 0
|
|
2297
2444
|
const p = tmp()
|
|
2298
2445
|
// `viaRepItem` is `sep === undefined && count >= min && (count > 0 ||
|
|
@@ -2387,7 +2534,7 @@ ${p}rt=ctx._rootTriviaLog!==undefined?ctx._rootTriviaLog.length:0${markSinks}
|
|
|
2387
2534
|
// is taken unconditionally by the closure engine (`assemble.ts:2067`), so
|
|
2388
2535
|
// it is resolved separately from `itemFx` — reusing that one would hand
|
|
2389
2536
|
// `EMPTY_FX` to every recovered error in a grammar that does not report.
|
|
2390
|
-
const recFx = REC ? fxRef(code[ip + 6]!)
|
|
2537
|
+
const recFx = !REC ? '' : (flags & 16) !== 0 ? '[]' : fxRef(code[ip + 6]!)
|
|
2391
2538
|
const sepSent = REC ? sentRef(code[ip + 7]!) : 'undefined'
|
|
2392
2539
|
const recSent = sep === undefined
|
|
2393
2540
|
? my
|
|
@@ -2405,7 +2552,7 @@ continue
|
|
|
2405
2552
|
`
|
|
2406
2553
|
return `${head}
|
|
2407
2554
|
const out=${collect ? '[]' : 'undefined'}
|
|
2408
|
-
${knownTrivia === undefined ? 'const hasTrivia=ctx.trivia!==undefined\n' : ''}${L.buf ? '' : 'const needMark=_rollbackNeeded(ctx)\n'}${REC ? `const ${my}=ctx._sync\n` : ''}${itemCls === undefined ? '' : `const ${p}gate=ctx._probe===undefined\n`}let cur=pos
|
|
2555
|
+
${knownTrivia === undefined ? 'const hasTrivia=ctx.trivia!==undefined\n' : ''}${L.buf ? '' : 'const needMark=_rollbackNeeded(ctx)\n'}${REC ? `const ${my}=ctx._sync\n` : ''}${itemCls === undefined && sepCls === undefined ? '' : `const ${p}gate=ctx._probe===undefined\n`}let cur=pos
|
|
2409
2556
|
let count=0
|
|
2410
2557
|
for(;;){
|
|
2411
2558
|
${max >= 0 ? `if(count>=${max})break\n` : ''}${sep !== undefined ? `if(count>0&&count>=${min}&&cur>=input.length)break\n` : ''}${itemCls !== undefined && sep === undefined ? `if(count>=${min}&&${p}gate&&${hasTrivia === 'false' ? 'true' : hasTrivia === 'true' ? 'false' : '!hasTrivia'}&&${itemMiss('cur')})break\n` : ''}let ${p}raw=0,${p}tl=0,${p}lv=0,${p}lg=0,${p}rt=0${sinks.fd ? `,${p}fd=0` : ''}${sinks.er ? `,${p}er=0` : ''}
|
|
@@ -2415,7 +2562,11 @@ let sepEnd=-1
|
|
|
2415
2562
|
${sep !== undefined ? `if(count>0){
|
|
2416
2563
|
const lb=cstLeavesLen(ctx)
|
|
2417
2564
|
let sp=cur
|
|
2418
|
-
${hasTrivia === 'false' ? '' : hasTrivia === 'true' ? `sp=${skip}(input,sp,ctx)\n` : `if(hasTrivia)sp=${skip}(input,sp,ctx)\n`}
|
|
2565
|
+
${hasTrivia === 'false' ? '' : hasTrivia === 'true' ? `sp=${skip}(input,sp,ctx)\n` : `if(hasTrivia)sp=${skip}(input,sp,ctx)\n`}${sepCls === undefined ? '' : `if(count>=${min}&&${p}gate&&${sepMiss('sp')}){
|
|
2566
|
+
${rb}
|
|
2567
|
+
break
|
|
2568
|
+
}
|
|
2569
|
+
`}ctx._fc=false
|
|
2419
2570
|
${sepIp >= 0 && leadingTriviaFieldAt(code, k, sepIp) ? 'LEAD.at=cur\n' : ''}const sv=${sep}(input,sp,ctx)
|
|
2420
2571
|
if(sv===FAIL){
|
|
2421
2572
|
${rb}
|
|
@@ -2430,13 +2581,13 @@ itemStart=${hasTrivia === 'false' ? 'EC.e' : hasTrivia === 'true' ? `${skip}(inp
|
|
|
2430
2581
|
${rb}
|
|
2431
2582
|
${trailingAllowed ? 'if(sepEnd>=0)cur=sepEnd\n' : ''}break
|
|
2432
2583
|
}
|
|
2433
|
-
${itemCls === undefined ? '' : `if(count>=${min}&&${p}gate&&${itemMiss('itemStart')}){
|
|
2584
|
+
${itemCls === undefined || (sep === undefined && hasTrivia === 'false') ? '' : `if(count>=${min}&&${p}gate&&${sep === undefined && hasTrivia === 'hasTrivia' ? 'hasTrivia&&' : ''}${itemMiss('itemStart')}){
|
|
2434
2585
|
${rb}
|
|
2435
2586
|
${trailingAllowed ? 'if(sepEnd>=0)cur=sepEnd\n' : ''}break
|
|
2436
2587
|
}
|
|
2437
|
-
`}ctx._fc=false
|
|
2588
|
+
`}${gapCode(rb)}ctx._fc=false
|
|
2438
2589
|
const v=${child}(input,itemStart,ctx)
|
|
2439
|
-
if(v===FAIL){
|
|
2590
|
+
${gapIp >= 0 ? 'if(gapped)GAP.at=-1\n' : ''}if(v===FAIL){
|
|
2440
2591
|
${REC
|
|
2441
2592
|
// THE ROLLBACK MOVES INSIDE THE ARMS. The strict loop rolls back
|
|
2442
2593
|
// unconditionally before testing `_fc`, which is sound because every path out
|
|
@@ -2734,7 +2885,7 @@ return nd
|
|
|
2734
2885
|
const ruleEntries: string[] = []
|
|
2735
2886
|
for (const [rname, entryIp] of Object.entries(prog.rules)) {
|
|
2736
2887
|
if (!served(entryIp)) continue
|
|
2737
|
-
// `_r_<Name>` — the composition surface, in
|
|
2888
|
+
// `_r_<Name>` — the composition surface, in the source lowering's spelling and
|
|
2738
2889
|
// deliberately NOT namespaced, so a sibling calls it by name.
|
|
2739
2890
|
const target = link(entryIp)
|
|
2740
2891
|
const rn = `_r_${rname.replace(/[^A-Za-z0-9_$]/g, '_')}`
|
package/src/table/emit.ts
CHANGED
|
@@ -6,18 +6,20 @@ import { cfgKey, type RunCfg } from './assemble.ts'
|
|
|
6
6
|
import { PARSEMAN_VERSION } from '../version.ts'
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Opens every printed assembly factory.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* Opens every printed assembly factory. Helpers are bound BY POSITION, so a runtime that
|
|
10
|
+
* passes any other number of them, fewer or more, binds the wrong function to a name;
|
|
11
|
+
* refuse it when the assembly is built, instead of failing partway through the first parse
|
|
12
|
+
* on whichever helper is reached first. Runs once per assembly.
|
|
12
13
|
*/
|
|
13
|
-
export const FACTORY_ARITY_GUARD = `if(arguments.length
|
|
14
|
+
export const FACTORY_ARITY_GUARD = `if(arguments.length!==${EMITTED_PARAMS.length})throw new Error(${JSON.stringify(`parseman: this parser was printed by parseman ${PARSEMAN_VERSION}; run it on parseman ${PARSEMAN_VERSION} or reprint it`)});`
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Print a program as the module a build emits.
|
|
17
18
|
*
|
|
18
|
-
* Everything here is DATA plus the author's own reducers
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
* Everything here is DATA plus the author's own reducers, and any assemblies
|
|
20
|
+
* the build pre-compiled (`a:`). The recognizer is not in this string: it is
|
|
21
|
+
* `tableRules` from `parseman/table`, shared by every grammar in the bundle and
|
|
22
|
+
* by every variant of each.
|
|
21
23
|
*/
|
|
22
24
|
|
|
23
25
|
function jsString(s: string): string {
|
|
@@ -170,11 +172,10 @@ export function defaultAssemblyCfgs(prog: TableProgram): RunCfg[] {
|
|
|
170
172
|
* Print the assemblies a build pre-compiled, as the `a:` field of the program
|
|
171
173
|
* literal — see `TableProgram.asm`.
|
|
172
174
|
*
|
|
173
|
-
* THE FACTORY IS A REAL FUNCTION LITERAL.
|
|
174
|
-
*
|
|
175
|
-
* `
|
|
176
|
-
*
|
|
177
|
-
* artifact contract rather than recreate the old runtime/macro split.
|
|
175
|
+
* THE FACTORY IS A REAL FUNCTION LITERAL. A macro build emits one here for the
|
|
176
|
+
* AST, no-line-tracking option set, holding only the sites it selected within its
|
|
177
|
+
* budget (`compile-rule-map.ts`, `selectAssembly`); every other option set gets
|
|
178
|
+
* no entry and runs the closure assembler.
|
|
178
179
|
*
|
|
179
180
|
* A refusal is not an error. `Unemittable` names a construct the emitter does
|
|
180
181
|
* not lower; that option set simply gets no entry, and `assemble.ts` runs the
|
|
@@ -186,7 +187,7 @@ function emitAssemblies(prog: TableProgram, cfgs: readonly AssemblyCfg[]): strin
|
|
|
186
187
|
*
|
|
187
188
|
* The field's PRESENCE is the compiler artifact contract. That is what switches
|
|
188
189
|
* the runtime `Function` constructor off (`assemble.ts`), and it costs four
|
|
189
|
-
* bytes. Its CONTENTS are
|
|
190
|
+
* bytes. Its CONTENTS are the build's selected factories; with none, the
|
|
190
191
|
* artifact runs the closure engine — no eval, no
|
|
191
192
|
* size growth, and the refusal readable on `Assembly.emitRefusal`.
|
|
192
193
|
*
|
|
@@ -229,6 +230,7 @@ function emitAssemblies(prog: TableProgram, cfgs: readonly AssemblyCfg[]): strin
|
|
|
229
230
|
const plan = em.plan
|
|
230
231
|
out.push('{'
|
|
231
232
|
+ `key:${key},`
|
|
233
|
+
+ `arity:${EMITTED_PARAMS.length},`
|
|
232
234
|
+ `factory:function(${EMITTED_PARAMS.join(',')}){${FACTORY_ARITY_GUARD}${em.source}\n},`
|
|
233
235
|
+ 'plan:{'
|
|
234
236
|
+ `classes:[${plan.classes.map(r => `[${r.join(',')}]`).join(',')}],`
|
|
@@ -248,12 +250,11 @@ export type EmitOptions = {
|
|
|
248
250
|
/** Name of the exported binding. */
|
|
249
251
|
readonly name?: string
|
|
250
252
|
/**
|
|
251
|
-
*
|
|
253
|
+
* Emitted factories to pre-compile — see `defaultAssemblyCfgs`.
|
|
252
254
|
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
* second normal compilation path; it is not used by the macro plugin.
|
|
255
|
+
* Without them the artifact prints `a:[]`, the compact closure artifact, which
|
|
256
|
+
* never reaches the `Function` constructor. The macro plugin passes one
|
|
257
|
+
* selective assembly here (`compile-rule-map.ts`, `replacementWithMetadata`).
|
|
257
258
|
*/
|
|
258
259
|
readonly assemblies?: readonly AssemblyCfg[]
|
|
259
260
|
/**
|