import type { contains, ErrorMessage, noSuggest, NumberLiteral, setIndex, unionKeyOf, writeUnclosedGroupMessage, writeUnmatchedGroupCloseMessage, ZeroWidthSpace } from "@ark/util"; import type { writeUnresolvableBackreferenceMessage } from "./escape.ts"; import type { quantify, QuantifyingChar } from "./quantify.ts"; import type { Flags, IndexedCaptures, NamedCaptures, Regex, RegexContext } from "./regex.ts"; export interface State extends State.Group { unscanned: string; groups: State.Group[]; /** the initial flags passed to the root of the expression */ flags: Flags; } export declare namespace State { type from = s; type initialize = from<{ unscanned: source; groups: []; capture: never; branches: []; sequence: SequenceTree.Empty; root: ""; caseInsensitive: contains; flags: flags; }>; enum UnnamedCaptureKind { indexed, lookaround, noncapturing } type CaptureKind = string | UnnamedCaptureKind; type Group = { /** the name of the group or its kind */ capture: CaptureKind; branches: RegexAst[]; sequence: RegexAst; root: RegexAst; caseInsensitive: boolean; }; namespace Group { type from = g; type pop = [...last, init]; type finalize = g["branches"] extends [] ? pushQuantifiable : [...g["branches"], pushQuantifiable] extends (infer branches extends RegexAst[]) ? finalizeUnion : never; type finalizeUnion = remaining extends ([ infer head extends RegexAst, ...infer tail extends RegexAst[] ]) ? head extends UnionTree ? finalizeUnion : finalizeUnion : UnionTree; } } export type Boundary = Anchor | "(" | ")" | "[" | "]"; export type Anchor = "^" | "$"; export type Control = QuantifyingChar | Boundary | "|" | "." | "{" | "-" | "\\"; export type AnchorMarker = `<${ZeroWidthSpace}${inner}${ZeroWidthSpace}>`; export type StartAnchorMarker = AnchorMarker<"^">; export type EndAnchorMarker = AnchorMarker<"$">; export declare namespace s { type error = State.from<{ unscanned: ErrorMessage; groups: []; capture: never; branches: []; sequence: SequenceTree.Empty; root: ""; caseInsensitive: false; flags: ""; }>; type shiftQuantifiable = State.from<{ unscanned: unscanned; groups: s["groups"]; capture: s["capture"]; branches: s["branches"]; sequence: pushQuantifiable; root: root; caseInsensitive: s["caseInsensitive"]; flags: s["flags"]; }>; type pushQuantified = State.from<{ unscanned: unscanned; groups: s["groups"]; capture: s["capture"]; branches: s["branches"]; sequence: pushQuantifiable; root: ""; caseInsensitive: s["caseInsensitive"]; flags: s["flags"]; }>; type pushQuantifier = State.from<{ unscanned: unscanned; groups: s["groups"]; capture: s["capture"]; branches: s["branches"]; sequence: pushQuantifiable; root: ""; caseInsensitive: s["caseInsensitive"]; flags: s["flags"]; }>; type finalizeBranch = State.from<{ unscanned: unscanned; groups: s["groups"]; capture: s["capture"]; branches: [...s["branches"], pushQuantifiable]; sequence: SequenceTree.Empty; root: ""; caseInsensitive: s["caseInsensitive"]; flags: s["flags"]; }>; type anchor = State.from<{ unscanned: unscanned; groups: s["groups"]; capture: s["capture"]; branches: s["branches"]; sequence: pushQuantifiable>; root: ""; caseInsensitive: s["caseInsensitive"]; flags: s["flags"]; }>; type pushGroup = State.from<{ unscanned: unscanned; groups: [...s["groups"], s]; capture: capture; branches: []; sequence: SequenceTree.Empty; root: ""; caseInsensitive: caseInsensitive extends boolean ? caseInsensitive : s["caseInsensitive"]; flags: s["flags"]; }>; type popGroup = s["groups"] extends State.Group.pop ? State.from<{ unscanned: unscanned; groups: init; capture: last["capture"]; branches: last["branches"]; sequence: pushQuantifiable; root: s["capture"] extends CapturedGroupKind ? GroupTree, s["capture"]> : s["capture"] extends State.UnnamedCaptureKind.lookaround ? "" : State.Group.finalize; caseInsensitive: last["caseInsensitive"]; flags: s["flags"]; }> : s.error>; type finalize = s["groups"] extends [unknown, ...unknown[]] ? ErrorMessage> : finalizeRegexOrError, { captures: EmptyCaptures; names: {}; flags: s["flags"]; errors: []; }>>; type finalizeRegexOrError = r["ctx"]["errors"] extends [] ? applyAnchors extends infer pattern extends string ? contains extends false ? contains extends false ? Regex> : ErrorMessage> : ErrorMessage> : never : r["ctx"]["errors"][0]; type finalizeContext = ctx["captures"] extends EmptyCaptures ? finalizeContextWithoutCaptures : finalizeContextWithCaptures<{ captures: ctx["captures"] extends ([ IndexedCaptureOffset, ...infer rest extends IndexedCaptures ]) ? rest : never; names: ctx["names"]; flags: ctx["flags"]; errors: ctx["errors"]; }>; type finalizeContextWithoutCaptures = ctx["flags"] extends "" ? {} : { flags: ctx["flags"]; }; type finalizeContextWithCaptures = keyof ctx["names"] extends never ? ctx["flags"] extends "" ? { captures: ctx["captures"]; } : { captures: ctx["captures"]; flags: ctx["flags"]; } : ctx["flags"] extends "" ? { captures: ctx["captures"]; names: ctx["names"]; } : { captures: ctx["captures"]; names: ctx["names"]; flags: ctx["flags"]; }; } export type RegexAst = string | ReferenceNode | UnionTree | SequenceTree | GroupTree | QuantifierTree; export interface ReferenceNode { kind: "reference"; to: to; } export declare namespace ReferenceNode { type finalize = to extends NumberLiteral & keyof ctx["captures"] ? ctx["captures"][to] extends IncompleteCaptureGroup ? FinalizationResult.error> : FinalizationResult.from<{ pattern: inferReference; ctx: ctx; }> : to extends keyof ctx["names"] ? ctx["names"][to] extends IncompleteCaptureGroup ? FinalizationResult.error> : FinalizationResult.from<{ pattern: inferReference; ctx: ctx; }> : FinalizationResult.error>; type inferReference = to extends string ? to : ""; } export declare const writeIncompleteReferenceError: (ref: ref) => writeIncompleteReferenceError; export type writeIncompleteReferenceError = `Reference to incomplete group '${ref}' has no effect`; export interface SequenceTree { kind: "sequence"; ast: ast; } export declare namespace SequenceTree { type Empty = SequenceTree<[]>; type finalize = _finalize; type _finalize = tree extends [infer head, ...infer tail] ? finalizeTree extends infer r ? r extends FinalizationResult ? _finalize, r["ctx"]> : never : never : FinalizationResult.from<{ pattern: pattern; ctx: ctx; }>; } export interface UnionTree { kind: "union"; ast: ast; } export declare namespace UnionTree { type finalize = _finalize; type FinalizedBranch = { pattern: string; captures: IndexedCaptures; names: NamedCaptures; }; namespace FinalizedBranch { type from = b; } type _finalize = astBranches extends [infer head, ...infer tail] ? finalizeTree extends infer r ? r extends FinalizationResult ? _finalize, ctx> : never : never : finalizeBranches; type finalizeBranch = [ ...acc, FinalizedBranch.from<{ pattern: r["pattern"]; captures: finalizeBranchCaptures; names: r["ctx"]["names"]; }> ]; type finalizeBranchCaptures> = acc extends [] ? branchCaptures : acc[0]["captures"] extends (infer firstCaptureBranch extends IndexedCaptures) ? branchCaptures extends [] ? { [i in keyof firstCaptureBranch]: undefined; } : [...{ [i in keyof firstCaptureBranch]: undefined; }, ...branchCaptures] : never; type finalizeBranches = i extends keyof acc & NumberLiteral ? FinalizationResult.from<{ pattern: acc[i]["pattern"]; ctx: { flags: ctx["flags"]; captures: [...ctx["captures"], ...acc[i]["captures"]]; names: { [k in unionKeyOf]: k extends (keyof acc[i]["names"]) ? acc[i]["names"][k] : undefined; }; errors: ctx["errors"]; }; }> : never; } export type CapturedGroupKind = string | State.UnnamedCaptureKind.indexed; export type IncompleteCaptureGroup = noSuggest<"incompleteCaptureGroup">; export type IndexedCaptureOffset = noSuggest<"indexedCaptureOffset">; /** * Offset captures to match 1-based indexing for references * (i.e so that \1 would match the first capture group) */ export type EmptyCaptures = [IndexedCaptureOffset]; export interface GroupTree { kind: "group"; capture: capture; ast: ast; } export declare namespace GroupTree { type finalize = finalizeGroupAst extends infer r ? r extends FinalizationResult ? finalizeGroupResult : never : never; type finalizeGroupAst = finalizeTree; type finalizeGroupResult = FinalizationResult.from<{ pattern: r["pattern"]; ctx: self["capture"] extends string ? finalizeNamedCapture : self["capture"] extends State.UnnamedCaptureKind.indexed ? finalizeUnnamedCapture : r["ctx"]; }>; type finalizeNamedCapture = FinalizationContext.from<{ captures: setIndex>; names: { [k in keyof ctx["names"]]: k extends name ? anchorsAway : ctx["names"][k]; }; flags: ctx["flags"]; errors: ctx["errors"]; }>; type finalizeUnnamedCapture = FinalizationContext.from<{ captures: setIndex>; names: ctx["names"]; flags: ctx["flags"]; errors: ctx["errors"]; }>; } export interface QuantifierTree { kind: "quantifier"; ast: ast; min: number; max: number | null; } export declare namespace QuantifierTree { type finalize = finalizeTree extends infer r extends FinalizationResult ? finalizeQuantifierResult : never; type finalizeQuantifierResult> = self["min"] extends 0 ? quantifiedCaptures extends [] ? finalizeNonZeroMinQuantified : finalizeZeroMinQuantifiedWithCaptures : finalizeNonZeroMinQuantified; type finalizeNonZeroMinQuantified = FinalizationResult.from<{ pattern: quantify; ctx: r["ctx"]; }>; type finalizeZeroMinQuantifiedWithCaptures = finalizeZeroQuantified | finalizeOnePlusQuantified; type finalizeZeroQuantified = FinalizationResult.from<{ pattern: ""; ctx: { captures: [ ...ctx["captures"], ...{ [i in keyof quantifiedCaptures]: undefined; } ]; flags: r["ctx"]["flags"]; names: zeroQuantifiedNames; errors: r["ctx"]["errors"]; }; }>; type zeroQuantifiedNames = { [k in keyof result]: k extends keyof base ? result[k] : undefined; } & unknown; type finalizeOnePlusQuantified = max extends 1 ? r : FinalizationResult.from<{ pattern: quantify; ctx: r["ctx"]; }>; } export type pushQuantifiable = root extends "" ? sequence : sequence extends string ? sequence extends "" ? root : root extends string ? appendNonRedundant : SequenceTree<[sequence, root]> : sequence extends SequenceTree ? pushToSequence : SequenceTree<[sequence, root]>; type pushToSequence = sequence extends SequenceTree.Empty ? root : root extends SequenceTree ? SequenceTree<[...sequence["ast"], ...root["ast"]]> : SequenceTree<[...sequence["ast"], root]>; type extractNewCaptures = result extends readonly [...base, ...infer elements extends IndexedCaptures] ? elements : []; export interface FinalizationContext extends Required { errors: ErrorMessage[]; } export declare namespace FinalizationContext { type from = ctx; } export type FinalizationResult = { pattern: string; ctx: FinalizationContext; }; export declare namespace FinalizationResult { type from = r; type error = from<{ pattern: string; ctx: { captures: ctx["captures"]; names: ctx["names"]; flags: ctx["flags"]; errors: [...ctx["errors"], ErrorMessage]; }; }>; } export type finalizeTree = tree extends string ? FinalizationResult.from<{ pattern: tree; ctx: ctx; }> : tree extends SequenceTree ? SequenceTree.finalize : tree extends UnionTree ? UnionTree.finalize : tree extends GroupTree ? GroupTree.finalize : tree extends QuantifierTree ? QuantifierTree.finalize : tree extends ReferenceNode ? ReferenceNode.finalize : never; type applyAnchors = pattern extends `${StartAnchorMarker}${infer startStripped}` ? startStripped extends `${infer bothStripped}${EndAnchorMarker}` ? bothStripped : appendNonRedundant : pattern extends `${infer endStripped}${EndAnchorMarker}` ? prependNonRedundant : prependNonRedundant, string>; type anchorsAway = pattern extends `${StartAnchorMarker}${infer startStripped}` ? startStripped extends `${infer bothStripped}${EndAnchorMarker}` ? bothStripped : startStripped : pattern extends `${infer endStripped}${EndAnchorMarker}` ? endStripped : pattern; type appendNonRedundant = string extends base ? string extends suffix ? string : `${base}${suffix}` : `${number}` extends base ? `${number}` extends suffix ? `${number}` : `${base}${suffix}` : `${base}${suffix}`; type prependNonRedundant = string extends base ? string extends prefix ? string : `${prefix}${base}` : `${number}` extends base ? `${number}` extends prefix ? `${number}` : `${prefix}${base}` : `${prefix}${base}`; export declare const writeMidAnchorError: (anchor: anchor) => writeMidAnchorError; type writeMidAnchorError = `Anchor ${anchor} may not appear mid-pattern`; export {};