parserator
Version:
An elegant parser combinators library for Typescript
1 lines • 65.5 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/either.ts","../src/state.ts","../src/debug.ts","../src/errors.ts","../src/parser.ts","../src/chain.ts","../src/combinators.ts","../src/utils.ts"],"sourcesContent":["export * from \"./chain\"\nexport * from \"./combinators\"\nexport * from \"./debug\"\nexport * from \"./either\"\nexport * from \"./errors\"\nexport * from \"./parser\"\nexport * from \"./state\"\nexport * from \"./types\"\nexport * from \"./utils\"\n","export type Either<R, L> = Left<L, R> | Right<R, L>\n\nexport class Left<L, R = never> {\n\treadonly _tag = \"Left\"\n\tconstructor(public readonly left: L) {}\n\t*[Symbol.iterator](): Generator<Either<R, L>, R, any> {\n\t\treturn yield this\n\t}\n}\n\nexport class Right<R, L> {\n\treadonly _tag = \"Right\"\n\tconstructor(public readonly right: R) {}\n\t*[Symbol.iterator](): Generator<Either<R, L>, R, any> {\n\t\treturn yield this\n\t}\n}\n\nexport const Either = {\n\tleft<L, R = never>(l: L): Either<R, L> {\n\t\treturn new Left(l)\n\t},\n\n\tright<R, L = never>(r: R): Either<R, L> {\n\t\treturn new Right(r)\n\t},\n\n\tisLeft<R, L>(either: Either<R, L>): either is Left<L, R> {\n\t\treturn either._tag === \"Left\"\n\t},\n\n\tisRight<R, L>(either: Either<R, L>): either is Right<R, L> {\n\t\treturn either._tag === \"Right\"\n\t},\n\n\tmatch<R, L, T>(\n\t\teither: Either<R, L>,\n\t\tpatterns: {\n\t\t\tonLeft: (left: L) => T\n\t\t\tonRight: (right: R) => T\n\t\t},\n\t): T {\n\t\tif (Either.isLeft(either)) {\n\t\t\treturn patterns.onLeft(either.left)\n\t\t}\n\t\treturn patterns.onRight(either.right)\n\t},\n\n\tgen<R, L>(f: () => Generator<Either<any, L>, R, any>): Either<R, L> {\n\t\tconst iterator = f()\n\t\tlet current = iterator.next()\n\n\t\twhile (!current.done) {\n\t\t\tconst either = current.value\n\t\t\tif (Either.isLeft(either)) {\n\t\t\t\treturn either\n\t\t\t}\n\t\t\tcurrent = iterator.next(either.right)\n\t\t}\n\n\t\treturn Either.right(current.value)\n\t},\n}\n","import type { Either } from \"./either\"\nimport type { Prettify } from \"./types\"\n\nexport type ParserContext<Ctx = {}> = Prettify<\n\tCtx & {\n\t\tdebug?: boolean\n\t\tsource: string\n\t}\n>\n\nexport type ParserOptions = { name?: string }\n\nexport class ParserError {\n\tconstructor(\n\t\tpublic message: string,\n\t\tpublic expected: string[],\n\t\tpublic found?: string,\n\t) {}\n}\n\nexport type ParserOutput<T, Ctx = {}> = {\n\tstate: ParserState<Ctx>\n\tresult: Either<T, ParserError>\n}\n\nexport type SourcePosition = {\n\tline: number\n\tcolumn: number\n\toffset: number\n}\n\nexport type ParserState<Ctx = {}> = {\n\tremaining: string\n\tpos: SourcePosition\n\tcontext: ParserContext<Ctx>\n}\n\n/**\n * Utility object containing static methods for creating and manipulating parser state.\n */\nexport const State = {\n\t/**\n\t * Creates a new parser state from an input string.\n\t *\n\t * @param input - The input string to parse\n\t * @returns A new parser state initialized at the start of the input\n\t */\n\tfromInput<Ctx = {}>(\n\t\tinput: string,\n\t\tcontext: ParserContext<Ctx>,\n\t): ParserState<Ctx> {\n\t\treturn {\n\t\t\tremaining: input,\n\t\t\tpos: { line: 1, column: 1, offset: 0 },\n\t\t\tcontext,\n\t\t}\n\t},\n\n\t/**\n\t * Creates a new state by consuming n characters from the current state.\n\t *\n\t * @param state - The current parser state\n\t * @param n - Number of characters to consume\n\t * @returns A new state with n characters consumed and position updated\n\t * @throws Error if attempting to consume more characters than remaining\n\t */\n\tconsume<Ctx = {}>(state: ParserState<Ctx>, n: number): ParserState<Ctx> {\n\t\tif (n === 0) return state\n\t\tif (n > state.remaining.length) {\n\t\t\tthrow new Error(\"Cannot consume more characters than remaining\")\n\t\t}\n\n\t\tconst consumed = state.remaining.slice(0, n)\n\t\tlet { line, column, offset } = state.pos\n\n\t\tfor (const char of consumed) {\n\t\t\tif (char === \"\\n\") {\n\t\t\t\tline++\n\t\t\t\tcolumn = 1\n\t\t\t} else {\n\t\t\t\tcolumn++\n\t\t\t}\n\t\t\toffset++\n\t\t}\n\n\t\treturn {\n\t\t\tremaining: state.remaining.slice(n),\n\t\t\tpos: { line, column, offset },\n\t\t\tcontext: state.context,\n\t\t}\n\t},\n\n\t/**\n\t * Creates a new state by consuming a specific string from the current state.\n\t *\n\t * @param state - The current parser state\n\t * @param str - The string to consume\n\t * @returns A new state with the string consumed and position updated\n\t * @throws Error if the input doesn't start with the specified string\n\t */\n\tconsumeString<Ctx = {}>(\n\t\tstate: ParserState<Ctx>,\n\t\tstr: string,\n\t): ParserState<Ctx> {\n\t\tif (!state.remaining.startsWith(str)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Cannot consume \"${str}\" - input \"${state.remaining}\" doesn't start with it`,\n\t\t\t)\n\t\t}\n\t\treturn State.consume(state, str.length)\n\t},\n\n\tmove<Ctx = {}>(state: ParserState<Ctx>, moveBy: number) {\n\t\treturn State.consume(\n\t\t\t{\n\t\t\t\t...state,\n\t\t\t\tremaining: state.context.source,\n\t\t\t\tpos: { line: 1, column: 1, offset: 0 },\n\t\t\t},\n\t\t\tstate.pos.offset + moveBy,\n\t\t)\n\t},\n\n\t/**\n\t * Creates a new state by consuming characters while a predicate is true.\n\t *\n\t * @param state - The current parser state\n\t * @param predicate - Function that tests each character\n\t * @returns A new state with matching characters consumed\n\t */\n\tconsumeWhile<Ctx = {}>(\n\t\tstate: ParserState<Ctx>,\n\t\tpredicate: (char: string) => boolean,\n\t): ParserState<Ctx> {\n\t\tlet i = 0\n\t\twhile (i < state.remaining.length && predicate(state.remaining[i])) {\n\t\t\ti++\n\t\t}\n\t\treturn State.consume(state, i)\n\t},\n\n\t/**\n\t * Gets the next n characters from the input without consuming them.\n\t *\n\t * @param state - The current parser state\n\t * @param n - Number of characters to peek (default: 1)\n\t * @returns The next n characters as a string\n\t */\n\tpeek<Ctx = {}>(state: ParserState<Ctx>, n: number = 1): string {\n\t\treturn state.remaining.slice(0, n)\n\t},\n\n\t/**\n\t * Checks if the parser has reached the end of input.\n\t *\n\t * @param state - The current parser state\n\t * @returns True if at end of input, false otherwise\n\t */\n\tisAtEnd<Ctx = {}>(state: ParserState<Ctx>): boolean {\n\t\treturn state.remaining.length === 0\n\t},\n\n\tprintPosition<Ctx = {}>(state: ParserState<Ctx>): string {\n\t\treturn `line ${state.pos.line}, column ${state.pos.column}, offset ${state.pos.offset}`\n\t},\n}\n","import { Either } from \"./either\"\nimport { Parser } from \"./parser\"\nimport { type ParserOutput, type ParserState, State } from \"./state\"\n/**\n * Creates a debug output for a parser's current state and result\n */\nexport function debugState<Ctx = {}>(\n\tlabel: string,\n\tstate: ParserState<Ctx>,\n\tresult: ParserOutput<any, Ctx>,\n\toptions: {\n\t\tinputPreviewLength?: number\n\t\tseparator?: string\n\t} = {},\n) {\n\tconst { inputPreviewLength = 20, separator = \"=\".repeat(40) } = options\n\n\tconsole.log(`\\n=== ${label} ===`)\n\tconsole.log(\"Position:\", State.printPosition(state))\n\tconsole.log(\n\t\t\"Input:\",\n\t\tJSON.stringify(\n\t\t\tstate.remaining.slice(0, inputPreviewLength) +\n\t\t\t\t(state.remaining.length > inputPreviewLength ? \"...\" : \"\"),\n\t\t),\n\t)\n\tconsole.log(\n\t\t\"Result:\",\n\t\tEither.isRight(result.result)\n\t\t\t? `Success: ${JSON.stringify(result.result.right.value)}`\n\t\t\t: `Error: ${result.result.left.message}`,\n\t)\n\tconsole.log(separator)\n}\n\n/**\n * Adds debug output to a parser\n */\nexport function debug<T, Ctx = {}>(\n\tparser: Parser<T, Ctx>,\n\tlabel: string,\n): Parser<T, Ctx> {\n\treturn parser.tap(({ state, result }) => debugState(label, state, result))\n}\n\n/**\n * Creates a parser that logs its input state and continues\n */\nexport function trace<Ctx = {}>(label: string): Parser<void, Ctx> {\n\treturn new Parser((state) => {\n\t\tconsole.log(`\\n[TRACE] ${label}`)\n\t\tconsole.log(\"Position:\", State.printPosition(state))\n\t\tconsole.log(\"Remaining:\", JSON.stringify(state.remaining))\n\t\treturn Parser.succeed(undefined, state)\n\t})\n}\n\n/**\n * Adds breakpoints to a parser for step-by-step debugging\n */\nexport function breakpoint<T, Ctx = {}>(\n\tparser: Parser<T, Ctx>,\n\tlabel: string,\n): Parser<T, Ctx> {\n\treturn parser.tap(({ state, result }) => {\n\t\tdebugState(label, state, result)\n\t\t// eslint-disable-next-line no-debugger\n\t\tdebugger\n\t})\n}\n\n/**\n * Times how long a parser takes to run\n */\nexport function benchmark<T, Ctx = {}>(\n\tparser: Parser<T, Ctx>,\n\tlabel: string,\n): Parser<T, Ctx> {\n\treturn new Parser((state) => {\n\t\tconst start = performance.now()\n\t\tconst result = parser.run(state)\n\t\tconst end = performance.now()\n\t\tconsole.log(`\\n[BENCHMARK] ${label}: ${(end - start).toFixed(2)}ms`)\n\t\treturn result\n\t})\n}\n","import type { ParserError, ParserState, SourcePosition } from \"./state\"\n\nexport function printPosition(position: SourcePosition) {\n\treturn `line ${position.line}, column ${position.column}`\n}\n\nexport function printArrow(position: SourcePosition) {\n\tconst lineNumberDigits = position.line.toString().length\n\treturn \" \".repeat(lineNumberDigits + 3 + position.column - 1) + \"^\"\n}\n\nexport function printErrorContext<Ctx = {}>(\n\tstate: ParserState<Ctx>,\n\tmessage?: string,\n) {\n\treturn (\n\t\t\"Parser Error:\\n\" +\n\t\tprintErrorLine(state) +\n\t\t\"\\n\" +\n\t\tprintArrow(state.pos) +\n\t\t`${message ? `\\n${message}` : \"\"}`\n\t)\n}\n\nexport function printErrorLine<Ctx = {}>(state: ParserState<Ctx>) {\n\tconst lines = state.context.source.split(\"\\n\")\n\tconst lineNum = state.pos.line\n\tconst startLine = Math.max(0, lineNum - 1)\n\tconst endLine = lineNum\n\tconst relevantLines = lines.slice(startLine, endLine + 1)\n\tconst padding = lineNum.toString().length\n\n\treturn relevantLines\n\t\t.map((line, i) => {\n\t\t\tconst num = startLine + i + 1\n\t\t\tconst paddedNum = num.toString().padStart(padding, \" \")\n\t\t\treturn `${paddedNum} | ${line}`\n\t\t})\n\t\t.join(\"\\n\")\n}\n\nexport function printPositionWithOffset(position: SourcePosition) {\n\treturn `line ${position.line}, column ${position.column}, offset ${position.offset}`\n}\n\nexport function getErrorLine<Ctx = {}>(\n\terror: ParserError,\n\tstate: ParserState<Ctx>,\n) {\n\tconst errorLine = state.context.source.slice(\n\t\tstate.pos.offset,\n\t\tstate.context.source.indexOf(\"\\n\", state.pos.offset),\n\t)\n\treturn errorLine\n}\n","import { debug } from \"./debug\"\nimport { Either } from \"./either\"\nimport { printErrorContext } from \"./errors\"\nimport {\n\ttype ParserContext,\n\tParserError,\n\ttype ParserOptions,\n\ttype ParserOutput,\n\ttype ParserState,\n\tState,\n} from \"./state\"\nimport type { Prettify } from \"./types\"\n\ntype BindResult<T, K extends string, B> = Prettify<\n\tT & {\n\t\t[k in K]: B\n\t}\n>\n\nexport class Parser<T, Ctx = {}> {\n\tconstructor(\n\t\t/**\n\t\t * @internal\n\t\t */\n\t\tpublic run: (state: ParserState<Ctx>) => ParserOutput<T, Ctx>,\n\t\tpublic options?: ParserOptions,\n\t) {}\n\n\tname(name: string) {\n\t\tthis.options = { ...this.options, name }\n\t\treturn this\n\t}\n\n\tstatic succeed<T, Ctx = {}>(\n\t\tvalue: T,\n\t\tstate: ParserState<Ctx>,\n\t): ParserOutput<T, Ctx> {\n\t\treturn {\n\t\t\tstate,\n\t\t\tresult: Either.right(value),\n\t\t}\n\t}\n\n\tstatic fail<Ctx = {}>(\n\t\terror: {\n\t\t\tmessage: string\n\t\t\texpected?: string[]\n\t\t\tfound?: string\n\t\t},\n\t\tstate: ParserState<Ctx>,\n\t): ParserOutput<never, Ctx> {\n\t\tconst errorMessage = error.message.startsWith(\"Parser Error:\")\n\t\t\t? error.message\n\t\t\t: printErrorContext(state, error.message)\n\n\t\treturn {\n\t\t\tstate,\n\t\t\tresult: Either.left(\n\t\t\t\tnew ParserError(errorMessage, error.expected ?? [], error.found),\n\t\t\t),\n\t\t}\n\t}\n\n\tstatic error<Ctx = {}>(\n\t\tmessage: string,\n\t\texpected: string[] = [],\n\t\tstateCallback?: (state: ParserState<Ctx>) => ParserState<Ctx>,\n\t): Parser<never, Ctx> {\n\t\treturn new Parser((state) => {\n\t\t\treturn Parser.fail(\n\t\t\t\t{ message, expected },\n\t\t\t\tstateCallback ? stateCallback(state) : state,\n\t\t\t)\n\t\t})\n\t}\n\n\t/**\n\t * Adds an error message to the parser\n\t * @param makeMessage - A function that returns an error message\n\t * @returns A new parser with the error message added\n\t */\n\twithError(\n\t\tmakeMessage: (errorCtx: {\n\t\t\terror: ParserError\n\t\t\tstate: ParserState<Ctx>\n\t\t}) => string,\n\t): Parser<T, Ctx> {\n\t\treturn new Parser<T, Ctx>((state) => {\n\t\t\tconst output = this.run(state)\n\t\t\tif (Either.isLeft(output.result)) {\n\t\t\t\treturn Parser.fail(\n\t\t\t\t\t{\n\t\t\t\t\t\tmessage: makeMessage({\n\t\t\t\t\t\t\terror: output.result.left,\n\t\t\t\t\t\t\tstate: output.state,\n\t\t\t\t\t\t}),\n\t\t\t\t\t\texpected: output.result.left.expected,\n\t\t\t\t\t},\n\t\t\t\t\toutput.state,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn output\n\t\t}, this.options)\n\t}\n\n\tparse(\n\t\tinput: string,\n\t\tcontext = { source: input } as ParserContext<Ctx>,\n\t): ParserOutput<T, Ctx> {\n\t\tconst { result, state } = this.run(State.fromInput(input, context))\n\t\tif (Either.isLeft(result)) {\n\t\t\treturn Parser.fail(result.left, state)\n\t\t}\n\t\treturn Parser.succeed(result.right, state)\n\t}\n\n\twithTrace(label: string): Parser<T, Ctx> {\n\t\treturn new Parser<T, Ctx>((state) => {\n\t\t\tif (!state.context?.debug) {\n\t\t\t\treturn this.run(state)\n\t\t\t}\n\t\t\treturn debug(this, label).run(state)\n\t\t}, this.options)\n\t}\n\n\tparseOrError(\n\t\tinput: string,\n\t\tcontext = { source: input } as ParserContext<Ctx>,\n\t) {\n\t\tconst { result } = this.run(State.fromInput(input, context))\n\t\tif (Either.isRight(result)) {\n\t\t\treturn result.right\n\t\t}\n\t\treturn result.left\n\t}\n\n\tparseOrThrow(\n\t\tinput: string,\n\t\tcontext = { source: input } as ParserContext<Ctx>,\n\t): T {\n\t\tconst { result } = this.parse(\n\t\t\tinput,\n\t\t\tcontext ?? {\n\t\t\t\tsource: input,\n\t\t\t},\n\t\t)\n\n\t\tif (Either.isLeft(result)) {\n\t\t\tthrow new Error(result.left.message)\n\t\t}\n\t\treturn result.right\n\t}\n\n\tmap<B>(f: (a: T) => B): Parser<B, Ctx> {\n\t\treturn new Parser<B, Ctx>((state) => {\n\t\t\tconst { result, state: newState } = this.run(state)\n\t\t\tif (Either.isLeft(result)) {\n\t\t\t\treturn Parser.fail(result.left, state)\n\t\t\t}\n\t\t\treturn Parser.succeed(f(result.right), newState)\n\t\t})\n\t}\n\n\tflatMap<B>(f: (a: T) => Parser<B, Ctx>): Parser<B, Ctx> {\n\t\treturn new Parser<B, Ctx>((state) => {\n\t\t\tconst { result, state: newState } = this.run(state)\n\t\t\tif (Either.isLeft(result)) {\n\t\t\t\treturn Parser.fail(result.left, newState)\n\t\t\t}\n\t\t\tconst nextParser = f(result.right)\n\t\t\treturn nextParser.run(newState)\n\t\t})\n\t}\n\n\tstatic pure = <A>(a: A): Parser<A> =>\n\t\tnew Parser((state) => Parser.succeed(a, state))\n\n\tstatic Do = Parser.pure({})\n\n\t/**\n\t * Creates a new parser that lazily evaluates the given function.\n\t * This is useful for creating recursive parsers.\n\t *\n\t * @param fn - A function that returns a parser\n\t * @returns A new parser that evaluates the function when parsing\n\t * @template T The type of value produced by the parser\n\t *\n\t * @example\n\t * ```ts\n\t * // Create a recursive parser for nested parentheses\n\t * const parens: Parser<string> = Parser.lazy(() =>\n\t * between(\n\t * char('('),\n\t * char(')'),\n\t * parens\n\t * )\n\t * )\n\t * ```\n\t */\n\tstatic lazy<T>(fn: () => Parser<T>): Parser<T> {\n\t\treturn new Parser((state) => {\n\t\t\tconst parser = fn()\n\t\t\treturn parser.run(state)\n\t\t})\n\t}\n\n\tzip<B>(parserB: Parser<B, Ctx>): Parser<[T, B], Ctx> {\n\t\treturn new Parser((state) => {\n\t\t\tconst { result: a, state: stateA } = this.run(state)\n\t\t\tif (Either.isLeft(a)) {\n\t\t\t\treturn Parser.fail(a.left, stateA)\n\t\t\t}\n\t\t\tconst { result: b, state: stateB } = parserB.run(stateA)\n\t\t\tif (Either.isLeft(b)) {\n\t\t\t\treturn Parser.fail(b.left, stateB)\n\t\t\t}\n\t\t\treturn Parser.succeed([a.right, b.right], stateB)\n\t\t})\n\t}\n\n\tthen<B>(parserB: Parser<B, Ctx>): Parser<B, Ctx> {\n\t\treturn this.zip(parserB).map(([_, b]) => b)\n\t}\n\n\tzipRight = this.then\n\n\tthenDiscard<B>(parserB: Parser<B, Ctx>): Parser<T, Ctx> {\n\t\treturn this.zip(parserB).map(([a, _]) => a)\n\t}\n\n\tzipLeft = this.thenDiscard\n\n\tbind<K extends string, B>(\n\t\tk: K,\n\t\tother: Parser<B, Ctx> | ((a: T) => Parser<B, Ctx>),\n\t): Parser<BindResult<T, K, B>, Ctx> {\n\t\treturn new Parser<BindResult<T, K, B>, Ctx>((state) => {\n\t\t\tconst { result: resultA, state: stateA } = this.run(state)\n\t\t\tif (Either.isLeft(resultA)) {\n\t\t\t\treturn Parser.fail(resultA.left, stateA)\n\t\t\t}\n\t\t\tconst nextParser = other instanceof Parser ? other : other(resultA.right)\n\t\t\tconst { result: resultB, state: stateB } = nextParser.run(stateA)\n\t\t\tif (Either.isLeft(resultB)) {\n\t\t\t\treturn Parser.fail(resultB.left, stateB)\n\t\t\t}\n\t\t\treturn Parser.succeed(\n\t\t\t\t{ ...resultA.right, [k]: resultB.right } as BindResult<T, K, B>,\n\t\t\t\tstateB,\n\t\t\t)\n\t\t}, this.options)\n\t}\n\n\t*[Symbol.iterator](): Generator<Parser<T, Ctx>, T, any> {\n\t\treturn yield this\n\t}\n\n\t/**\n\t * Adds a tap point to observe the current state and result during parsing.\n\t * Useful for debugging parser behavior.\n\t *\n\t * @param callback - Function called with current state and result\n\t * @returns The same parser with the tap point added\n\t */\n\ttap(\n\t\tcallback: (args: {\n\t\t\tstate: ParserState<Ctx>\n\t\t\tresult: ParserOutput<T, Ctx>\n\t\t}) => void,\n\t): Parser<T, Ctx> {\n\t\treturn new Parser((state) => {\n\t\t\tconst result = this.run(state)\n\t\t\tcallback({ state, result })\n\t\t\treturn result\n\t\t}, this.options)\n\t}\n\n\tstatic gen<T, Ctx = unknown>(\n\t\tf: () => Generator<Parser<any, Ctx>, T, any>,\n\t): Parser<T, Ctx> {\n\t\treturn new Parser<T, Ctx>((state) => {\n\t\t\tconst iterator = f()\n\t\t\tlet current = iterator.next()\n\t\t\tlet currentState: ParserState<Ctx> = state\n\t\t\twhile (!current.done) {\n\t\t\t\tconst { result, state: updatedState } = current.value.run(currentState)\n\t\t\t\tif (Either.isLeft(result)) {\n\t\t\t\t\treturn Parser.fail(result.left, updatedState)\n\t\t\t\t}\n\t\t\t\tcurrentState = updatedState\n\t\t\t\tcurrent = iterator.next(result.right)\n\t\t\t}\n\t\t\treturn Parser.succeed(current.value, currentState)\n\t\t})\n\t}\n\n\ttrim(parser: Parser<any, Ctx>) {\n\t\treturn parser.then(this).thenDiscard(parser)\n\t}\n\n\ttrimLeft(parser: Parser<any, Ctx>): Parser<T, Ctx> {\n\t\treturn parser.then(this)\n\t}\n\n\ttrimRight(parser: Parser<any, Ctx>): Parser<T, Ctx> {\n\t\treturn this.thenDiscard(parser)\n\t}\n}\n\nexport function parser<T, Ctx = unknown>(\n\tf: () => Generator<Parser<any, Ctx>, T, any>,\n): Parser<T, Ctx> {\n\treturn new Parser<T, Ctx>((state) => {\n\t\tconst iterator = f()\n\t\tlet current = iterator.next()\n\t\tlet currentState: ParserState<Ctx> = state\n\t\twhile (!current.done) {\n\t\t\tconst { result, state: updatedState } = current.value.run(currentState)\n\t\t\tif (Either.isLeft(result)) {\n\t\t\t\treturn Parser.fail(result.left, updatedState)\n\t\t\t}\n\t\t\tcurrentState = updatedState\n\t\t\tcurrent = iterator.next(result.right)\n\t\t}\n\t\treturn Parser.succeed(current.value, currentState)\n\t})\n}\n","import { Either } from \"./either\"\nimport { Parser } from \"./parser\"\n\nexport type Chain<Ctx = {}> = {\n\t<T, U>(parser: Parser<T, Ctx>, fn1: (value: T) => Parser<U>): Parser<U>\n\t<T1, T2, T3>(\n\t\tparser: Parser<T1, Ctx>,\n\t\tfn1: (value: T1) => Parser<T2, Ctx>,\n\t\tfn2: (value: T2) => Parser<T3, Ctx>,\n\t): Parser<T3>\n\t<T1, T2, T3, T4>(\n\t\tparser: Parser<T1, Ctx>,\n\t\tfn1: (value: T1) => Parser<T2, Ctx>,\n\t\tfn2: (value: T2) => Parser<T3, Ctx>,\n\t\tfn3: (value: T3) => Parser<T4, Ctx>,\n\t): Parser<T4>\n\t<T1, T2, T3, T4, T5>(\n\t\tparser: Parser<T1, Ctx>,\n\t\tfn1: (value: T1) => Parser<T2, Ctx>,\n\t\tfn2: (value: T2) => Parser<T3, Ctx>,\n\t\tfn3: (value: T3) => Parser<T4, Ctx>,\n\t\tfn4: (value: T4) => Parser<T5, Ctx>,\n\t): Parser<T5>\n\t<T1, T2, T3, T4, T5, T6>(\n\t\tparser: Parser<T1, Ctx>,\n\t\tfn1: (value: T1) => Parser<T2, Ctx>,\n\t\tfn2: (value: T2) => Parser<T3, Ctx>,\n\t\tfn3: (value: T3) => Parser<T4, Ctx>,\n\t\tfn4: (value: T4) => Parser<T5, Ctx>,\n\t\tfn5: (value: T5) => Parser<T6, Ctx>,\n\t): Parser<T6>\n\t<T1, T2, T3, T4, T5, T6, T7>(\n\t\tparser: Parser<T1, Ctx>,\n\t\tfn1: (value: T1) => Parser<T2, Ctx>,\n\t\tfn2: (value: T2) => Parser<T3, Ctx>,\n\t\tfn3: (value: T3) => Parser<T4, Ctx>,\n\t\tfn4: (value: T4) => Parser<T5, Ctx>,\n\t\tfn5: (value: T5) => Parser<T6, Ctx>,\n\t\tfn6: (value: T6) => Parser<T7, Ctx>,\n\t): Parser<T7>\n\t<T1, T2, T3, T4, T5, T6, T7, T8>(\n\t\tparser: Parser<T1, Ctx>,\n\t\tfn1: (value: T1) => Parser<T2, Ctx>,\n\t\tfn2: (value: T2) => Parser<T3, Ctx>,\n\t\tfn3: (value: T3) => Parser<T4, Ctx>,\n\t\tfn4: (value: T4) => Parser<T5, Ctx>,\n\t\tfn5: (value: T5) => Parser<T6, Ctx>,\n\t\tfn6: (value: T6) => Parser<T7, Ctx>,\n\t\tfn7: (value: T7) => Parser<T8, Ctx>,\n\t): Parser<T8>\n\t<T1, T2, T3, T4, T5, T6, T7, T8, T9>(\n\t\tparser: Parser<T1, Ctx>,\n\t\tfn1: (value: T1) => Parser<T2, Ctx>,\n\t\tfn2: (value: T2) => Parser<T3, Ctx>,\n\t\tfn3: (value: T3) => Parser<T4, Ctx>,\n\t\tfn4: (value: T4) => Parser<T5, Ctx>,\n\t\tfn5: (value: T5) => Parser<T6, Ctx>,\n\t\tfn6: (value: T6) => Parser<T7, Ctx>,\n\t\tfn7: (value: T7) => Parser<T8, Ctx>,\n\t\tfn8: (value: T8) => Parser<T9, Ctx>,\n\t): Parser<T9>\n\t<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(\n\t\tparser: Parser<T1, Ctx>,\n\t\tfn1: (value: T1) => Parser<T2, Ctx>,\n\t\tfn2: (value: T2) => Parser<T3, Ctx>,\n\t\tfn3: (value: T3) => Parser<T4, Ctx>,\n\t\tfn4: (value: T4) => Parser<T5, Ctx>,\n\t\tfn5: (value: T5) => Parser<T6, Ctx>,\n\t\tfn6: (value: T6) => Parser<T7, Ctx>,\n\t\tfn7: (value: T7) => Parser<T8, Ctx>,\n\t\tfn8: (value: T8) => Parser<T9, Ctx>,\n\t\tfn9: (value: T9) => Parser<T10, Ctx>,\n\t): Parser<T10>\n}\n\nexport const chain = <Ctx = {}>(\n\tparser: Parser<any, Ctx>,\n\t...fns: Array<(value: any) => Parser<any, Ctx>>\n): Chain<Ctx> => {\n\treturn new Parser<any, Ctx>((state) => {\n\t\tlet result = parser.run(state)\n\t\tfor (const fn of fns) {\n\t\t\tconst { result: parserResult, state: newState } = result\n\t\t\tif (Either.isLeft(parserResult)) {\n\t\t\t\treturn Parser.fail(parserResult.left, newState)\n\t\t\t}\n\t\t\tconst value = parserResult.right\n\t\t\tresult = fn(value).run(newState)\n\t\t}\n\t\treturn result\n\t}) as any\n}\n","import { Either } from \"./either\"\nimport { Parser } from \"./parser\"\nimport { type ParserState, State } from \"./state\"\n\n/**\n * Creates a parser that looks ahead in the input stream without consuming any input.\n * The parser will succeed with the result of the given parser but won't advance the input position.\n *\n * @param parser - The parser to look ahead with\n * @returns A new parser that peeks at the input without consuming it\n * ```ts\n * const parser = lookAhead(char('a'))\n * parser.run('abc') // Right(['a', {...}])\n * // Input position remains at 'abc', 'a' is not consumed\n * ```\n */\nexport function lookAhead<T, Ctx = {}>(\n\tparser: Parser<T, Ctx>,\n): Parser<T | undefined, Ctx> {\n\treturn new Parser((state) => {\n\t\tconst { result } = parser.run(state)\n\t\tif (Either.isRight(result)) {\n\t\t\treturn Parser.succeed(result.right, state)\n\t\t}\n\t\treturn Parser.succeed(undefined, state)\n\t})\n}\n\n/**\n * Creates a parser that succeeds only if the given parser fails to match.\n * If the parser succeeds, this parser fails with an error message.\n *\n * @param parser - The parser that should not match\n * @returns A new parser that succeeds only if the input parser fails\n * ```ts\n * const notA = notFollowedBy(char('a'))\n * notA.run('bcd') // Right([true, {...}]) - Succeeds because 'a' is not found\n * notA.run('abc') // Left(error) - Fails because 'a' is found\n * ```\n */\nexport function notFollowedBy<T, Ctx = {}>(\n\tparser: Parser<T, Ctx>,\n): Parser<boolean, Ctx> {\n\treturn new Parser((state) => {\n\t\tconst { result, state: newState } = parser.run(state)\n\t\tif (Either.isRight(result)) {\n\t\t\tif (parser.options?.name) {\n\t\t\t\tconst message = `Found ${parser.options.name} when it should not appear here`\n\t\t\t\treturn Parser.fail({ message, expected: [] }, newState)\n\t\t\t}\n\t\t\treturn Parser.fail(\n\t\t\t\t{\n\t\t\t\t\tmessage: \"Expected not to follow\",\n\t\t\t\t\texpected: [],\n\t\t\t\t\tfound: state.remaining.at(0),\n\t\t\t\t},\n\t\t\t\tnewState,\n\t\t\t)\n\t\t}\n\t\treturn Parser.succeed(true, newState)\n\t})\n}\n\n/**\n * Creates a parser that matches an exact string in the input.\n *\n * @param str - The string to match\n * @returns A parser that matches and consumes the exact string\n * ```ts\n * const parser = string(\"hello\")\n * parser.run(\"hello world\") // Right([\"hello\", {...}])\n * parser.run(\"goodbye\") // Left(error)\n * ```\n */\nexport const string = <Ctx = {}>(str: string): Parser<string, Ctx> =>\n\tnew Parser(\n\t\t(state) => {\n\t\t\tif (state.remaining.startsWith(str)) {\n\t\t\t\treturn Parser.succeed(str, State.consume(state, str.length))\n\t\t\t}\n\n\t\t\tconst message =\n\t\t\t\t`Expected '${str}', ` +\n\t\t\t\t`but found '${state.remaining.slice(0, str.length)}'`\n\n\t\t\treturn Parser.fail(\n\t\t\t\t{\n\t\t\t\t\tmessage,\n\t\t\t\t\texpected: [str],\n\t\t\t\t\tfound: state.remaining.slice(0, str.length),\n\t\t\t\t},\n\t\t\t\tstate,\n\t\t\t)\n\t\t},\n\t\t{ name: str },\n\t)\n\n/**\n * Creates a parser that matches an exact string literal type.\n * Similar to string parser but preserves the literal type information.\n *\n * @param str - The string literal to match\n * @returns A parser that matches and consumes the exact string with preserved type\n * ```ts\n * const parser = narrowedString(\"hello\") // Parser<\"hello\">\n * parser.run(\"hello world\") // Right([\"hello\", {...}])\n * parser.run(\"goodbye\") // Left(error)\n * ```\n */\nexport function narrowedString<const T extends string, Ctx>(\n\tstr: T,\n): Parser<T, Ctx> {\n\treturn string(str) as any\n}\n\n/**\n * Creates a parser that matches a single character.\n *\n * @param ch - The character to match\n * @returns A parser that matches and consumes a single character\n * ```ts\n * const parser = char(\"a\")\n * parser.run(\"abc\") // Right([\"a\", {...}])\n * parser.run(\"xyz\") // Left(error)\n * ```\n */\nexport const char = <T extends string, Ctx = {}>(ch: T): Parser<T, Ctx> => {\n\treturn new Parser(\n\t\t(state) => {\n\t\t\tif (ch.length !== 1) {\n\t\t\t\treturn Parser.fail(\n\t\t\t\t\t{ message: \"Incorrect usage of char parser.\", expected: [ch] },\n\t\t\t\t\tstate,\n\t\t\t\t)\n\t\t\t}\n\t\t\tif (state.remaining[0] === ch) {\n\t\t\t\treturn Parser.succeed(ch, State.consume(state, 1))\n\t\t\t}\n\n\t\t\tconst message = `Expected ${ch} but found ${state.remaining.at(0)}.`\n\t\t\treturn Parser.fail(\n\t\t\t\t{ message, expected: [ch], found: state.remaining.at(0) },\n\t\t\t\tstate,\n\t\t\t)\n\t\t},\n\t\t{ name: ch },\n\t)\n}\n\n/**\n * A parser that matches any single alphabetic character (a-z, A-Z).\n *\n * ```ts\n * const parser = alphabet\n * parser.run(\"abc\") // Right([\"a\", {...}])\n * parser.run(\"123\") // Left(error)\n * ```\n */\nexport const alphabet = new Parser(\n\t(state) => {\n\t\tif (State.isAtEnd(state)) {\n\t\t\treturn Parser.fail(\n\t\t\t\t{ message: \"Unexpected end of input\", expected: [] },\n\t\t\t\tstate,\n\t\t\t)\n\t\t}\n\t\tconst first = state.remaining[0]\n\t\tif (first && /^[a-zA-Z]$/.test(first)) {\n\t\t\treturn Parser.succeed(first, State.consume(state, 1))\n\t\t}\n\t\tconst message = `Expected alphabetic character, but got '${first}'`\n\t\treturn Parser.fail(\n\t\t\t{ message, expected: [], found: state.remaining[0] },\n\t\t\tstate,\n\t\t)\n\t},\n\t{ name: \"alphabet\" },\n)\n\n/**\n * A parser that matches any single digit character (0-9).\n *\n * ```ts\n * const parser = digit\n * parser.run(\"123\") // Right([\"1\", {...}])\n * parser.run(\"abc\") // Left(error)\n * ```\n */\nexport const digit = new Parser(\n\t(state) => {\n\t\tif (State.isAtEnd(state)) {\n\t\t\treturn Parser.fail(\n\t\t\t\t{ message: \"Unexpected end of input\", expected: [] },\n\t\t\t\tstate,\n\t\t\t)\n\t\t}\n\t\tconst first = state.remaining[0]\n\t\tif (first && /^[0-9]$/.test(first)) {\n\t\t\treturn Parser.succeed(first, State.consume(state, 1))\n\t\t}\n\t\tconst message = `Expected digit, but got '${first}'`\n\t\treturn Parser.fail(\n\t\t\t{ message, expected: [], found: state.remaining[0] },\n\t\t\tstate,\n\t\t)\n\t},\n\t{ name: \"digit\" },\n)\n\n/**\n * Creates a parser that matches zero or more occurrences of elements separated by a separator.\n *\n * @param sepParser - Parser for the separator between elements\n * @param parser - Parser for the elements\n * @returns A parser that produces an array of matched elements\n *\n * ```ts\n * const parser = sepBy(char(','), digit)\n * parser.run(\"1,2,3\") // Right([[\"1\", \"2\", \"3\"], {...}])\n * parser.run(\"\") // Right([[], {...}])\n * ```\n */\n// TODO: fix this\nexport function sepBy<S, T, Ctx>(\n\tsepParser: Parser<S, Ctx>,\n\tparser: Parser<T, Ctx>,\n): Parser<T[], Ctx> {\n\treturn new Parser((state) => {\n\t\tconst results: T[] = []\n\t\tlet currentState = state\n\n\t\tconst { result: firstResult, state: firstState } = parser.run(currentState)\n\t\tif (Either.isLeft(firstResult)) {\n\t\t\treturn Parser.fail(firstResult.left, firstState)\n\t\t}\n\n\t\tresults.push(firstResult.right)\n\t\tcurrentState = firstState\n\n\t\twhile (true) {\n\t\t\tconst { result: sepResult, state: sepState } = sepParser.run(currentState)\n\t\t\tif (Either.isLeft(sepResult)) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tcurrentState = sepState\n\n\t\t\tconst { result: itemResult, state: itemResultState } =\n\t\t\t\tparser.run(currentState)\n\t\t\tif (Either.isLeft(itemResult)) {\n\t\t\t\treturn Parser.fail(itemResult.left, itemResultState)\n\t\t\t}\n\t\t\tresults.push(itemResult.right)\n\t\t\tcurrentState = itemResultState\n\t\t}\n\n\t\treturn Parser.succeed(results, currentState)\n\t})\n}\n\n/**\n * Creates a parser that matches content between two string delimiters.\n *\n * @param start - The opening delimiter string\n * @param end - The closing delimiter string\n * @param parser - The parser for the content between delimiters\n * @returns A parser that matches content between delimiters\n *\n * ```ts\n * const parser = between('(', ')', digit)\n * parser.run('(5)') // Right(['5', {...}])\n * parser.run('5') // Left(error)\n * ```\n */\nexport function between<T, Ctx = {}>(\n\tstart: Parser<any, Ctx>,\n\tend: Parser<any, Ctx>,\n\tparser: Parser<T, Ctx>,\n): Parser<any, Ctx> {\n\treturn new Parser((state) => {\n\t\t// Parse opening delimiter\n\t\tconst startResult = start.run(state)\n\t\tif (Either.isLeft(startResult.result)) {\n\t\t\treturn startResult\n\t\t}\n\n\t\t// Parse content\n\t\tconst contentResult = parser.run(startResult.state)\n\t\tif (Either.isLeft(contentResult.result)) {\n\t\t\treturn contentResult\n\t\t}\n\n\t\t// Parse closing delimiter\n\t\tconst endResult = end.run(contentResult.state)\n\t\tif (Either.isLeft(endResult.result)) {\n\t\t\treturn endResult\n\t\t}\n\n\t\t// Return the content and final state\n\t\treturn Parser.succeed(contentResult.result.right, endResult.state)\n\t})\n}\n\nexport function anyChar<Ctx = {}>() {\n\treturn new Parser<string, Ctx>((state) => {\n\t\tif (State.isAtEnd(state)) {\n\t\t\treturn Parser.fail(\n\t\t\t\t{ message: \"Unexpected end of input\", expected: [] },\n\t\t\t\tstate,\n\t\t\t)\n\t\t}\n\t\treturn Parser.succeed(state.remaining[0], State.consume(state, 1))\n\t})\n}\n\n/**\n * Internal helper function for creating repetition parsers.\n *\n * @param count - Minimum number of repetitions required\n * @returns A function that creates a parser matching multiple occurrences\n */\nfunction many_<S, T, Ctx = {}>(count: number) {\n\treturn (\n\t\tparser: Parser<T, Ctx>,\n\t\tseparator?: Parser<S, Ctx>,\n\t): Parser<T[], Ctx> => {\n\t\treturn new Parser((state) => {\n\t\t\tconst results: T[] = []\n\t\t\tlet currentState = state\n\n\t\t\twhile (true) {\n\t\t\t\t// Try to parse the next item\n\t\t\t\tconst itemResult = parser.run(currentState)\n\t\t\t\tif (Either.isLeft(itemResult.result)) {\n\t\t\t\t\t// If we have enough items, return success\n\t\t\t\t\tif (results.length >= count) {\n\t\t\t\t\t\treturn Parser.succeed(results, currentState)\n\t\t\t\t\t}\n\t\t\t\t\tconst message = `Expected at least ${count} occurrences, but only found ${results.length}`\n\t\t\t\t\treturn Parser.fail({ message, expected: [] }, itemResult.state)\n\t\t\t\t}\n\n\t\t\t\t// Add the item and update state\n\t\t\t\tconst { result: value, state: newState } = itemResult\n\t\t\t\tresults.push(value.right)\n\t\t\t\tcurrentState = newState\n\n\t\t\t\t// If we have a separator, try to parse it\n\t\t\t\tif (separator) {\n\t\t\t\t\tconst { result: sepResult, state } = separator.run(currentState)\n\t\t\t\t\tif (Either.isLeft(sepResult)) {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcurrentState = state\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (results.length >= count) {\n\t\t\t\treturn Parser.succeed(results, currentState)\n\t\t\t}\n\n\t\t\tconst message = `Expected at least ${count} occurrences, but only found ${results.length}`\n\t\t\treturn Parser.fail({ message, expected: [] }, currentState)\n\t\t})\n\t}\n}\n\n/**\n * Creates a parser that matches zero or more occurrences of the input parser.\n *\n * @param parser - The parser to repeat\n * @returns A parser that produces an array of all matches\n */\nexport const many0 = <S, T, Ctx = {}>(\n\tparser: Parser<T, Ctx>,\n\tseparator?: Parser<S, Ctx>,\n) => many_<S, T, Ctx>(0)(parser, separator)\n\n/**\n * Creates a parser that matches one or more occurrences of the input parser.\n *\n * @param parser - The parser to repeat\n * @returns A parser that produces an array of all matches (at least one)\n */\nexport const many1 = <S, T, Ctx>(\n\tparser: Parser<T, Ctx>,\n\tseparator?: Parser<S, Ctx>,\n) => many_<S, T, Ctx>(1)(parser, separator)\n\n/**\n * Creates a parser that matches at least n occurrences of the input parser.\n *\n * @param parser - The parser to repeat\n * @param n - Number of required repetitions\n * @returns A parser that produces an array of at least n matches\n */\nexport const manyN = <S, T, Ctx>(\n\tparser: Parser<T, Ctx>,\n\tn: number,\n\tseparator?: Parser<S, Ctx>,\n) => many_<S, T, Ctx>(n)(parser, separator)\n\n/**\n * Creates a parser that matches exactly n occurrences of the input parser.\n *\n * @param parser - The parser to repeat\n * @param n - Number of required repetitions\n * @param separator - Optional parser to match between occurrences\n * @returns A parser that produces an array of exactly n matches\n */\n\nexport const manyNExact = <S, T, Ctx>(\n\tparser: Parser<T, Ctx>,\n\tn: number,\n\tseparator?: Parser<S, Ctx>,\n) =>\n\tParser.gen(function* () {\n\t\tconst results = yield* manyN(parser, n, separator)\n\t\tif (results.length !== n) {\n\t\t\tconst message = `Expected exactly ${n} occurrences, but found ${results.length}`\n\t\t\treturn yield* Parser.error<Ctx>(message)\n\t\t}\n\t\treturn results\n\t})\n\n/**\n * Internal helper function for creating skipping repetition parsers.\n *\n * @param count - Minimum number of repetitions required\n * @returns A function that creates a parser skipping multiple occurrences\n */\nfunction skipMany_<T, Ctx>(count: number) {\n\treturn (parser: Parser<T, Ctx>): Parser<undefined, Ctx> => {\n\t\treturn new Parser((state) => {\n\t\t\tlet currentState = state\n\t\t\tlet successes = 0\n\n\t\t\twhile (true) {\n\t\t\t\tconst { result, state: newState } = parser.run(currentState)\n\t\t\t\tif (Either.isLeft(result)) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tsuccesses++\n\t\t\t\tcurrentState = newState\n\t\t\t}\n\n\t\t\tif (successes >= count) {\n\t\t\t\treturn Parser.succeed(undefined, currentState)\n\t\t\t}\n\t\t\tconst message = `Expected at least ${count} occurrences, but only found ${successes}`\n\t\t\treturn Parser.fail({ message, expected: [] }, state)\n\t\t})\n\t}\n}\n\n/**\n * Creates a parser that skips zero or more occurrences of the input parser.\n *\n * @param parser - The parser to skip\n * @returns A parser that skips all matches\n */\nexport const skipMany0 = <T, Ctx = {}>(parser: Parser<T, Ctx>) =>\n\tskipMany_<T, Ctx>(0)(parser)\n\n/**\n * Creates a parser that skips one or more occurrences of the input parser.\n *\n * @param parser - The parser to skip\n * @returns A parser that skips all matches (requires at least one)\n */\nexport const skipMany1 = <T, Ctx>(parser: Parser<T, Ctx>) =>\n\tskipMany_<T, Ctx>(1)(parser)\n\n/**\n * Creates a parser that skips exactly n occurrences of the input parser.\n *\n * @param parser - The parser to skip\n * @param n - Number of required repetitions to skip\n * @returns A parser that skips exactly n matches\n */\nexport const skipManyN = <T, Ctx>(parser: Parser<T, Ctx>, n: number) =>\n\tskipMany_<T, Ctx>(n)(parser)\n\n/**\n * Creates a parser that skips input until the given parser succeeds.\n *\n * @param parser - The parser to look for\n * @returns A parser that skips input until a match is found\n */\nexport function skipUntil<T, Ctx = {}>(\n\tparser: Parser<T, Ctx>,\n): Parser<undefined, Ctx> {\n\treturn new Parser((state) => {\n\t\tlet currentState = state\n\n\t\twhile (!State.isAtEnd(currentState)) {\n\t\t\tconst { result, state: newState } = parser.run(currentState)\n\t\t\tif (Either.isRight(result)) {\n\t\t\t\treturn Parser.succeed(undefined, newState)\n\t\t\t}\n\t\t\tcurrentState = State.consume(currentState, 1)\n\t\t}\n\n\t\treturn Parser.succeed(undefined, currentState)\n\t})\n}\n\n/**\n * Creates a parser that takes input until the given parser succeeds.\n *\n * @param parser - The parser to look for\n * @returns A parser that takes input until a match is found\n */\nexport function takeUntil<T, Ctx = {}>(\n\tparser: Parser<T, Ctx>,\n): Parser<string, Ctx> {\n\treturn new Parser((state) => {\n\t\tlet currentState = state\n\t\tlet collected = \"\"\n\n\t\twhile (!State.isAtEnd(currentState)) {\n\t\t\tconst { result, state: newState } = parser.run(currentState)\n\t\t\tif (Either.isRight(result)) {\n\t\t\t\treturn Parser.succeed(collected, newState)\n\t\t\t}\n\t\t\tcollected += currentState.remaining[0]\n\t\t\tcurrentState = State.consume(currentState, 1)\n\t\t}\n\n\t\treturn Parser.succeed(collected, currentState)\n\t})\n}\n\n/**\n * Creates a parser that takes input until the given character is found.\n *\n * @param char - The character to look for\n * @returns A parser that takes input until the character is found\n */\nexport function parseUntilChar<Ctx = {}>(char: string): Parser<string, Ctx> {\n\treturn new Parser((state) => {\n\t\tif (char.length !== 1) {\n\t\t\treturn Parser.fail(\n\t\t\t\t{\n\t\t\t\t\tmessage: \"Incorrect usage of parseUntilChar parser.\",\n\t\t\t\t\texpected: [char],\n\t\t\t\t},\n\t\t\t\tstate,\n\t\t\t)\n\t\t}\n\t\tlet currentState = state\n\t\tlet collected = \"\"\n\n\t\twhile (!State.isAtEnd(currentState)) {\n\t\t\tif (currentState.remaining[0] === char) {\n\t\t\t\treturn Parser.succeed(collected, currentState)\n\t\t\t}\n\t\t\tcollected += currentState.remaining[0]\n\t\t\tcurrentState = State.consume(currentState, 1)\n\t\t}\n\n\t\tconst message = `Expected character ${char} but found ${collected}`\n\t\treturn Parser.fail({ message, expected: [char] }, currentState)\n\t})\n}\n\n/**\n * A parser that skips any number of space characters.\n */\nexport const skipSpaces = new Parser(\n\t(state) =>\n\t\tParser.succeed(\n\t\t\tundefined,\n\t\t\tState.consumeWhile(state, (char) => char === \" \"),\n\t\t),\n\t{ name: \"skipSpaces\" },\n)\n\n/**\n * Creates a parser that tries multiple parsers in order until one succeeds.\n *\n * @param parsers - Array of parsers to try\n * @returns A parser that succeeds if any of the input parsers succeed\n */\nexport function or<Parsers extends Parser<any, any>[], Ctx = {}>(\n\t...parsers: Parsers\n): Parser<Parsers[number] extends Parser<infer T, Ctx> ? T : never, Ctx> {\n\treturn new Parser((state) => {\n\t\t// const expectedNames: string[] = []\n\t\tfor (const parser of parsers) {\n\t\t\tconst { result, state: newState } = parser.run(state)\n\t\t\tif (Either.isRight(result)) {\n\t\t\t\treturn Parser.succeed(result.right, newState)\n\t\t\t}\n\t\t\t// if (parser.options?.name) {\n\t\t\t// \texpectedNames.push(parser.options.name)\n\t\t\t// }\n\t\t}\n\n\t\tconst message = `None of the ${parsers.length} choices could be satisfied`\n\t\treturn Parser.fail({ message }, state)\n\t})\n}\n\n/**\n * Creates a parser that optionally matches the input parser.\n * If the parser fails, returns undefined without consuming input.\n *\n * @param parser - The parser to make optional\n * @returns A parser that either succeeds with a value or undefined\n */\nexport function optional<T, Ctx = {}>(parser: Parser<T, Ctx>) {\n\treturn new Parser((state: ParserState<Ctx>) => {\n\t\tconst { result, state: newState } = parser.run(state)\n\t\tif (Either.isLeft(result)) {\n\t\t\treturn Parser.succeed(undefined, newState)\n\t\t}\n\t\t// return result\n\t\treturn Parser.succeed(result.right, newState)\n\t})\n}\n\ntype LastParser<T, Ctx = {}> = T extends [...any[], Parser<infer L, Ctx>]\n\t? L\n\t: never\n\n/**\n * Creates a parser that runs multiple parsers in sequence.\n * Returns the result of the last parser in the sequence.\n *\n * @param parsers - Array of parsers to run in sequence\n * @returns A parser that succeeds if all parsers succeed in sequence\n */\nexport function sequence<Parsers extends Parser<any>[], Ctx = {}>(\n\tparsers: [...Parsers],\n): Parser<LastParser<Parsers, Ctx>, Ctx> {\n\treturn new Parser((state: ParserState<Ctx>) => {\n\t\tconst results: Parsers[] = []\n\t\tlet currentState = state\n\n\t\tfor (const parser of parsers) {\n\t\t\tconst { result, state: newState } = parser.run(currentState)\n\t\t\tif (Either.isLeft(result)) {\n\t\t\t\treturn Parser.fail(result.left, newState)\n\t\t\t}\n\t\t\tresults.push(result.right)\n\t\t\t// TODO: fix this\n\t\t\t// @ts-expect-error this should be fine\n\t\t\tcurrentState = newState\n\t\t}\n\n\t\treturn Parser.succeed(results.at(-1), currentState) as any\n\t})\n}\n\n/**\n * Creates a parser that matches input against a regular expression.\n * The regex must match at the start of the input.\n *\n * @param re - The regular expression to match against\n * @returns A parser that matches the regex pattern\n */\nexport const regex = <Ctx = {}>(re: RegExp): Parser<string, Ctx> => {\n\t// Create a new RegExp without global flag to ensure consistent behavior\n\tconst nonGlobalRe = new RegExp(re.source, re.flags.replace(\"g\", \"\"))\n\n\treturn new Parser(\n\t\t(state) => {\n\t\t\tconst match = nonGlobalRe.exec(state.remaining)\n\t\t\tif (match && match.index === 0) {\n\t\t\t\tconst value = match[0]\n\t\t\t\treturn Parser.succeed(value, state)\n\t\t\t}\n\t\t\tconst message = `Expected ${re} but found ${state.remaining.slice(0, 10)}...`\n\t\t\treturn Parser.fail(\n\t\t\t\t{\n\t\t\t\t\tmessage,\n\t\t\t\t\texpected: [re.toString()],\n\t\t\t\t},\n\t\t\t\tstate,\n\t\t\t)\n\t\t},\n\t\t{ name: re.toString() },\n\t)\n}\n\nexport function zip<A, B>(\n\tparserA: Parser<A>,\n\tparserB: Parser<B>,\n): Parser<[A, B]> {\n\treturn parserA.zip(parserB)\n}\n\nexport function then<A, B>(parserA: Parser<A>, parserB: Parser<B>): Parser<B> {\n\treturn parserA.then(parserB)\n}\n\nexport const zipRight = then\n\nexport function thenDiscard<A, B>(\n\tparserA: Parser<A>,\n\tparserB: Parser<B>,\n): Parser<A> {\n\treturn parserA.thenDiscard(parserB)\n}\nexport const zipLeft = thenDiscard\n\n/**\n * Creates a parser that takes input until the given parser would succeed, without consuming the parser.\n *\n * @param parser - The parser to look for\n * @returns A parser that takes input until before a match would be found\n */\nexport function takeUpto<T>(parser: Parser<T>): Parser<string> {\n\treturn new Parser((state) => {\n\t\tlet currentState = state\n\t\tlet collected = \"\"\n\n\t\twhile (!State.isAtEnd(currentState)) {\n\t\t\tconst { result } = parser.run(currentState)\n\t\t\tif (Either.isRight(result)) {\n\t\t\t\treturn Parser.succeed(collected, currentState)\n\t\t\t}\n\t\t\tcollected += currentState.remaining[0]\n\t\t\tcurrentState = State.consume(currentState, 1)\n\t\t}\n\n\t\treturn Parser.succeed(collected, currentState)\n\t})\n}\n","import { Parser } from \"./parser\"\n\nexport const peekState = new Parser((s) => {\n\treturn Parser.succeed(s, s)\n})\n\nexport const peekRemaining = new Parser((s) => {\n\tconsole.log(s.remaining)\n\treturn Parser.succeed(s.remaining, s)\n})\n\nexport const peekAhead = (n: number) =>\n\tnew Parser((s) => {\n\t\treturn Parser.succeed(s.remaining.slice(0, n), s)\n\t})\n\nexport const peekLine = new Parser((s) => {\n\tconst restOfLine = s.remaining.slice(0, s.remaining.indexOf(\"\\n\"))\n\tconsole.log(restOfLine)\n\treturn Parser.succeed(restOfLine, s)\n})\n\nexport const peekUntil = (ch: string) =>\n\tnew Parser((s) => {\n\t\tconst index = s.remaining.indexOf(ch)\n\t\treturn Parser.succeed(s.remaining.slice(0, index), s)\n\t})\n"],"mappings":"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