parserator
Version:
An elegant parser combinators library for Typescript
1,367 lines (1,362 loc) • 59.5 kB
TypeScript
/**
* Represents a location span in source code with position and size information.
* @example
* ```typescript
* const span: Span = {
* offset: 10,
* length: 5,
* line: 2,
* column: 3
* };
* ```
*/
type Span = {
/** Byte offset from the start of the source */
offset: number;
/** Length of the span in bytes */
length: number;
/** Line number (1-indexed) */
line: number;
/** Column number (1-indexed) */
column: number;
};
/**
* Creates a Span from parser state and optional length.
* @param state - Parser state containing position information
* @param length - Length of the span (defaults to 0)
* @returns A new Span object
* @example
* ```typescript
* const state = { pos: { offset: 10, line: 2, column: 3 } };
* const span = Span(state, 5);
* // Returns: { offset: 10, length: 5, line: 2, column: 3 }
* ```
*/
declare function Span(state: {
pos: {
offset: number;
line: number;
column: number;
};
}, length?: number): Span;
type ExpectedParseError = {
tag: "Expected";
span: Span;
items: string[];
context: string[];
found?: string;
};
type UnexpectedParseError = {
tag: "Unexpected";
span: Span;
found: string;
context: string[];
hints?: string[];
};
type CustomParseError = {
tag: "Custom";
span: Span;
message: string;
hints?: string[];
context: string[];
};
type FatalParseError = {
tag: "Fatal";
span: Span;
message: string;
context: string[];
};
/**
* Union type representing all possible parsing errors.
* Each error type has a discriminant tag for pattern matching.
* @example
* ```typescript
* function handleError(error: ParseError) {
* switch (error.tag) {
* case "Expected":
* console.log(`Expected ${error.items.join(" or ")}`);
* break;
* case "Unexpected":
* console.log(`Unexpected ${error.found}`);
* break;
* // ... handle other cases
* }
* }
* ```
*/
type ParseError = CustomParseError | ExpectedParseError | UnexpectedParseError | FatalParseError;
/**
* Factory functions for creating different types of ParseError objects.
* Provides a convenient API for constructing errors without manually setting tags.
* @example
* ```typescript
* const span = Span(state);
*
* // Create an expected error
* const expectedError = ParseError.expected({
* span,
* items: ["identifier", "keyword"],
* context: ["function declaration"],
* found: "number"
* });
*
* // Create a custom error
* const customError = ParseError.custom({
* span,
* message: "Invalid syntax",
* context: ["expression"],
* hints: ["Try using parentheses"]
* });
* ```
*/
declare const ParseError: {
/** Creates an ExpectedParseError for when specific tokens were expected */
expected: (params: Omit<ExpectedParseError, "tag">) => ExpectedParseError;
/** Creates an UnexpectedParseError for when an unexpected token was found */
unexpected: (params: Omit<UnexpectedParseError, "tag">) => UnexpectedParseError;
/** Creates a CustomParseError with a custom message */
custom: (params: Omit<CustomParseError, "tag">) => CustomParseError;
/** Creates a FatalParseError that cannot be recovered from */
fatal: (params: Omit<FatalParseError, "tag">) => FatalParseError;
};
/**
* A collection of parsing errors with formatting and analysis capabilities.
* Automatically determines the primary (furthest) error for reporting.
* @example
* ```typescript
* const errors = [
* ParseError.expected({ span: spanAt10, items: ["("], context: [] }),
* ParseError.unexpected({ span: spanAt15, found: ")", context: [] })
* ];
* const bundle = new ParseErrorBundle(errors, sourceCode);
*
* console.log(bundle.toString()); // Shows the furthest error
* console.log(bundle.format("ansi")); // Formatted with colors
* ```
*/
declare class ParseErrorBundle {
errors: ParseError[];
source: string;
/**
* Creates a new ParseErrorBundle.
* @param errors - Array of parsing errors
* @param source - The original source code being parsed
* @returns {ParseErrorBundle} A new ParseErrorBundle instance containing the errors and source
*/
constructor(errors: ParseError[], source: string);
/**
* Gets the primary error (the one that occurred furthest in the input).
* This is typically the most relevant error to show to the user.
* @returns {ParseError} The error with the highest offset position
*/
get primary(): ParseError;
/**
* Gets all errors that occurred at the same position as the primary error.
* Useful when multiple parse attempts failed at the same location.
* @returns {ParseError[]} Array of errors at the furthest position
*/
get primaryErrors(): ParseError[];
/**
* Converts the primary error to a simple string representation.
* @returns {string} A human-readable error message
*/
toString(): string;
/**
* Formats the error bundle using the specified formatter.
* @param format - The output format ("plain", "ansi", "html", or "json")
* @returns {string} Formatted error message with context and highlighting
*/
format(format?: "plain" | "ansi" | "html" | "json"): string;
}
declare class Left<L, R = never> {
readonly left: L;
readonly _tag = "Left";
constructor(left: L);
[Symbol.iterator](): Generator<Either<R, L>, R, any>;
}
declare class Right<R, L = any> {
readonly right: R;
readonly _tag = "Right";
constructor(right: R);
[Symbol.iterator](): Generator<Either<R, L>, R, any>;
}
type Either<R, L> = Left<L, R> | Right<R, L>;
declare const Either: {
left<L, R = never>(l: L): Either<R, L>;
right<R, L = never>(r: R): Either<R, L>;
isLeft<R, L>(either: Either<R, L>): either is Left<L, R>;
isRight<R, L>(either: Either<R, L>): either is Right<R, L>;
match<R, L, T>(onLeft: (left: L) => T, onRight: (right: R) => T): (either: Either<R, L>) => T;
gen<R, L>(f: () => Generator<Either<any, L>, R, any>): Either<R, L>;
};
type Spanned<T> = [value: T, span: Span];
/**
* Represents the output of a parser operation, containing both the updated state
* and the parsing result (either success or error).
* @template T - The type of the successfully parsed value
* @example
* ```typescript
* const output: ParserOutput<string> = {
* state: newState,
* result: Either.right("parsed value")
* };
* ```
*/
type ParserOutput<T> = {
/** The parser state after the operation */
state: ParserState;
/** Either a successful result of type T or a ParseErrorBundle */
result: Either<T, ParseErrorBundle>;
};
/**
* Factory function for creating ParserOutput objects.
* @template T - The type of the successfully parsed value
* @param state - The parser state after the operation
* @param result - Either a successful result or error bundle
* @returns A new ParserOutput object
* @example
* ```typescript
* import { Either } from "./either";
*
* const successOutput = ParserOutput(newState, Either.right("success"));
* const errorOutput = ParserOutput(oldState, Either.left(errorBundle));
* ```
*/
declare const ParserOutput: <T>(state: ParserState, result: Either<T, ParseErrorBundle>) => ParserOutput<T>;
/**
* Represents a position within source code with line, column, and byte offset.
* All values are 1-indexed for human readability.
* @example
* ```typescript
* const position: SourcePosition = {
* line: 3, // Third line
* column: 15, // 15th character on that line
* offset: 42 // 42nd character from start of input
* };
* ```
*/
type SourcePosition = {
/** Line number (1-indexed) */
line: number;
/** Column number (1-indexed) */
column: number;
/** Byte offset from start of input (0-indexed) */
offset: number;
};
declare class SourcePosition_ {
line: number;
column: number;
offset: number;
constructor(line: number, column: number, offset: number);
}
/**
* Represents the complete state of a parser at any point during parsing.
* Contains the input being parsed, current position, and optional debugging/context information.
* @example
* ```typescript
* const state: ParserState = {
* remaining: "hello world",
* pos: { line: 1, column: 1, offset: 0 },
* source: "hello world",
* debug: true,
* labelStack: ["expression", "identifier"],
* committed: false
* };
* ```
*/
type ParserState = {
/** The portion of input that hasn't been consumed yet */
remaining: string;
/** Current position in the source code */
pos: SourcePosition;
/** The complete original input string */
source: string;
/** Whether debug mode is enabled for detailed error reporting */
debug?: boolean;
/** Stack of parsing context labels for error reporting */
labelStack?: string[];
/** Whether the parser has committed to this parse path */
committed?: boolean;
};
/**
* Utility object containing static methods for creating and manipulating parser state.
*/
declare const State: {
/**
* Creates a new parser state from an input string.
*
* @param input - The input string to parse
* @returns A new parser state initialized at the start of the input
*/
fromInput(input: string): ParserState;
/**
* Creates a new state by consuming n characters from the current state.
*
* @param state - The current parser state
* @param n - Number of characters to consume
* @returns A new state with n characters consumed and position updated
* @throws Error if attempting to consume more characters than remaining
*/
consume(state: ParserState, n: number): ParserState;
/**
* Creates a new state by consuming a specific string from the current state.
*
* @param state - The current parser state
* @param str - The string to consume
* @returns A new state with the string consumed and position updated
* @throws Error if the input doesn't start with the specified string
*/
consumeString(state: ParserState, str: string): ParserState;
/**
* Creates a new state by moving to a specific offset position in the source.
* Resets to the beginning and then consumes to the target position.
* @param state - The current parser state
* @param moveBy - Number of characters to move forward from current position
* @returns A new state at the target position
*/
move(state: ParserState, moveBy: number): ParserState;
/**
* Creates a new state by consuming characters while a predicate is true.
*
* @param state - The current parser state
* @param predicate - Function that tests each character
* @returns A new state with matching characters consumed
*/
consumeWhile(state: ParserState, predicate: (char: string) => boolean): ParserState;
/**
* Gets the next n characters from the input without consuming them.
*
* @param state - The current parser state
* @param n - Number of characters to peek (default: 1)
* @returns The next n characters as a string
*/
peek(state: ParserState, n?: number): string;
/**
* Checks if the parser has reached the end of input.
*
* @param state - The current parser state
* @returns True if at end of input, false otherwise
*/
isAtEnd(state: ParserState): boolean;
/**
* Creates a human-readable string representation of the current parser position.
* @param state - The current parser state
* @returns A formatted string showing line, column, and offset
* @example
* ```typescript
* const posStr = State.printPosition(state);
* // Returns: "line 5, column 12, offset 89"
* ```
*/
printPosition(state: ParserState): string;
};
/**
* Parser is the core type that represents a parser combinator.
*
* A parser is a function that takes an input state and produces either:
* - A successful parse result with the remaining input state
* - An error describing why the parse failed
*
* Parsers can be composed using various combinators to build complex parsers
* from simple building blocks.
*
* @template T The type of value this parser produces when successful
*/
declare class Parser<T> {
/**
* @internal
*/
run: (state: ParserState) => ParserOutput<T>;
/**
* Creates a new Parser instance.
*
* @param run - The parsing function that takes a parser state and returns a parse result
*/
constructor(
/**
* @internal
*/
run: (state: ParserState) => ParserOutput<T>);
/**
* Creates a successful parser output with the given value and state.
*
* This is a low-level helper used internally to construct successful parse results.
* It doesn't consume any input and returns the value with the current state unchanged.
*
* @param value - The value to wrap in a successful result
* @param state - The current parser state
* @returns {ParserOutput<T>} A successful parser output containing the value
* @template T The type of the successful value
* @internal
*/
static succeed<T>(value: T, state: ParserState): ParserOutput<T>;
/**
* Creates a parser that always succeeds with the given value without consuming any input.
*
* This is the basic way to inject a value into the parser context. The parser will
* succeed immediately with the provided value and won't advance the parser state.
*
* @param a - The value to lift into the parser context
* @returns {Parser<A>} A parser that always succeeds with the given value
* @template A The type of the value being lifted
*
* @example
* ```ts
* const always42 = Parser.lift(42)
* always42.parse("any input") // succeeds with 42
*
* // Useful for providing default values
* const parseNumberOrDefault = number.or(Parser.lift(0))
*
* // Can be used to inject values in parser chains
* const parser = parser(function* () {
* const name = yield* identifier
* const separator = yield* Parser.lift(":")
* const value = yield* number
* return { name, separator, value }
* })
* ```
*/
static lift: <A>(a: A) => Parser<A>;
/**
* Lifts a binary function into the parser context, applying it to the results of two parsers.
*
* This is the applicative functor's version of `map` for functions of two arguments.
* It runs both parsers in sequence and applies the function to their results if both succeed.
*
* @param ma - The first parser
* @param mb - The second parser
* @param f - A function that takes the results of both parsers and produces a new value
* @returns {Parser<C>} A parser that applies the function to the results of both input parsers
* @template A The type of value produced by the first parser
* @template B The type of value produced by the second parser
* @template C The type of value produced by applying the function
*
* @example
* ```ts
* // Combine two parsed values with a function
* const parsePoint = Parser.liftA2(
* number,
* number.trimLeft(comma),
* (x, y) => ({ x, y })
* )
* parsePoint.parse("10, 20") // succeeds with { x: 10, y: 20 }
*
* // Build a data structure from multiple parsers
* const parsePerson = Parser.liftA2(
* identifier,
* number.trimLeft(colon),
* (name, age) => ({ name, age })
* )
* parsePerson.parse("John:30") // succeeds with { name: "John", age: 30 }
* ```
*/
static liftA2: <A, B, C>(ma: Parser<A>, mb: Parser<B>, f: (a: A, b: B) => C) => Parser<C>;
/**
* Applies a parser that produces a function to a parser that produces a value.
*
* This is the applicative functor's application operator. It allows you to apply
* functions within the parser context, enabling powerful composition patterns.
*
* @param ma - A parser that produces a value
* @param mf - A parser that produces a function from that value type to another type
* @returns {Parser<B>} A parser that applies the parsed function to the parsed value
* @template A The type of the input value
* @template B The type of the output value after function application
*
* @example
* ```ts
* // Parse a function name and apply it
* const parseFn = choice([
* string("double").map(() => (x: number) => x * 2),
* string("square").map(() => (x: number) => x * x)
* ])
* const result = Parser.ap(number, parseFn.trimLeft(space))
* result.parse("5 double") // succeeds with 10
* result.parse("5 square") // succeeds with 25
*
* // Chain multiple applications
* const add = (x: number) => (y: number) => x + y
* const parseAdd = Parser.lift(add)
* const addParser = Parser.ap(
* number,
* Parser.ap(number.trimLeft(plus), parseAdd)
* )
* addParser.parse("3 + 4") // succeeds with 7
* ```
*/
static ap: <A, B>(ma: Parser<A>, mf: Parser<(_: A) => B>) => Parser<B>;
/**
* Creates a failed parser output with the given error information.
*
* This is a low-level helper for constructing parse errors. It creates a custom
* error with the provided message and optional expected/found information.
*
* @param error - Error details including message and optional expected/found values
* @param state - The parser state where the error occurred
* @returns {ParserOutput<never>} A failed parser output containing the error
* @internal
*/
static fail(error: {
message: string;
expected?: string[];
found?: string;
}, state: ParserState): ParserOutput<never>;
/**
* Creates a parser that always fails with a fatal error.
*
* Fatal errors are non-recoverable and prevent backtracking in choice combinators.
* Use this when you've determined that the input is definitely malformed and trying
* other alternatives would be meaningless.
*
* @param message - The error message to display
* @returns {Parser<never>} A parser that always fails with a fatal error
*
* @example
* ```ts
* const number = regex(/-?[0-9]+/).map(Number);
* const parsePositive = number.flatMap(n =>
* n > 0 ? Parser.lift(n) : Parser.fatal("Expected positive number")
* )
* ```
*/
static fatal: (message: string) => Parser<never>;
/**
* Runs the parser on the given input string and returns the full parser output.
*
* This method provides access to both the parse result and the final parser state,
* which includes information about the remaining unparsed input and position.
*
* @param input - The string to parse
* @returns {ParserOutput<T>} A parser output containing both the result (success or error) and final state
*
* @example
* ```ts
* const parser = string("hello");
* const output = parser.parse("hello world");
* // output.result contains Either.right("hello")
* // output.state contains remaining input " world" and position info
* ```
*/
parse(input: string): ParserOutput<T>;
/**
* Runs the parser on the given input and returns either the parsed value or error bundle.
*
* This is a convenience method that unwraps the Either result, making it easier
* to handle the common case where you just need the value or error without the
* full parser state information.
*
* @param input - The string to parse
* @returns {T | ParseErrorBundle} The successfully parsed value of type T, or a ParseErrorBundle on failure
*
* @example
* ```ts
* const parser = number();
* const result = parser.parseOrError("42");
* if (result instanceof ParseErrorBundle) {
* console.error(result.format());
* } else {
* console.log(result); // 42
* }
* ```
*/
parseOrError(input: string): T | ParseErrorBundle;
/**
* Runs the parser on the given input and returns the parsed value or throws an error.
*
* This method is useful when you're confident the parse will succeed or want to
* handle parse errors as exceptions. The thrown error is a ParseErrorBundle which
* contains detailed information about what went wrong.
*
* @param input - The string to parse
* @returns {T} The successfully parsed value of type T
* @throws {ParseErrorBundle} Thrown when parsing fails
*
* @example
* ```ts
* const parser = number();
* try {
* const value = parser.parseOrThrow("42");
* console.log(value); // 42
* } catch (error) {
* if (error instanceof ParseErrorBundle) {
* console.error(error.format());
* }
* }
* ```
*/
parseOrThrow(input: string): T;
/**
* Transforms the result of this parser by applying a function to the parsed value.
*
* This is the functor map operation. If the parser succeeds, the function is applied
* to the result. If the parser fails, the error is propagated unchanged. The input
* is not consumed if the transformation fails.
*
* @param f - A function that transforms the parsed value
* @returns {Parser<B>} A new parser that produces the transformed value
* @template B The type of the transformed value
*
* @example
* ```ts
* // Parse a number and double it
* const doubled = number().map(n => n * 2);
* doubled.parse("21") // succeeds with 42
*
* // Parse a string and get its length
* const stringLength = quoted('"').map(s => s.length);
* stringLength.parse('"hello"') // succeeds with 5
*
* // Chain multiple transformations
* const parser = identifier()
* .map(s => s.toUpperCase())
* .map(s => ({ name: s }));
* parser.parse("hello") // succeeds with { name: "HELLO" }
* ```
*/
map<B>(f: (a: T) => B): Parser<B>;
/**
* Chains this parser with another parser that depends on the result of this one.
*
* This is the monadic bind operation (also known as chain or andThen). It allows
* you to create a parser whose behavior depends on the result of a previous parse.
* This is essential for context-sensitive parsing where later parsing decisions
* depend on earlier results.
*
* @param f - A function that takes the parsed value and returns a new parser
* @returns {Parser<B>} A new parser that runs the second parser after the first succeeds
* @template B The type of value produced by the resulting parser
*
* @example
* ```ts
* // Parse a number and then that many 'a' characters
* const parser = number().flatMap(n =>
* string('a'.repeat(n))
* );
* parser.parse("3aaa") // succeeds with "aaa"
*
* // Parse a type annotation and return appropriate parser
* const typeParser = identifier().flatMap(type => {
* switch(type) {
* case "int": return number();
* case "string": return quoted('"');
* default: return Parser.fail({ message: `Unknown type: ${type}` });
* }
* });
*
* // Validate parsed values
* const positiveNumber = number().flatMap(n =>
* n > 0
* ? Parser.lift(n)
* : Parser.fail({ message: "Expected positive number" })
* );
* ```
*/
flatMap<B>(f: (a: T) => Parser<B>): Parser<B>;
/**
* Creates a parser that always succeeds with the given value without consuming input.
*
* This is an alias for `Parser.lift` that follows the monadic naming convention.
* It's the "return" or "pure" operation for the Parser monad, injecting a plain
* value into the parser context.
*
* @param a - The value to wrap in a successful parser
* @returns {Parser<A>} A parser that always succeeds with the given value
* @template A The type of the value being lifted
*
* @example
* ```ts
* // Always succeed with a constant value
* const always42 = Parser.pure(42);
* always42.parse("any input") // succeeds with 42
*
* // Use in flatMap to wrap values
* const parser = number().flatMap(n =>
* n > 0 ? Parser.pure(n) : Parser.fail({ message: "Must be positive" })
* );
* ```
*/
static pure: <A>(a: A) => Parser<A>;
/**
* Creates a new parser that lazily evaluates the given function.
* This is useful for creating recursive parsers.
*
* @param fn - A function that returns a parser
* @returns {Parser<T>} A new parser that evaluates the function when parsing
* @template T The type of value produced by the parser
*
* @example
* ```ts
* // Create a recursive parser for nested parentheses
* const parens: Parser<string> = Parser.lazy(() =>
* between(
* char('('),
* char(')'),
* parens
* )
* )
* ```
*/
static lazy<T>(fn: () => Parser<T>): Parser<T>;
/**
* Combines this parser with another parser, returning both results as a tuple.
*
* This is a fundamental sequencing operation that runs two parsers in order.
* If either parser fails, the entire operation fails. The results are returned
* as a tuple containing both parsed values.
*
* @param parserB - The second parser to run after this one
* @returns {Parser<[T, B]>} A parser that produces a tuple of both results
* @template B The type of value produced by the second parser
*
* @example
* ```ts
* // Parse a coordinate pair
* const coordinate = number().zip(number().trimLeft(comma));
* coordinate.parse("10, 20") // succeeds with [10, 20]
*
* // Parse a key-value pair
* const keyValue = identifier().zip(number().trimLeft(colon));
* keyValue.parse("age:30") // succeeds with ["age", 30]
*
* // Combine multiple parsers
* const triple = number()
* .zip(number().trimLeft(comma))
* .zip(number().trimLeft(comma))
* .map(([[a, b], c]) => [a, b, c]);
* triple.parse("1, 2, 3") // succeeds with [1, 2, 3]
* ```
*/
zip<B>(parserB: Parser<B>): Parser<[T, B]>;
/**
* Sequences this parser with another, keeping only the second result.
*
* This is useful when you need to parse something but only care about what
* comes after it. The first parser must succeed for the second to run, but
* its result is discarded.
*
* @param parserB - The parser whose result will be kept
* @returns {Parser<B>} A parser that produces only the second result
* @template B The type of value produced by the second parser
*
* @example
* ```ts
* // Parse a value after a label
* const labeledValue = string("value:").then(number());
* labeledValue.parse("value:42") // succeeds with 42
*
* // Skip whitespace before parsing
* const trimmedNumber = whitespace().then(number());
* trimmedNumber.parse(" 123") // succeeds with 123
*
* // Parse the body after a keyword
* const functionBody = keyword("function").then(identifier()).then(block());
* ```
*/
then<B>(parserB: Parser<B>): Parser<B>;
/**
* Alias for `then` - sequences parsers and keeps the right result.
*
* This alias follows the naming convention from applicative functors where
* "zipRight" means to combine two values but keep only the right one.
*
* @see {@link then} for details and examples
*/
zipRight: <B>(parserB: Parser<B>) => Parser<B>;
/**
* Sequences this parser with another, keeping only the first result.
*
* This is useful when you need to parse something that must be present but
* whose value you don't need. Common uses include parsing required delimiters
* or terminators.
*
* @param parserB - The parser to run but whose result will be discarded
* @returns {Parser<T>} A parser that produces only the first result
* @template B The type of value produced by the second parser (discarded)
*
* @example
* ```ts
* // Parse a statement and discard the semicolon
* const statement = expression().thenDiscard(char(';'));
* statement.parse("x + 1;") // succeeds with the expression, semicolon discarded
*
* // Parse a quoted string and discard the closing quote
* const quotedContent = char('"').then(stringUntil('"')).thenDiscard(char('"'));
*
* // Parse array elements and discard separators
* const element = number().thenDiscard(optional(char(',')));
* ```
*/
thenDiscard<B>(parserB: Parser<B>): Parser<T>;
/**
* Alias for `thenDiscard` - sequences parsers and keeps the left result.
*
* This alias follows the naming convention from applicative functors where
* "zipLeft" means to combine two values but keep only the left one.
*
* @see {@link thenDiscard} for details and examples
*/
zipLeft: <B>(parserB: Parser<B>) => Parser<T>;
/**
* Makes this parser usable in generator syntax for cleaner sequential parsing.
*
* This iterator implementation allows parsers to be used with `yield*` in
* generator functions, enabling a more imperative style of parser composition
* that can be easier to read for complex sequential parsing.
*
* @returns {Generator<Parser<T>, T, any>} A generator that yields this parser and returns its result
* @internal
*/
[Symbol.iterator](): Generator<Parser<T>, T, any>;
/**
* Adds a tap point to observe the current state and result during parsing.
* Useful for debugging parser behavior.
*
* @example
* ```ts
* const parser = parser(function* () {
* const name = yield* identifier();
* yield* char(':');
* const value = yield* number();
* return { name, value };
* });
* parser.tap(({ state, result }) => {
* console.log(`Parsed ${result} at position ${state.pos}`);
* });
* ```
*
* @param callback - Function called with current state and result
* @returns {Parser<T>} The same parser with the tap point added
*/
tap(callback: (args: {
state: ParserState;
result: ParserOutput<T>;
}) => void): Parser<T>;
static gen: <T_1>(f: () => Generator<Parser<any>, T_1, any>) => Parser<T_1>;
trim(parser: Parser<any>): Parser<T>;
trimLeft(parser: Parser<any>): Parser<T>;
trimRight(parser: Parser<any>): Parser<T>;
/**
* Adds a label to this parser for better error messages
* @param name - The label name to add to the context stack
* @returns {Parser<T>} A new parser with the label added
*/
label(name: string): Parser<T>;
/**
* Helper for creating semantic expectations with both label and error message
* @param description - The description for both the label and error message
* @returns {Parser<T>} A new parser with both labeling and error message
*/
expect(description: string): Parser<T>;
/**
* Helper for creating semantic expectations with both label and error message
* @param errorBundle - The error bundle containing the errors to be displayed
* @param state - The current parser state
* @returns {ParserOutput<never>} A parser output with the error bundle and the current state
* @internal
*/
static failRich(errorBundle: {
errors: ParseError[];
}, state: ParserState): ParserOutput<never>;
/**
* Commits to the current parsing path, preventing backtracking beyond this point.
*
* Once a parser is committed, if it fails later in the sequence, the error won't
* backtrack to try other alternatives in a `choice` or `or` combinator. This leads
* to more specific error messages instead of generic "expected one of" errors.
*
* @returns {Parser<T>} A new parser that sets the commit flag after successful parsing
*
* @example
* ```ts
* // Use commit after matching a keyword to ensure specific error messages
* const ifStatement = parser(function* () {
* yield* keyword("if")
* yield* commit() // After seeing "if", we know it's an if statement
* yield* char('(').expect("opening parenthesis after 'if'")
* const condition = yield* expression
* yield* char(')').expect("closing parenthesis")
* const body = yield* block
* return { type: "if", condition, body }
* })
*
* // In a choice, commit prevents backtracking
* const statement = choice([
* ifStatement,
* whileStatement,
* assignment
* ])
*
* // Input: "if x > 5 {}" (missing parentheses)
* // Without commit: "Expected if, while, or assignment"
* // With commit: "Expected opening parenthesis after 'if'"
* ```
*
* @example
* ```ts
* // Commit can be chained with other methods
* const jsonObject = char('{')
* .commit() // Once we see '{', it must be an object
* .then(whitespace)
* .then(objectContent)
* .expect("valid JSON object")
* ```
*
* @see {@link commit} - Standalone function version
* @see {@link cut} - Alias with Prolog-style naming
*/
commit: () => Parser<T>;
/**
* Creates an atomic parser that either fully succeeds or resets to the original state.
*
* This is useful for "all-or-nothing" parsing where you want to try a complex
* parser but not consume any input if it fails. The parser acts as a transaction -
* if any part fails, the entire parse is rolled back.
*
* @returns {Parser<T>} A new parser that resets state on failure
*
* @example
* ```ts
* // Without atomic - partial consumption on failure
* const badParser = parser(function* () {
* yield* string("foo")
* yield* string("bar") // If this fails, "foo" is already consumed
* })
*
* // With atomic - no consumption on failure
* const goodParser = parser(function* () {
* yield* string("foo")
* yield* string("bar") // If this fails, we reset to before "foo"
* }).atomic()
* ```
*
* @example
* ```ts
* // Useful for trying complex alternatives
* const value = or(
* // Try to parse as a complex expression
* expression.atomic(),
* // If that fails completely, try as a simple literal
* literal
* )
* ```
*
* @example
* ```ts
* // Lookahead parsing without consumption
* const startsWithKeyword = or(
* string("function").atomic(),
* string("const").atomic(),
* string("let").atomic()
* ).map(() => true).or(Parser.succeed(false))
* ```
*
* @see {@link atomic} - Standalone function version
*/
atomic(): Parser<T>;
spanned(): Parser<Spanned<T>>;
}
declare const parser: <T>(f: () => Generator<Parser<any>, T, any>) => Parser<T>;
/**
* @fileoverview
*/
/**
* Creates a parser that looks ahead in the input stream without consuming any input.
* The parser will succeed with the result of the given parser but won't advance the input position.
*
* @param par - The parser to look ahead with
* @returns {Parser<T | undefined>} A new parser that peeks at the input without consuming it
* ```ts
* const parser = lookahead(char('a'))
* parser.run('abc') // Right(['a', {...}])
* // Input position remains at 'abc', 'a' is not consumed
* ```
*/
declare const lookahead: <T>(par: Parser<T>) => Parser<T | undefined>;
/**
* Creates a parser that succeeds only if the given parser fails to match.
* If the parser succeeds, this parser fails with an error message.
*
* @param par - The parser that should not match
* @returns {Parser<boolean>} A new parser that succeeds only if the input parser fails
* ```ts
* const notA = notFollowedBy(char('a'))
* notA.run('bcd') // Right([true, {...}]) - Succeeds because 'a' is not found
* notA.run('abc') // Left(error) - Fails because 'a' is found
* ```
*/
declare function notFollowedBy<T>(par: Parser<T>): Parser<boolean>;
/**
* Creates a parser that matches an exact string in the input.
*
* @param str - The string to match
* @returns {Parser<string>} A parser that matches and consumes the exact string
* ```ts
* const parser = string("hello")
* parser.run("hello world") // Right(["hello", {...}])
* parser.run("goodbye") // Left(error)
* ```
*/
declare const string: (str: string) => Parser<string>;
/**
* Creates a parser that matches an exact string literal type.
* Similar to string parser but preserves the literal type information.
*
* @param str - The string literal to match
* @returns {Parser<T>} A parser that matches and consumes the exact string with preserved type
* ```ts
* const parser = narrowedString("hello") // Parser<"hello">
* parser.run("hello world") // Right(["hello", {...}])
* parser.run("goodbye") // Left(error)
* ```
*/
declare const narrowedString: <const T extends string>(str: T) => Parser<T>;
/**
* Creates a parser that matches a single character.
*
* @param ch - The character to match
* @returns {Parser<T>} A parser that matches and consumes a single character
* ```ts
* const parser = char("a")
* parser.run("abc") // Right(["a", {...}])
* parser.run("xyz") // Left(error)
* ```
*/
declare const char: <T extends string>(ch: T) => Parser<T>;
/**
* A parser that matches any single alphabetic character (a-z, A-Z).
*
* ```ts
* const parser = alphabet
* parser.run("abc") // Right(["a", {...}])
* parser.run("123") // Left(error)
* ```
*/
declare const alphabet: Parser<string>;
/**
* A parser that matches any single digit character (0-9).
*
* ```ts
* const parser = digit
* parser.run("123") // Right(["1", {...}])
* parser.run("abc") // Left(error)
* ```
*/
declare const digit: Parser<string>;
/**
* Creates a parser that matches zero or more occurrences of elements separated by a separator.
*
* @param sepParser - Parser for the separator between elements
* @param parser - Parser for the elements
* @returns {Parser<T[]>} A parser that produces an array of matched elements
*
* ```ts
* const parser = sepBy(char(','), digit)
* parser.run("1,2,3") // Right([["1", "2", "3"], {...}])
* parser.run("") // Right([[], {...}])
* ```
*/
declare function sepBy<S, T>(parser: Parser<T>, sepParser: Parser<S>): Parser<T[]>;
/**
* Parses one or more occurrences of a parser separated by another parser.
* Requires at least one match of the main parser.
*
* @param par - The parser for the elements
* @param sepParser - The parser for the separator
* @returns {Parser<T[]>} A parser that produces a non-empty array of parsed elements
*
* @example
* ```ts
* const numbers = sepBy1(number, char(','))
* numbers.parse("1,2,3") // Success: [1, 2, 3]
* numbers.parse("") // Error: Expected at least one element
* ```
*/
declare function sepBy1<S, T>(par: Parser<T>, sepParser: Parser<S>): Parser<T[]>;
/**
* Creates a parser that matches content between two string delimiters.
*
* @param start - The opening delimiter string
* @param end - The closing delimiter string
* @param par - The parser for the content between delimiters
* @returns {Parser<T>} A parser that matches content between delimiters
*
* ```ts
* const parser = between(char('('), char(')'), digit)
* parser.run('(5)') // Right(['5', {...}])
* parser.run('5') // Left(error)
* parser.run('(5') // Left(error: Expected closing delimiter)
* ```
*/
declare function between<T>(start: Parser<any>, end: Parser<any>, par: Parser<T>): Parser<T>;
/**
* A parser that matches any single character.
*
* @returns {Parser<string>} A parser that matches any single character
*/
declare function anyChar(): Parser<string>;
/**
* Creates a parser that matches zero or more occurrences of the input parser.
*
* @param parser - The parser to repeat
* @returns {Parser<T[]>} A parser that produces an array of all matches
*/
declare const many0: <S, T>(parser: Parser<T>, separator?: Parser<S>) => Parser<T[]>;
/**
* Parses zero or more occurrences of a parser (alias for many0).
*
* @param parser - The parser to repeat
* @returns {Parser<T[]>} A parser that produces an array of parsed elements
*/
declare const many: <T>(parser: Parser<T>) => Parser<T[]>;
/**
* Creates a parser that matches one or more occurrences of the input parser.
*
* @param parser - The parser to repeat
* @returns {Parser<T[]>} A parser that produces an array of all matches (at least one)
*/
declare const many1: <S, T>(parser: Parser<T>, separator?: Parser<S>) => Parser<T[]>;
/**
* Creates a parser that matches at least n occurrences of the input parser.
*
* @param parser - The parser to repeat
* @param n - Number of required repetitions
* @returns {Parser<T[]>} A parser that produces an array of at least n matches
*/
declare const manyN: <S, T>(parser: Parser<T>, n: number, separator?: Parser<S>) => Parser<T[]>;
/**
* Creates a parser that matches exactly n occurrences of the input parser.
*
* @param parser - The parser to repeat
* @param n - Number of required repetitions
* @param separator - Optional parser to match between occurrences
* @returns {Parser<T[]>} A parser that produces an array of exactly n matches
*/
declare const manyNExact: <S, T>(par: Parser<T>, n: number, separator?: Parser<S>) => Parser<T[]>;
/**
* Creates a parser that skips zero or more occurrences of the input parser.
*
* @param parser - The parser to skip
* @returns {Parser<undefined>} A parser that skips all matches
*/
declare const skipMany0: <T>(parser: Parser<T>) => Parser<undefined>;
/**
* Creates a parser that skips one or more occurrences of the input parser.
*
* @param parser - The parser to skip
* @returns {Parser<undefined>} A parser that skips all matches (requires at least one)
*/
declare const skipMany1: <T>(parser: Parser<T>) => Parser<undefined>;
/**
* Creates a parser that skips exactly n occurrences of the input parser.
*
* @param parser - The parser to skip
* @param n - Number of required repetitions to skip
* @returns A parser that skips exactly n matches
*/
declare const skipManyN: <T>(parser: Parser<T>, n: number) => Parser<undefined>;
/**
* Creates a parser that skips input until the given parser succeeds.
*
* @param parser - The parser to look for
* @returns A parser that skips input until a match is found
*/
declare function skipUntil<T>(parser: Parser<T>): Parser<undefined>;
/**
* Creates a parser that takes input until the given parser succeeds.
*
* @param parser - The parser to look for
* @returns A parser that takes input until a match is found
*/
declare function takeUntil<T>(parser: Parser<T>): Parser<string>;
/**
* Creates a parser that takes input until the given character is found.
*
* @param char - The character to look for
* @returns A parser that takes input until the character is found
*/
declare function parseUntilChar(char: string): Parser<string>;
/**
* A parser that skips any number of space characters.
*/
declare const skipSpaces: Parser<undefined>;
/**
* Creates a parser that tries multiple parsers in order until one succeeds.
*
* @param parsers - Array of parsers to try
* @returns A parser that succeeds if any of the input parsers succeed
*/
/**
* Creates a parser that tries each of the given parsers in order until one succeeds.
*
* This combinator is commit-aware: if any parser sets the `committed` flag during
* parsing, no further alternatives will be tried. This enables better error messages
* by preventing backtracking once we've identified the intended parse path.
*
* @param parsers - Array of parsers to try in order
* @returns {Parser<Parsers[number] extends Parser<infer T> ? T : never>} A parser that succeeds with the first successful parser's result
*
* @example
* ```ts
* // Basic usage - tries each alternative
* const value = or(
* numberLiteral,
* stringLiteral,
* booleanLiteral
* )
* ```
*
* @example
* ```ts
* // With commit for better errors
* const statement = or(
* parser(function* () {
* yield* keyword("if")
* yield* commit() // No backtracking after this
* yield* char('(').expect("opening parenthesis")
* // ...
* }),
* whileStatement,
* assignment
* )
*
* // Input: "if x > 5" (missing parentheses)
* // Without commit: "Expected if, while, or assignment"
* // With commit: "Expected opening parenthesis"
* ```
*
* @example
* ```ts
* // Error accumulation without commit
* const config = or(
* jsonParser.label("JSON format"),
* yamlParser.label("YAML format"),
* tomlParser.label("TOML format")
* )
* // Errors from all three parsers are accumulated
* ```
*/
declare function or<Parsers extends Parser<any>[]>(...parsers: Parsers): Parser<Parsers[number] extends Parser<infer T> ? T : never>;
/**
* Creates a parser that optionally matches the input parser.
* If the parser fails, returns undefined without consuming input.
*
* @param parser - The parser to make optional
* @returns {Parser<T | undefined>} A parser that either succeeds with a value or undefined
*/
declare function optional<T>(parser: Parser<T>): Parser<T | undefined>;
type SequenceOutput<T extends Parser<any>[], Acc extends any[] = []> = T["length"] extends 0 ? Acc : T extends [Parser<infer Head extends any>, ...infer Tail extends any[]] ? SequenceOutput<Tail, [...Acc, Head]> : never;
/**
* Creates a parser that runs multiple parsers in sequence and returns all results.
*
* @param parsers - Array of parsers to run in sequence
* @returns {Parser<SequenceOutput<T>>} A parser that succeeds if all parsers succeed in order, returning a tuple of all results
*
* @example
* ```ts
* const parser = sequence([digit, char('-'), digit])
* parser.run('1-2') // Right([['1', '-', '2'], {...}])
* ```
*/
declare const sequence: <const T extends any[]>(parsers: T) => Parser<SequenceOutput<T>>;
/**
* Creates a parser that matches input against a regular expression.
* The regex must match at the start of the input.
*
* @param re - The regular expression to match against
* @returns {Parser<string>} A parser that matches the regex pattern
*/
declare const regex: (re: RegExp) => Parser<string>;
declare function zip<A, B>(parserA: Parser<A>, parserB: Parser<B>): Parser<[A, B]>;
declare function then<A, B>(parserA: Parser<A>, parserB: Parser<B>): Parser<B>;
declare const zipRight: typeof then;
declare function thenDiscard<A, B>(parserA: Parser<A>, parserB: Parser<B>): Parser<A>;
declare const zipLeft: typeof thenDiscard;
/**
* Creates a parser that takes input until the given parser would succeed, without consuming the parser.
*
* @param parser - The parser to look for
* @returns A parser that takes input until before a match would be found
*/
declare function takeUpto<T>(parser: Parser<T>): Parser<string>;
/**
* Creates a parser that commits to the current parsing path, preventing backtracking.
*
* After calling `commit()`, if parsing fails later in the sequence, the parser won't
* backtrack to try alternatives in a `choice` or `or` combinator. This results in
* more specific, helpful error messages instead of generic "expected one of" errors.
*
* @returns {Parser<void>} A parser that sets the commit flag in the parsing context
*
* @example
* ```ts
* // Use commit after identifying the type of construct
* const ifStatement = parser(function* () {
* yield* keyword("if")
* yield* commit() // No backtracking after this point
* yield* char('(').expect("opening parenthesis after 'if'")
* const condition = yield* expression
* yield* char(')').expect("closing parenthesis")
* const body = yield* block
* return { type: "if", condition, body }
* })
* ```
*
* @example
* ```ts
* // Commit in different parsing contexts
* const jsonParser = parser(function* () {
* const firstChar = yield* peekChar
*
* if (firstChar === '{') {
* yield* char('{')
* yield* commit() // Definitely parsing an object
* return yield* jsonObject
* } else if (firstChar === '[') {
* yield* char('[')
* yield* commit() // Definitely parsing an array
* return yield* jsonArray
* }
* // ...
* })
* ```
*
* @example
* ```ts
* // Commit with error recovery
* const statement = choice([
* ifStatement, // Has commit() after "if"
* whileStatement, // Has commit() after "while"
* forStatement, // Has commit() after "for"
* expression // No commit, can always fall back to this
* ])
*
* // Input: "if (x > 5 { }" (missing closing paren)
* // Result: "Expected closing parenthesis" (not "Expected if, while, for, or expression")
* ```
*
* @see {@link cut} - Alias with Prolog-style naming
*/
declare function commit(): Parser<void>;
/**
* Alias for {@link commit} using Prolog-style naming.
*
* The cut operator (!) in Prolog prevents backtracking, similar to how
* this prevents the parser from trying other alternatives after this point.
*
* @example
* ```ts
* const prologStyleIf = parser(function* () {
* yield* keyword("if")
* yield* cut() // Using Prolog-style naming
* yield* char('(')
* // ...
* })
* ```
*/
declare const cut: typeof commit;
/**
* Creates an atomic parser that either fully succeeds or resets to the original state.
*
* This combinator wraps a parser in a transaction-like behavior. If the parser fails
* at a