aboutsummaryrefslogtreecommitdiff
path: root/src/parse-expr.ts
blob: dbad1a196305d074548807b5c3402fdd72f956e3 (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
import { Enum, constructors, match } from './utils/adt';
import * as P from './utils/parser-comb'

export type Expr = Enum<{
  Call: { name: string, args: Expr[] }
  Var: { name: string, defaultValue: Expr }
  Identifier: string
  LiteralString: string
}>

export const Expr = constructors<Expr>()

const whitespace = P.regex(/\s*/)
const consumeWhitespace = <A>(p: P.Parser<A>): P.Parser<A> => P.between(whitespace, p, whitespace)

const identifierParser = P.regex(/^[a-z][a-z0-9]+/i)
const identifierExprParser = P.map(identifierParser, ident => Expr.Identifier(ident))

const callParser = P.map(P.zip2(
  consumeWhitespace(identifierParser),
  P.between(P.string('('), consumeWhitespace(identifierParser), P.string(')')),
), ([name, rest]) => Expr.Call({ name, args: [Expr.Identifier(rest)] }))

const exprParser: P.Parser<Expr> = P.or([
  callParser,
  identifierExprParser,
])

export const parse = (input: string): Expr => {
  const res = exprParser(input.trim());
  return match(res, {
    Ok: ({ value }) => value,
    Err: ({ error, input }) => {
      throw new Error(`${error}.\n Left input: ${input.slice(0, 20)}...`)
    },
  })
}