aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/parse-expr.ts50
-rw-r--r--tests/parse-expr.spec.ts31
2 files changed, 65 insertions, 16 deletions
diff --git a/src/parse-expr.ts b/src/parse-expr.ts
index ec387d6..1c5c08f 100644
--- a/src/parse-expr.ts
+++ b/src/parse-expr.ts
@@ -1,42 +1,61 @@
-import { Enum, constructors, match } from './utils/adt';
+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 }
+ Call: { name: string; args: Expr[] }
Identifier: string
- Chain: { exprs: Expr[] }
+ 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 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 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, ident => Expr.Identifier(ident))
+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 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 multiExpr = P.many1(exprParser)
+const multiExprParser = P.many1(exprParser)
export const parse = (input: string): Array<Expr> => {
- const res = multiExpr(input.trim());
+ const res = multiExprParser(input.trim())
return match(res, {
Ok: ({ value, input }) => {
if (input) {
- throw new Error(`Input not consumed completely here brosky: "${input}"`)
+ throw new Error(`Input not consumed completely here brosky: "${input}"`)
}
return value
},
@@ -45,4 +64,3 @@ export const parse = (input: string): Array<Expr> => {
},
})
}
-
diff --git a/tests/parse-expr.spec.ts b/tests/parse-expr.spec.ts
index 875af63..f873464 100644
--- a/tests/parse-expr.spec.ts
+++ b/tests/parse-expr.spec.ts
@@ -42,4 +42,35 @@ describe('parser', () => {
}),
])
})
+
+ it('should parse string literal', () => {
+ expect(parse(`"hello world toodles \' nice single quote there"`)).toEqual([
+ Expr.LiteralString(`hello world toodles \' nice single quote there`),
+ ])
+
+ expect(parse(` 'hello world toodles \" nice double quote there' `)).toEqual(
+ [Expr.LiteralString(`hello world toodles \" nice double quote there`)]
+ )
+ })
+
+ it('should parse var identifiers', () => {
+ expect(parse(`var(--hello, 'default')`)).toEqual([
+ Expr.Call({
+ name: 'var',
+ args: [Expr.VarIdentifier('--hello'), Expr.LiteralString(`default`)],
+ }),
+ ])
+
+ expect(parse(`calc(var(--hello))`)).toEqual([
+ Expr.Call({
+ name: 'calc',
+ args: [
+ Expr.Call({
+ name: 'var',
+ args: [Expr.VarIdentifier('--hello')],
+ }),
+ ],
+ }),
+ ])
+ })
})