open Tjutil open Tjllist type ts = char llist type state = int * int * (char list * char * char list) type error = state * string type 'a t = state -> ts -> ('a * state * ts, error) either exception ParseError of string let lt_pos (l1,p1,_) (l2,p2,_) = if l1 < l2 then true else if l1 = l2 then p1 < p2 else false let eplus (st1,msg1) (st2,msg2) = if lt_pos st1 st2 then (st2,msg2) else (st1,msg1) let showerr ((line,pos,(pre,c,post)),msg) = !%"line %d, %d: %s: pre=%S char=%C post=%S" line pos msg (string_of_chars pre) c (string_of_chars post) let return : 'a -> 'a t = fun x -> fun state code -> Inl (x, state, code) let error msg = fun state _code -> Inr (state, msg) let (>>=) : 'a t -> ('a -> 'b t) -> 'b t = fun p f -> fun state code -> match p state code with | Inl (x, state', ts) -> f x state' ts | Inr err -> Inr err let (>>) : 'a t -> 'b t -> 'b t = fun p1 p2 -> p1 >>= fun _ -> p2 let (<.<) : 'a t -> 'b t -> 'a t = fun p1 p2 -> p1 >>= fun x -> p2 >> return x let ( ^? ) : 'a t -> string -> 'a t = fun p msg -> fun state code -> match p state code with | Inl l -> Inl l | Inr (st,msg0) -> Inr (st,msg ^": "^msg0) (* (<|>) : 'a m -> 'a m -> 'a m *) let (<|>) : 'a t -> 'a t -> 'a t = fun p1 p2 -> fun state code -> match p1 state code with | Inl (x1, state', ts) -> Inl (x1, state', ts) | Inr err1 -> begin match p2 state code with | Inl (x2, state', ts) -> Inl (x2,state',ts) | Inr err2 -> Inr (eplus err1 err2) end (* let (<|?>) p1 p2 = fun state code -> match p1 state code with | Inl (x1, state', ts) -> Inl (x1, state', ts) | Inr err1 -> print_endline err1; begin match p2 state code with | Inl (x2, state', ts) -> Inl (x2,state',ts) | Inr err2 -> Inr (eplus err1 err2) end *) let rec many : 'a t -> ('a list) t = fun p -> (p >>= fun x -> many p >>= fun xs -> return (x::xs)) <|> (return []) let many1 p = p >>= fun x -> many p >>= fun xs -> return (x::xs) let sep separator p = (p >>= fun x -> many (separator >> p) >>= fun xs -> return (x::xs)) <|> (return []) let opt : 'a t -> ('a option) t = fun p -> (p >>= fun x -> return (Some x)) <|> (return None) let _char1_with_debug state = function | Nil -> Inr (state,"(Nil)") | Cons (x,xs) -> let next (pre,x0, _) = let pre' = if List.length pre < 100 then pre @ [x0] else List.tl pre @ [x0] in (pre' , x, take 100 !$xs) in match x, state with | '\n', (line,_pos,cs) -> Inl (x,(line+1,-1, next cs), !$xs) | _, (line,pos,cs) -> Inl (x,(line, pos+1, next cs),!$xs) let char1_without_debug state = function | Nil -> Inr (state,"(Nil)") | Cons (x,xs) -> Inl (x, state, !$xs) let char1 = char1_without_debug let char_when f = char1 >>= fun c -> if f c then return c else error (!%"(char:'%c')" c) let char c = char_when ((=) c) let keyword w = let rec iter i = if i < String.length w then char w.[i] >> iter (i+1) else return w in iter 0 let make_ident f = many1 (char_when f) >>= fun cs -> return (string_of_chars cs) let int = opt (char '-') >>= fun minus -> make_ident (function '0'..'9' -> true | _ -> false) >>= fun s -> return begin match minus with | None -> int_of_string s | Some _ -> - int_of_string s end let run p state ts = match p state ts with | Inl (x,_state',_xs) -> x | Inr err -> raise (ParseError (showerr err)) let init_state = (1, 0, ([],'_',[])) let run_ch p ch = run p init_state (of_stream (Cpdfstream.of_channel ch)) let run_stdin p = run_ch p stdin let run_file p filename = open_in_with filename (fun ch -> run_ch p ch) let run_string p s = run p init_state (of_string s) let run_function p f = run p init_state (of_function f)