/** * SHAPE RECOGNITION AND SOURCE EMISSION FOR SCANNABLE REGEXES. * * Restored from `archive/codegen-fastpaths:src/compiler/scannable-run.ts` and * re-aimed at `emit-assembly.ts`. It is a SOURCE emitter, not a runtime * interpreter: `emitShapeMatch` constant-folds every range and code point into * the text of a per-site `_pf` body via `classCond`/`litCond`/`foldEq`. Built * instead as static runtime bodies the nine shapes would reproduce exactly the * `inRanges` per-character loop this file exists to remove. * * The two trivia-loop emitters (`scanBranch`, `scanBranchLabeled`) are NOT * restored: fusing the trivia path into piece bodies is a separate unit, and * carrying dead emitters here would make this file look like it already did it. * * Structural recognition of "scannable" parser arms — regex shapes that lower to * a tight character-scan loop instead of `regex.exec` / combinator dispatch. Each * shape is derived from the regex STRUCTURE, not from any hardcoded knowledge * that a given regex "is whitespace" or "is a comment": * * [X]+ / [X]* → run while the char ∈ X (chars) * [^X]* → consume , run until char ∈ X (until) * (?:…)* → consume , run to literal (delimited) * x?<[X]*>… → a general linear chain of lit/run (seq) * * `seq` is the CATEGORY generalization: any chain of literal segments (required * or optional `x?`) and char-class runs (positive/negated, `?`/`*`/`+`). It * subsumes CSS/Less `-?ident`, `--custom`, `@-?keyword`, `[^…]+`, `::?`, etc. * without hardcoding a single byte — lowered only when a greedy one-pass scan * provably equals the engine's backtracking (see `seqIsUnambiguous`). * * A `oneOrMore(choice(a, b, …))` where every arm is one of these compiles to a * single char-dispatch loop with one branch per arm — any count/order, because * the shapes dispatch on their first 1–2 chars and are checked in turn. Trivia * (whitespace + comments) is just the value-discarded instance of this; nothing * here is trivia-specific. */ /** * One segment of a `seq` shape. A `seq` is the general category "a fixed linear * chain of literals and char-class runs" — no alternation, no groups, no * backtracking. It subsumes every CSS/Less token that is "(optional prefix)? * literal* char-run* …" without hardcoding any particular byte: * * lit — a run of fixed code points, optionally present (`x?`) or required. * run — a char-class run: positive/negated ranges, min 0/1, bounded to one * char (`[x]`, `[x]?`) or unbounded (`[x]*`, `[x]+`). * group — a non-capturing sub-pattern `(?:…)` (§8f), quantified the same way * as `run` (`min`/`unbounded`). `inner` is any already-recognized * `ScanShape` for the group's body (so a group can itself contain * `seq`/`chars`/`alt`/… — including a nested alternation via §8e). * Only used when `groupInnerSafe(inner)` holds (see that function). * * We only lower a `seq` when greedy left-to-right scanning provably equals the * regex engine's backtracking match (see `seqIsUnambiguous`). */ export type SeqPart = { part: 'lit'; cps: number[]; optional: boolean; } | { part: 'run'; ranges: Array<[number, number]>; negated: boolean; min: number; max: number; } | { part: 'group'; inner: ScanShape; min: 0 | 1; unbounded: boolean; }; export type ScanShape = { kind: 'chars'; ranges: Array<[number, number]>; minOne: boolean; } | { kind: 'ident'; head: Array<[number, number]>; tail: Array<[number, number]>; } | { kind: 'until'; open: number[]; stop: Array<[number, number]>; } | { kind: 'delimited'; open: number[]; close: number[]; } | { kind: 'string'; quote: number; excluded: Array<[number, number]>; escLineTerm: boolean; } | { kind: 'seq'; parts: SeqPart[]; } | { kind: 'litFold'; open: number[]; } | { kind: 'lookahead'; inner: ScanShape; ranges: Array<[number, number]>; classNegated: boolean; negative: boolean; } | { kind: 'alt'; arms: ScanShape[]; disjoint: boolean; firsts: Array; }; /** Backslash (`\`) code point — the escape lead char in string shapes. */ export declare const BACKSLASH = 92; import { SPACE_RANGES, parseClassRanges } from '../regex/classes.ts'; export { SPACE_RANGES, parseClassRanges }; /** ASCII case-insensitive letter check for `i`-flag literal lowering. */ export declare const foldEq: (cVar: string, cp: number) => string; type CharSet = { ranges: Array<[number, number]>; negated: boolean; }; /** * Recognize one scannable arm from its regex source, or null if it isn't one of * the structural shapes. Top-level alternation (§8e) is tried FIRST — `|` has * the lowest precedence in regex, so splitting on it happens before anything * else, with each arm then recursively re-entering this same function (so an * arm's own trailing lookahead, or its own nested `(?:…)`-wrapped alternation, * is still recognized). Failing that: char-class run, ident, string, then * open-until-terminator, delimited, and finally the general linear `seq` chain. * A trailing `(?!class)`/`(?=class)` is peeled off next (§8b) and re-wraps * whatever shape the remaining base recognizes as. */ export declare function parseScanShape(source: string): ScanShape | null; /** * Flag-aware wrapper around `parseScanShape`. Lowering to a raw code-point scan * assumes default regex semantics, so any flag that changes matching (`i` case * folding, `u` surrogate handling, `m`/`s` anchor/dot behavior) disables it. * `g`/`y` are stickiness-only and safe. */ export declare function scanShapeFromRegex(source: string, flags: string): ScanShape | null; export declare const classCond: (cVar: string, ranges: Array<[number, number]>) => string; /** Literal-match condition at `base + k` for each code point; uses `firstVar` at offset 0. */ export declare const litCond: (base: string, cps: number[], firstVar?: string) => string; /** Line-terminator code points `.` does not match (`\n \r \u2028 \u2029`). */ export declare const LINE_TERMINATORS: readonly [10, 13, 8232, 8233]; /** The body-stop chars that abort a string match (excluded set minus quote/backslash). */ export declare function stringHardStop(shape: Extract): Array<[number, number]>; /** Mints a fresh, unique local variable name (`prefix` + counter). */ export type Mint = (prefix?: string) => string; /** * The SINGLE source of truth for how a scannable shape matches at `start`. Both * the terminal emitter and the trivia scan loop consume this, so no context can * silently reinterpret an incomplete match (e.g. an unterminated string): * * - `setup` statements (indented by `ind`) that compute the match. * - `ok` a boolean expr: did a token match at `start`? (zero-width for * `chars*` counts as a match — terminals allow it.) * - `end` the position AFTER the token. **Invariant:** `end === start` * whenever there is no progress (match failed or matched empty), * so the trivia loop can gate purely on `end > start`. * * `firstChar`, when supplied, is an expression already equal to * `charCodeAt(start)` (the trivia loop reads it once and shares it). */ export type ShapeMatch = { setup: string[]; ok: string; end: string; }; export declare function emitShapeMatch(shape: ScanShape, start: string, mint: Mint, ind: string, firstChar?: string): ShapeMatch; //# sourceMappingURL=scan-shapes.d.ts.map