open import "prelude.ml"
|
|
open import "./monads.ml"
|
|
|
|
private type consumed = C | E
|
|
|
|
private type error = Unexpected of string * list string
|
|
|
|
instance show error begin
|
|
let show = function
|
|
| Unexpected (s, []) -> s
|
|
| Unexpected (s, ms) ->
|
|
"Unexpected " ^ s ^ "\n"
|
|
^ "Expecting one of " ^ foldl (^) "" ms
|
|
end
|
|
|
|
private type pos <- { line : int, col : int }
|
|
|
|
private type result 'a =
|
|
| Ok of consumed * 'a * string * pos
|
|
| Err of consumed * error * string * pos
|
|
|
|
let join_consumed x y =
|
|
match x with
|
|
| C -> C
|
|
| E -> y
|
|
|
|
type parser_t 'm 'a = private P of pos -> string -> 'm (result 'a)
|
|
type parser <- parser_t identity
|
|
|
|
instance functor 'm => functor (parser_t 'm) begin
|
|
let f <$> P x = P @@ fun pos i ->
|
|
flip map (x pos i) @@ fun p ->
|
|
match p with
|
|
| Ok (c, x, i) -> Ok (c, f x, i)
|
|
| Err e -> Err e
|
|
end
|
|
|
|
instance monad 'm => applicative (parser_t 'm) begin
|
|
let pure x = P (fun p s -> pure (Ok (E, x, s, p)))
|
|
let P f <*> P x = P @@ fun p s ->
|
|
let! f = f p s
|
|
match f with
|
|
| Ok (c, f, s, p) ->
|
|
let! x = x p s
|
|
match x with
|
|
| Ok (c', x, s, p) -> pure @@ Ok (join_consumed c c', f x, s, p)
|
|
| Err (c', p) -> pure @@ Err (join_consumed c c', p)
|
|
| Err e -> pure @@ Err e
|
|
end
|
|
|
|
let x *> y = (fun _ x -> x) <$> x <*> y
|
|
let x <* y = (| const x y |)
|
|
let x <$ a = map (const x) a
|
|
|
|
instance monad 'm => monad (parser_t 'm) begin
|
|
let P x >>= f = P @@ fun p s ->
|
|
let! x = x p s
|
|
match x with
|
|
| Ok (c, x, s, p) ->
|
|
let P kont = f x
|
|
let! x = kont p s
|
|
match x with
|
|
| Ok (c', x, s) -> pure @@ Ok (join_consumed c c', x, s)
|
|
| Err (c', p) -> pure @@ Err (join_consumed c c', p)
|
|
| Err e -> pure @@ Err e
|
|
end
|
|
|
|
let private fail e = P (fun p s -> pure (Err (E, e, s, p)))
|
|
|
|
let empty : forall 'm 'a. monad 'm => parser_t 'm 'a =
|
|
fail (Unexpected ("empty parse", []))
|
|
|
|
let unexpected e =
|
|
fail (Unexpected (e, []))
|
|
|
|
let alt_err (Unexpected (u, xs)) (Unexpected (_, ys)) =
|
|
Unexpected (u, xs ++ ys)
|
|
|
|
let P x <|> P y = P @@ fun p s ->
|
|
let! x = x p s
|
|
match x with
|
|
| Ok x -> pure (Ok x)
|
|
| Err (c, m, s, p) ->
|
|
let! y = y p s
|
|
match y with
|
|
| Ok (c', x, s) -> pure (Ok (join_consumed c c', x, s))
|
|
| Err (c', m', s) -> pure (Err (join_consumed c c', alt_err m m', s))
|
|
|
|
let P x <+> y = P @@ fun p s ->
|
|
let! x = x p s
|
|
match x with
|
|
| Ok x -> pure (Ok x)
|
|
| Err (c, m, s, p) ->
|
|
let P y = force y
|
|
let! y = y p s
|
|
match y with
|
|
| Ok (c', x, s) -> pure (Ok (join_consumed c c', x, s))
|
|
| Err (c', m', s) -> pure (Err (join_consumed c c', alt_err m m', s))
|
|
|
|
private module S = import "lua/string.ml"
|
|
|
|
let char : forall 'm. applicative 'm => parser_t 'm string =
|
|
P @@ fun p s ->
|
|
let x = S.substring s 1 1
|
|
if x <> "" then
|
|
let r = S.substring s 2 (S.length s)
|
|
if x == "\n" then
|
|
pure @@ Ok (C, x, r, { line = p.line + 1, col = 0 })
|
|
else
|
|
pure @@ Ok (C, x, r, { line = p.line, col = p.col + 1 })
|
|
else
|
|
pure @@ Err (E, Unexpected ("end-of-file", ["character"]), s, p)
|
|
|
|
let eof : forall 'm. applicative 'm => parser_t 'm () =
|
|
P @@ fun p s ->
|
|
if s == "" then
|
|
pure @@ Ok (E, (), s, p)
|
|
else
|
|
pure @@
|
|
Err (E, Unexpected (S.substring s 1 1, ["end-of-file"]), s, p)
|
|
|
|
let satisfy pred =
|
|
P @@ fun p s ->
|
|
let x = S.substring s 1 1
|
|
if x <> "" && pred x then
|
|
let r = S.substring s 2 (S.length s)
|
|
if x == "\n" then
|
|
pure @@ Ok (C, x, r, { line = p.line + 1, col = 0 })
|
|
else
|
|
pure @@ Ok (C, x, r, { line = p.line, col = p.col + 1 })
|
|
else
|
|
let m =
|
|
if x == "" then
|
|
"end of file"
|
|
else
|
|
x
|
|
pure @@ Err (E, Unexpected (m, ["character"]), s, p)
|
|
|
|
let P k <?> m = P @@ fun p s ->
|
|
let! x = k p s
|
|
match x with
|
|
| Ok e -> pure (Ok e)
|
|
| Err (c, Unexpected (u, _), s) -> pure (Err (c, Unexpected (u, [m]), s))
|
|
|
|
let many_fold k z (P x) =
|
|
let rec go consumed kont pos s =
|
|
let! x = x pos s
|
|
match x with
|
|
| Err ((c, _, s, pos) as p) ->
|
|
match c with
|
|
| C -> pure (Err p)
|
|
| E -> kont consumed z pos s
|
|
| Ok (c, x, s, pos) ->
|
|
match c with
|
|
| E -> error @@ "many: got parser that accepts the empty string"
|
|
| C -> go C (fun c -> kont c # k x) pos s
|
|
P (go E (fun c z pos s -> pure (Ok (c, z, s, pos))))
|
|
|
|
let many p = many_fold (::) [] p
|
|
let some p =
|
|
let! x = p
|
|
(x ::) <$> many p
|
|
|
|
let sep_by_1 sep p =
|
|
let! x = p
|
|
let! xs = many (sep *> p)
|
|
pure (x :: xs)
|
|
|
|
let sep_by sep p = sep_by_1 sep p <|> pure []
|
|
external private val is_infix_of : string -> string -> bool =
|
|
"function(s, s2) return s2:find(s) ~= nil end"
|
|
|
|
external private val is_prefix_of : string -> string -> bool =
|
|
"function(s, s2) return (s2:find(s)) == 1 end"
|
|
|
|
let one_of chs =
|
|
let len = S.length chs
|
|
let rec loop ch i =
|
|
if i > len then
|
|
fail @@ Unexpected (ch, ["one of \"" ^ chs ^ "\""])
|
|
else if ch == S.substring chs i i then
|
|
pure ch
|
|
else
|
|
loop ch (i + 1)
|
|
let! ch = char
|
|
loop ch 1
|
|
|
|
let symbol str =
|
|
let len = S.length str
|
|
let rec loop acc i =
|
|
if i > len then
|
|
pure acc
|
|
else
|
|
let! c = char <?> S.substring str i i
|
|
if c == S.substring str i i then
|
|
loop (acc ^ c) (i + 1)
|
|
else
|
|
fail @@ Unexpected (acc ^ c, [S.substring str 1 i])
|
|
loop "" 1
|
|
|
|
let between o c p =
|
|
let! _ = o
|
|
let! x = p
|
|
let! _ = c
|
|
pure x
|
|
|
|
let try (P x) = P @@ fun pos s ->
|
|
let! x = x pos s
|
|
match x with
|
|
| Ok c -> pure (Ok c)
|
|
| Err (_, p, _) -> pure (Err (E, p, s, pos))
|
|
|
|
let optionally p = map Some (try p) <|> pure None
|
|
|
|
|
|
let rec sep_end_by_1 sep p =
|
|
let! x = p
|
|
( let! _ = sep
|
|
let! xs = sep_end_by sep p
|
|
pure (x :: xs)
|
|
) <|> pure [x]
|
|
and sep_end_by sep p =
|
|
sep_end_by_1 sep p <|> pure []
|
|
|
|
let chainr1 p op =
|
|
let rec scan =
|
|
let! x = p
|
|
rest x
|
|
and rest x =
|
|
( let! f = op
|
|
let! y = scan
|
|
pure (f x y)
|
|
) <|> pure x
|
|
let _ = rest (* shut up, amc *)
|
|
scan
|
|
|
|
let parse (P x) s =
|
|
let! x = x { line = 0, col = 0 } s
|
|
match x with
|
|
| Ok (_, x, r) -> pure @@ Right (x, r)
|
|
| Err (_, m, _, p) -> pure @@ Left (m, p)
|
|
|
|
let run_parser p s =
|
|
let Id x = parse p s
|
|
x
|
|
|
|
let run_parser' (P x) s =
|
|
let Id x = x { line = 0, col = 0 } s
|
|
x
|
|
|
|
let lift m = P @@ fun pos s ->
|
|
let! x = m
|
|
pure @@ Ok (E, x, s, pos)
|
|
|
|
let morph (k : forall 'a. 'm 'a -> 'n 'a) (P x) = P @@ fun s -> k (x s)
|
|
let void x = map (const ()) x
|