import { match } from './adt' import { Result, mapResult, chainResult } from './result' export type ParseResult = Result< { value: T; input: string }, { error: string; input: string } > export type Parser = (input: string) => ParseResult export const regex = (re: RegExp): Parser => input => { if (input.length === 0) return Result.Err({ error: 'fuckedinput', input }) const res = input.match(re) if (!res) return Result.Err({ error: 'fucked', input }) return Result.Ok({ value: res[0], input: input.replace(re, '') }) } export const string = (str: string): Parser => input => { if (input.length === 0) return Result.Err({ error: 'fuckedinput', input }) if (!input.startsWith(str)) return Result.Err({ error: 'fuckedstring', input }) return Result.Ok({ value: str, input: input.slice(str.length) }) } export const or = ([parser, ...rest]: Array>): Parser => input => { if (rest.length === 0) return parser(input) const result = parser(input) return match(result, { Ok: () => result, Err: _ => or(rest)(input), }) } export const mapParseResult = ( parser: Parser, fn: (_: { value: T; input: string }) => { value: R; input: string }, ): Parser => input => mapResult(parser(input), fn) export const map = (parser: Parser, fn: (_: T) => R): Parser => mapParseResult(parser, ({ value, ...rest }) => ({ ...rest, value: fn(value), })) export const zip2 = (parserA: Parser, parserB: Parser): Parser => input => { // TODO: refactor please. shit code const resa: Result< { value: A; input: string }, { error: string; input: string } > = parserA(input) return chainResult(resa, ({ value: a, input: inputB }) => { const res: Result< { value: readonly [A, B]; input: string }, { error: string; input: string } > = map(parserB, b => [a, b] as const)(inputB) return res }) } export const prefixed = ( parserPrefix: Parser, parser: Parser, ): Parser => map(zip2(parserPrefix, parser), ([_, a]) => a) export const suffixed = ( parser: Parser, parserSuffix: Parser, ): Parser => map(zip2(parser, parserSuffix), ([a, _]) => a) export const between = ( prefix: Parser, parser: Parser, suffix: Parser, ): Parser => suffixed(prefixed(prefix, parser), suffix) export const many0 = (parser: Parser): Parser> => originalInput => match(parser(originalInput), { Ok: ({ value, input }) => map(many0(parser), ls => [value, ...ls])(input), Err: ({ input }) => Result.Ok({ value: [], input }), }) export const many1 = (parser: Parser): Parser> => originalInput => match(parser(originalInput), { Ok: ({ value, input }) => map(many0(parser), ls => [value, ...ls])(input), Err: err => Result.Err(err), }) export const sepBy = (parser: Parser, sepP: Parser): Parser> => originalInput => match(parser(originalInput), { Ok: ({ value, input }) => map(many0(prefixed(sepP, parser)), ls => [value, ...ls])(input), Err: _ => Result.Ok({ value: [], input: originalInput }), }) export const optional = (parser: Parser): Parser => input => { const result = parser(input) return match(result, { Ok: _ => result, Err: _ => Result.Ok({ value: undefined, input }), }) }