aboutsummaryrefslogtreecommitdiff
path: root/src/utils/parser-comb.ts
blob: f682068c7f89ac257f9385c6f3db88b9670d484a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import { match } from './adt';
import { Result, mapResult, chainResult } from './result';

export type ParseResult<T> = Result<{ value: T, input: string }, { error: string, input: string }>;

export type Parser<T> = (input: string) => ParseResult<T>;

export const regex = (re: RegExp): Parser<string> => 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<string> => 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 = <T>([parser, ...rest]: Array<Parser<T>>): Parser<T> => input => {
  if (rest.length === 0) return parser(input);
  const result = parser(input)
  return match(result, {
    Ok: () => result,
    Err: (_) => or(rest)(input),
  });
}

export const mapParseResult = <T, R>(parser: Parser<T>, fn: (_: { value: T, input: string }) => { value: R, input: string }): Parser<R> => input =>
  mapResult(parser(input), fn)

export const map = <T, R>(parser: Parser<T>, fn: (_: T) => R): Parser<R> =>
  mapParseResult(parser, ({ value, ...rest }) => ({ ...rest, value: fn(value) }));

export const zip2 = <A, B>(parserA: Parser<A>, parserB: Parser<B>): Parser<readonly [A, B]> => 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 = <A>(parserPrefix: Parser<any>, parser: Parser<A>): Parser<A> =>
  map(zip2(parserPrefix, parser), ([_, a]) => a);

export const suffixed = <A>(parser: Parser<A>, parserSuffix: Parser<any>): Parser<A> =>
  map(zip2(parser, parserSuffix), ([a, _]) => a);

export const between = <A>(prefix: Parser<any>, parser: Parser<A>, suffix: Parser<any>): Parser<A> =>
  suffixed(prefixed(prefix, parser), suffix)

export const many0 = <A>(parser: Parser<A>): Parser<Array<A>> => originalInput =>
  match(parser(originalInput), {
    Ok: ({ value, input }) => map(many0(parser), ls => [value, ...ls])(input),
    Err: ({ input }) => Result.Ok({ value: [], input }),
  })

export const many1 = <A>(parser: Parser<A>): Parser<Array<A>> => originalInput =>
  match(parser(originalInput), {
    Ok: ({ value, input }) => map(many0(parser), ls => [value, ...ls])(input),
    Err: err => Result.Err(err),
  })

export const sepBy = <A>(parser: Parser<A>, sepP: Parser<any>): Parser<Array<A>> => 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 = <A>(parser: Parser<A>): Parser<undefined | A> => input => {
  const result = parser(input)
  return match(result, {
    Ok: _ => result,
    Err: _ => Result.Ok({ value: undefined, input })
  })
}