open Printf exception Reset_line (* used to indicate ^C is pressed *) let version = "%%VERSION%%" let error_to_string e = try raise e with | Lex.Token_not_found -> sprintf "invalid token" | Lex.Unclosed_quote -> sprintf "string not closed" | Parser.Expected t -> sprintf "expected %s" t | Parser.Unexpected_token t -> sprintf "unexpected token \"%s\"" t | Ast.Invalid_type t -> sprintf "invalid type %s" (Ast.Type.to_string t) | Eval.No_such_variable v -> sprintf "no such variable %s" v | Failure f -> sprintf "error on %s" f | Division_by_zero -> "cannot divide by zero" | _ -> raise e let print_error e = printf "error: %s\n" @@ error_to_string e let vars = Hashtbl.create 100 (* read-eval-print *) let rep vars : unit = printf "> "; let line = read_line () in if line = "quit" then raise Exit; let v = line |> Lex.tokenize |> Parser.parse |> Eval.eval vars in match v with | Nop -> () | _ -> Hashtbl.replace vars "ans" v; printf "%s\n" @@ Ast.Value.to_string v let init_repl () = Hashtbl.replace vars "ans" (Ast.Value.Int 0); (* treat Ctrl-C as to reset line *) let sigintf _ = raise Reset_line in Sys.(set_signal sigint (Signal_handle sigintf)) (* simple REPL with error handling *) let rec repl vars : unit = try rep vars; repl vars with | Exit | End_of_file -> () | Reset_line -> printf "\n"; repl vars | e -> print_error e; repl vars let () = init_repl (); printf "Configurable Evaluator %s\n" version; (* banner *) repl vars