From db97b1a9c42fe945ca37256f5ee18d52c6aa32b4 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Thu, 10 Aug 2023 18:18:46 +0530 Subject: feat: adds parser for simple expressions --- src/utils/adt.ts | 6 ++--- src/utils/parser-comb.ts | 63 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 src/utils/parser-comb.ts (limited to 'src/utils') diff --git a/src/utils/adt.ts b/src/utils/adt.ts index c19398b..e9a6c51 100644 --- a/src/utils/adt.ts +++ b/src/utils/adt.ts @@ -1,11 +1,9 @@ type TagValue = T extends Tag ? V : never export const match = - >(pattern: { + >(tag: T, pattern: { [key in T['tag'] | '_']?: (v: TagValue) => R - }) => - (tag: T): R => - ((pattern as any)[tag.tag] || (pattern._ as any))(tag.value) + }): R => ((pattern as any)[tag.tag] || (pattern._ as any))(tag.value) type Tag = { tag: N; value: V } export type Enum = { [N in keyof T]: Tag }[keyof T] diff --git a/src/utils/parser-comb.ts b/src/utils/parser-comb.ts new file mode 100644 index 0000000..9cea86f --- /dev/null +++ b/src/utils/parser-comb.ts @@ -0,0 +1,63 @@ +import { Enum, constructors, match } from './adt'; + +export type Result = Enum<{ Ok: V, Err: E }> +export const Result = constructors>() + +export const mapResult = (res: Result, fn: (_: A) => B): Result => + chainResult(res, a => Result.Ok(fn(a))) + +export const chainResult = (res: Result, fn: (_: A) => Result): Result => + match(res, { + Ok: a => fn(a), + Err: e => Result.Err(e), + }); + +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 => { + 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.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) + -- cgit v1.3.1