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