aboutsummaryrefslogtreecommitdiff
path: root/src/parser.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser.ts')
-rw-r--r--src/parser.ts66
1 files changed, 66 insertions, 0 deletions
diff --git a/src/parser.ts b/src/parser.ts
new file mode 100644
index 0000000..1c5c08f
--- /dev/null
+++ b/src/parser.ts
@@ -0,0 +1,66 @@
+import { Enum, constructors, match } from './utils/adt'
+import * as P from './utils/parser-comb'
+
+export type Expr = Enum<{
+ Call: { name: string; args: Expr[] }
+ Identifier: string
+ VarIdentifier: 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 comma = consumeWhitespace(P.string(','))
+const parens = <A>(p: P.Parser<A>): P.Parser<A> =>
+ P.between(P.string('('), p, P.string(')'))
+const identifierParser = P.regex(/^[a-z][a-z0-9_-]*/i)
+const varIdentifierParser = P.regex(/^--[a-z][a-z0-9-]*/i)
+const singleQuote = P.string("'")
+const doubleQuote = P.string('"')
+
+const identifierExprParser = P.map(identifierParser, Expr.Identifier)
+const varIdentifierExprParser = P.map(varIdentifierParser, Expr.VarIdentifier)
+
+const callExprParser = (input: string) =>
+ P.map(
+ P.zip2(
+ consumeWhitespace(identifierParser),
+ parens(consumeWhitespace(P.sepBy(exprParser, comma)))
+ ),
+ ([name, args]) => Expr.Call({ name, args })
+ )(input)
+
+const stringLiteralParser: P.Parser<Expr> = P.map(
+ P.or([
+ P.between(singleQuote, P.regex(/^[^']*/), singleQuote),
+ P.between(doubleQuote, P.regex(/^[^"]*/), doubleQuote),
+ ]),
+ Expr.LiteralString
+)
+
+const exprParser: P.Parser<Expr> = P.or([
+ stringLiteralParser,
+ varIdentifierExprParser,
+ callExprParser,
+ identifierExprParser,
+])
+
+const multiExprParser = P.many1(exprParser)
+
+export const parse = (input: string): Array<Expr> => {
+ const res = multiExprParser(input.trim())
+ return match(res, {
+ Ok: ({ value, input }) => {
+ if (input) {
+ throw new Error(`Input not consumed completely here brosky: "${input}"`)
+ }
+ return value
+ },
+ Err: ({ error, input }) => {
+ throw new Error(`${error}.\n Left input: ${input.slice(0, 20)}...`)
+ },
+ })
+}