summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/index.ts57
-rw-r--r--src/parser.ts44
-rw-r--r--src/utils.ts5
3 files changed, 72 insertions, 34 deletions
diff --git a/src/index.ts b/src/index.ts
index 75bc1af..2ea40eb 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,16 +1,14 @@
-import { constant, flow, identity, pipe } from 'fp-ts/function'
+import { constant, pipe } from 'fp-ts/function'
+import { chain, fold, right } from 'fp-ts/lib/Either'
+import { match } from './utils'
import {
- andThen,
delimited,
- digit,
- integer,
many1,
mapTo,
- matchChar,
optional,
or,
- pair,
Parser,
+ ParserResult,
symbol,
tuple3,
} from './parser'
@@ -36,16 +34,13 @@ export const anyBool = mapTo(
)
export const truthy = mapTo(symbol('\\T'), constant({ tag: 'Truthy' } as Expr))
export const falsey = mapTo(symbol('\\F'), constant({ tag: 'Falsey' } as Expr))
-// export const optional = mapTo(symbol('?'), constant({ tag: 'Optional' } as Expr))
-// export const zeroOrMore = mapTo(symbol('*'), constant({ tag: 'ZeroOrMore' } as Expr))
-// export const oneOrMore = mapTo(symbol('+'), constant({ tag: 'OneOrMore' } as Expr))
type Expr =
| { tag: 'Start' }
| { tag: 'End' }
- | { tag: 'Optional' }
- | { tag: 'OneOrMore' }
- | { tag: 'ZeroOrMore' }
+ | { tag: 'Optional'; expr: Expr }
+ | { tag: 'OneOrMore'; expr: Expr }
+ | { tag: 'ZeroOrMore'; expr: Expr }
| { tag: 'NextItem' }
| { tag: 'AnyItem' }
| { tag: 'Or' }
@@ -56,13 +51,35 @@ type Expr =
| { tag: 'Falsey' }
| { tag: 'Group'; exprs: Expr[] }
+export const wrapQuantifiers: (e: ParserResult<Expr>) => ParserResult<Expr> =
+ chain(([expr, input]) =>
+ pipe(
+ input,
+ or([symbol('*'), symbol('+'), symbol('?')]),
+ fold(
+ () => right([expr, input]),
+ ([c, inp]) =>
+ pipe(
+ c,
+ match<Expr, string>({
+ '*': () => ({ tag: 'ZeroOrMore', expr }),
+ '+': () => ({ tag: 'OneOrMore', expr }),
+ '?': () => ({ tag: 'Optional', expr }),
+ _: () => expr,
+ }),
+ (ex) => right([ex, inp])
+ )
+ )
+ )
+ )
+
export const expressionP: Parser<Expr> = (input: string) =>
pipe(
input,
or([
mapTo(
delimited(symbol('('), many1(expressionP), symbol(')')),
- (exprs) => ({ tag: 'Group', exprs })
+ (exprs) => ({ tag: 'Group', exprs } as Expr)
),
nextItem,
anyItem,
@@ -71,27 +88,17 @@ export const expressionP: Parser<Expr> = (input: string) =>
anyBool,
truthy,
falsey,
- ])
+ ]),
+ wrapQuantifiers
)
export const parser = tuple3(optional(start), many1(expressionP), optional(end))
/*
-^ $ => start and end of list
-? => optional
-. => any item
-* => 0 or more instances
-+ => 1 or more instances
{3,6} => 3 to 6 instances
-\s => string
-\n => number
-\b => boolean
-\T => truthy
-\F => falsey
(> 5) => number greater than
(< 5) => number less than
-, => followed by
[name x] => apply x on property `name`
| => or
/x/ => match regular expression (string values in list)
diff --git a/src/parser.ts b/src/parser.ts
index baa3e76..a7d6528 100644
--- a/src/parser.ts
+++ b/src/parser.ts
@@ -1,14 +1,24 @@
-import { flow, pipe } from 'fp-ts/function'
-import { Either, left, right, map, chain, orElse, fold } from 'fp-ts/Either'
+import { constant, flow, pipe } from 'fp-ts/function'
+import {
+ Either,
+ left,
+ right,
+ map,
+ chain,
+ orElse,
+ fold,
+ orElseW,
+} from 'fp-ts/Either'
import { none, some, Option } from 'fp-ts/Option'
-import { mapFst, snd } from 'fp-ts/Tuple'
+import { mapFst, mapSnd, snd } from 'fp-ts/Tuple'
import { eq } from './utils'
export type char = string
-export type ParserResult<T> = [T, string]
+export type ParserState<T> = [T, string]
export type ParserError = [string, string]
-export type Parser<T> = (input: string) => Either<ParserError, ParserResult<T>>
+export type ParserResult<T> = Either<ParserError, ParserState<T>>
+export type Parser<T> = (input: string) => ParserResult<T>
export const constP =
<T>(v: T): Parser<T> =>
@@ -39,13 +49,29 @@ export const many1 = <T>(parser: Parser<T>): Parser<Array<T>> =>
)
)
+const recoverInput =
+ <T>(p: Parser<T>): Parser<T> =>
+ (input: string) =>
+ pipe(
+ input,
+ p,
+ orElseW(
+ flow(
+ mapSnd((_) => input),
+ left
+ )
+ )
+ )
+
export const prefixed = <T>(a: Parser<any>, b: Parser<T>): Parser<T> =>
- flow(a, chain(flow(snd, b)))
+ recoverInput(flow(a, chain(flow(snd, b))))
export const suffixed = <T>(a: Parser<T>, b: Parser<any>): Parser<T> =>
- flow(
- a,
- chain(([out, inp]) => pipe(inp, b, map(mapFst((_) => out))))
+ recoverInput(
+ flow(
+ a,
+ chain(([out, inp]) => pipe(inp, b, map(mapFst((_) => out))))
+ )
)
export const delimited = <T>(
diff --git a/src/utils.ts b/src/utils.ts
index e7392bf..9c4c97b 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -2,3 +2,8 @@ export const eq =
<T>(a: T) =>
(b: T): boolean =>
a === b
+
+export const match =
+ <R, K extends string>(pattern: { [key in K | '_']: () => R }) =>
+ (k: K): R =>
+ (pattern[k] || pattern._)()