open Location open Ident exception Escape of exn exception InvalidInputFilename of string exception InvalidObjectFilename of string let extra_specs = ref [] (* if set to false toplevel exception aren't cought. * Useful for debugging with OCAMLRUNPARAM="b" *) let catch_exceptions = true (* retuns a filename without the suffix suff if any *) let prefix filename suff = if Filename.check_suffix filename suff then try Filename.chop_extension filename with Invalid_argument filename -> failwith "Not a point in the suffix?" else filename let toplevel = ref false let verbose = ref false let silent = ref false let typing_env = State.ref "Cduce.typing_env" Builtin.env let compile_env = State.ref "Cduce.compile_env" Compile.empty_toplevel let get_global_value cenv v = Eval.var (Compile.find v !compile_env) let get_global_type v = Typer.find_value v !typing_env let rec is_abstraction = function | Ast.Abstraction _ -> true | Ast.LocatedExpr (_,e) -> is_abstraction e | _ -> false let print_norm ppf d = Types.Print.print ppf ((*Types.normalize*) d) let print_sample ppf s = Sample.print ppf s let print_protect ppf s = Format.fprintf ppf "%s" s let print_value ppf v = Value.print ppf v let dump_value ppf x t v = Format.fprintf ppf "@[val %a : @[%a = %a@]@]@." Ident.print x print_norm t print_value v let dump_env ppf tenv cenv = Format.fprintf ppf "Types:%a@." Typer.dump_types tenv; Format.fprintf ppf "Namespace prefixes:@\n%a" Typer.dump_ns tenv; Format.fprintf ppf "Namespace prefixes used for pretty-printing:@.%t" Ns.InternalPrinter.dump; Format.fprintf ppf "Schemas: %s@." (String.concat " " (List.map U.get_str (Typer.get_schema_names tenv))); Format.fprintf ppf "Values:@."; Typer.iter_values tenv (fun x t -> dump_value ppf x t (get_global_value cenv x)) let directive_help ppf = Format.fprintf ppf "Toplevel directives: #quit;; quit the interpreter #env;; dump current environment #reinit_ns;; reinitialize namespace processing #help;; shows this help message #dump_value ;; dump an XML-ish representation of the resulting value of a given expression #print_schema ;; #print_type ;; #silent;; turn off outputs from the toplevel #verbose;; turn on outputs from the toplevel " let rec print_exn ppf = function | Location (loc, w, exn) -> Format.fprintf ppf "Error %a:@." Location.print_loc (loc,w); Location.html_hilight (loc,w); print_exn ppf exn | Value.CDuceExn v -> Format.fprintf ppf "Uncaught CDuce exception: @[%a@]@." print_value v | Typer.WrongLabel (t,l) -> Format.fprintf ppf "Wrong record selection; field %a " Label.print (LabelPool.value l); Format.fprintf ppf "not present in an expression of type:@.%a@." print_norm t | Typer.ShouldHave (t,msg) -> Format.fprintf ppf "This expression should have type:@.%a@.%a@." print_norm t print_protect msg | Typer.ShouldHave2 (t1,msg,t2) -> Format.fprintf ppf "This expression should have type:@.%a@.%a %a@." print_norm t1 print_protect msg print_norm t2 | Typer.Error s -> Format.fprintf ppf "%a@." print_protect s | Typer.Constraint (s,t) -> Format.fprintf ppf "This expression should have type:@.%a@." print_norm t; Format.fprintf ppf "but its inferred type is:@.%a@." print_norm s; Format.fprintf ppf "which is not a subtype, as shown by the sample:@.%a@." print_sample (Sample.get (Types.diff s t)) | Typer.NonExhaustive t -> Format.fprintf ppf "This pattern matching is not exhaustive@."; Format.fprintf ppf "Residual type:@.%a@." print_norm t; Format.fprintf ppf "Sample:@.%a@." print_sample (Sample.get t) | Typer.UnboundId (x,tn) -> Format.fprintf ppf "Unbound identifier %a%s@." Ident.print x (if tn then " (it is a type name)" else "") | Typer.UnboundExtId (cu,x) -> Format.fprintf ppf "Unbound external identifier %a:%a@." U.print (Types.CompUnit.value cu) Ident.print x | Ulexer.Error (i,j,s) -> let loc = Location.loc_of_pos (i,j), `Full in Format.fprintf ppf "Error %a:@." Location.print_loc loc; Location.html_hilight loc; Format.fprintf ppf "%s" s | Parser.Error s | Stream.Error s -> Format.fprintf ppf "Parsing error: %a@." print_protect s | Librarian.InconsistentCrc id -> Format.fprintf ppf "Link error:@."; let name = Encodings.Utf8.to_string (Types.CompUnit.value id) in Format.fprintf ppf "Inconsistent checksum (compilation unit: %s)@." name | Librarian.NoImplementation id -> Format.fprintf ppf "Link error:@."; let name = Encodings.Utf8.to_string (Types.CompUnit.value id) in Format.fprintf ppf "No implementation found for compilation unit: %s@." name | Librarian.Loop id -> Format.fprintf ppf "Compilation error:@."; let name = Encodings.Utf8.to_string (Types.CompUnit.value id) in Format.fprintf ppf "Loop between compilation unit (compilation unit: %s)@." name | InvalidInputFilename f -> Format.fprintf ppf "Compilation error:@."; Format.fprintf ppf "Source filename must have extension .cd@."; | InvalidObjectFilename f -> Format.fprintf ppf "Compilation error:@."; Format.fprintf ppf "Object filename must have extension .cdo and no path@."; | Librarian.InvalidObject f -> Format.fprintf ppf "Invalid object file %s@." f | Librarian.CannotOpen f -> Format.fprintf ppf "Cannot open file %s@." f | Location.Generic s -> Format.fprintf ppf "%a@." print_protect s | exn -> (* raise exn *) Format.fprintf ppf "%a@." print_protect (Printexc.to_string exn) let eval_quiet tenv cenv e = let (e,_) = Typer.type_expr tenv e in let e = Compile.compile_expr cenv e in Eval.expr e let debug ppf tenv cenv = function | `Subtype (t1,t2) -> Format.fprintf ppf "[DEBUG:subtype]@."; let t1 = Types.descr (Typer.typ tenv t1) and t2 = Types.descr (Typer.typ tenv t2) in let s = Types.subtype t1 t2 in Format.fprintf ppf "%a %a %a : %b@." print_norm t1 print_protect "<=" print_norm t2 s | `Sample t -> Format.fprintf ppf "[DEBUG:sample]@."; (try let t = Types.descr (Typer.typ tenv t) in Format.fprintf ppf "%a@." print_sample (Sample.get t) with Not_found -> Format.fprintf ppf "Empty type : no sample !@.") | `Filter (t,p) -> let t = Typer.typ tenv t and p = Typer.pat tenv p in Format.fprintf ppf "[DEBUG:filter t=%a p=%a]@." Types.Print.print (Types.descr t) Patterns.Print.print (Patterns.descr p); let f = Patterns.filter (Types.descr t) p in IdMap.iteri (fun x t -> Format.fprintf ppf " %a:%a@." Ident.print x print_norm (Types.descr t)) f | `Accept p -> Format.fprintf ppf "[DEBUG:accept]@."; let p = Typer.pat tenv p in let t = Patterns.accept p in Format.fprintf ppf " %a@." Types.Print.print (Types.descr t) | `Compile (t,pl) -> Format.fprintf ppf "[DEBUG:compile]@."; let t = Typer.typ tenv t and pl = List.map (Typer.pat tenv) pl in Patterns.Compile.debug_compile ppf t pl; Format.fprintf ppf "@."; (* Patterns.demo_compile ppf (Types.descr t) (List.map Patterns.descr pl) *) | `Explain (t0,t,e) -> Format.fprintf ppf "[DEBUG:explain]@."; let t = Types.descr (Typer.typ tenv t) in let t0 = Types.descr (Typer.typ tenv t0) in (match Explain.explain t0 t (eval_quiet tenv cenv e) with | Some p -> Format.fprintf ppf "%a@." Explain.print p | None -> Format.fprintf ppf "Value has given type@.") | `Single t -> Format.fprintf ppf "[DEBUG:single]@."; let t = Typer.typ tenv t in (try let c = Sample.single (Types.descr t) in Format.fprintf ppf "Constant:%a@." Types.Print.print_const c with | Exit -> Format.fprintf ppf "Non constant@." | Not_found -> Format.fprintf ppf "Empty@.") | `Approx (p,t) -> Format.fprintf ppf "[DEBUG:approx]@."; let t = Typer.typ tenv t in let p = Typer.pat tenv p in Patterns.demo ppf (Patterns.descr p) (Types.descr t); (* let (x,c) = Patterns.approx (Patterns.descr p) (Types.descr t) in List.iter (fun x -> Format.fprintf ppf "%a=* " U.print (Id.value x)) x; List.iter (fun (x,c) -> Format.fprintf ppf "%a=%a " U.print (Id.value x) Types.Print.print_const c ) c; *) Format.fprintf ppf "@." let flush_ppf ppf = Format.fprintf ppf "@." let directive ppf tenv cenv = function | `Debug d -> debug ppf tenv cenv d | `Quit -> (if !toplevel then raise End_of_file) | `Env -> dump_env ppf tenv cenv | `Print_type t -> let t = Typer.typ tenv t in Format.fprintf ppf "%a@." Types.Print.print_noname (Types.descr t) | `Reinit_ns -> Typer.set_ns_table_for_printer tenv | `Help -> directive_help ppf | `Dump pexpr -> Value.dump_xml ppf (eval_quiet tenv cenv pexpr); flush_ppf ppf | `Silent -> silent := true | `Verbose -> silent := false | `Builtins -> let b = Librarian.get_builtins () in Format.fprintf ppf "Embedded OCaml value: "; List.iter (fun s -> Format.fprintf ppf "%s " s) b; Format.fprintf ppf "@." let print_id_opt ppf = function | None -> Format.fprintf ppf "-" | Some id -> Format.fprintf ppf "val %a" Ident.print id let print_value_opt ppf = function | None -> () | Some v -> Format.fprintf ppf " = %a" print_value v let show ppf id t v = if !silent then () else Format.fprintf ppf "@[%a : @[%a%a@]@]@." print_id_opt id print_norm t print_value_opt v let phrases ppf phs = let (tenv,cenv,_) = Compile.comp_unit ~run:true ~show:(show ppf) ~loading:Librarian.import_and_run ~directive:(directive ppf) !typing_env !compile_env phs in typing_env := tenv; compile_env := cenv let catch_exn ppf_err exn = if not catch_exceptions then raise exn; match exn with | (End_of_file | Failure _ | Not_found | Invalid_argument _ | Sys.Break) as e -> raise e | exn -> print_exn ppf_err exn; Format.fprintf ppf_err "@." let parse rule input = try Parser.localize_exn (fun () -> rule input) with e -> Parser.sync (); raise e let run rule ppf ppf_err input = try phrases ppf (parse rule input); true with Escape exn -> raise exn | exn -> catch_exn ppf_err exn; false let topinput = run Parser.top_phrases let script = run Parser.prog let compile src out_dir = try if not (Filename.check_suffix src ".cd") then raise (InvalidInputFilename src); let cu = Filename.chop_suffix (Filename.basename src) ".cd" in let out_dir = match out_dir with | None -> Filename.dirname src | Some x -> x in let out = Filename.concat out_dir (cu ^ ".cdo") in let id = Types.CompUnit.mk (U.mk_latin1 cu) in Librarian.compile !verbose cu id src; Librarian.save cu id out; exit 0 with exn -> catch_exn Format.err_formatter exn; exit 1 let compile_run src = try let cu = if src = "" then "" else if not (Filename.check_suffix src ".cd") then raise (InvalidInputFilename src) else Filename.chop_suffix (Filename.basename src) ".cd" in let id = Types.CompUnit.mk (U.mk_latin1 cu) in Librarian.compile !verbose cu id src; Librarian.run id with exn -> catch_exn Format.err_formatter exn; exit 1 let run obj = try if not (Filename.check_suffix obj ".cdo") || (Filename.basename obj <> obj) then raise (InvalidObjectFilename obj); let cu = Filename.chop_suffix (Filename.basename obj) ".cdo" in let id = Types.CompUnit.mk (U.mk_latin1 cu) in Librarian.import_and_run id with exn -> catch_exn Format.err_formatter exn; exit 1 let dump_env ppf = dump_env ppf !typing_env !compile_env let eval s = let st = Stream.of_string s in let phs = parse Parser.prog st in let vals = ref [] in let show id t v = match id,v with | Some id, Some v -> let id = Id.value id in vals := (Some id,v) :: !vals | None, Some v -> vals := (None,v) :: !vals | _ -> assert false in let r () = ignore (Compile.comp_unit ~run:true ~show Builtin.env Compile.empty_toplevel phs) in Eval.new_stack r (); List.rev !vals let eval s = try eval s with exn -> let b = Buffer.create 1024 in let ppf = Format.formatter_of_buffer b in print_exn ppf exn; Format.fprintf ppf "@."; Value.failwith' (Buffer.contents b) let () = Operators.register_fun "eval_expr" Builtin_defs.string_latin1 Types.any (fun v -> match eval (Value.cduce2ocaml_string v) with | [ (None,v) ] -> v | _ -> Value.failwith' "eval: the string must evaluate to a single value" )