open Builtin_defs let eval = ref (fun ppf err s -> assert false) (* Types *) let types = [ "Empty", Types.empty; "Any", any; "Int", int; "Char", Types.char Chars.any; "Byte", char_latin1; "Atom", atom; "Pair", Types.Product.any; "Arrow", Types.Arrow.any; "Record", Types.Record.any; "String", string; "Latin1", string_latin1; "Bool", bool; "Float", float; ] let env = List.fold_left (fun accu (n,t) -> let n = Ident.U.mk n in Types.Print.register_global n t; Typer.enter_type (Ident.ident n) t accu ) Typer.empty_env types (* Operators *) open Operators let binary_op_gen = register_binary let unary_op_gen = register_unary let binary_op name t1 t2 f run = binary_op_gen name (fun arg1 arg2 constr precise -> f (arg1 t1 true) (arg2 t2 true)) run let binary_op_cst = register_op2 let binary_op_warning2 name t1 t2 w2 t run = binary_op_gen name (fun arg1 arg2 constr precise -> ignore (arg1 t1 false); let r = arg2 t2 true in if not (Types.subtype r w2) then raise (Typer.Warning ("This operator may fail", t)); t) run let unary_op_warning name targ w t run = unary_op_gen name (fun arg constr precise -> let res = arg targ true in if not (Types.subtype res w) then raise (Typer.Warning ("This operator may fail",t)); t) run open Ident let exn_load_file_utf8 = Value.CDuceExn ( Value.Pair ( Value.Atom (Atoms.V.mk_ascii "load_file_utf8"), Value.string_latin1 "File is not a valid UTF-8 stream")) let exn_int_of = Value.CDuceExn ( Value.Pair ( Value.Atom (Atoms.V.mk_ascii "Invalid_argument"), Value.string_latin1 "int_of")) let exn_not_found = Value.CDuceExn (Value.Atom (Atoms.V.mk_ascii "Not_found")) let eval_load_file ~utf8 e = Location.protect_op "load_file"; let fn = Value.get_string_latin1 e in let s = match Url.process fn with | Url.Filename fn -> let ic = open_in fn in let len = in_channel_length ic in let s = String.create len in really_input ic s 0 len; close_in ic; s | Url.Url txt -> txt in if utf8 then match U.mk_check s with | Some s -> Value.string_utf8 s | None -> raise exn_load_file_utf8 else Value.string_latin1 s let () = ();; (* Comparison operators *) binary_op "=" any any (fun t1 t2 -> if Types.is_empty (Types.cap t1 t2) then false_type else bool) (fun v1 v2 -> Value.vbool (Value.compare v1 v2 == 0));; binary_op_cst "<=" any any bool (fun v1 v2 -> Value.vbool (Value.compare v1 v2 <= 0));; binary_op_cst "<" any any bool (fun v1 v2 -> Value.vbool (Value.compare v1 v2 < 0));; binary_op_cst ">=" any any bool (fun v1 v2 -> Value.vbool (Value.compare v1 v2 >= 0));; binary_op_cst ">" any any bool (fun v1 v2 -> Value.vbool (Value.compare v1 v2 > 0));; (* I/O *) register_fun "string_of" any string_latin1 (fun v -> let b = Buffer.create 16 in let ppf = Format.formatter_of_buffer b in Value.print ppf v; Format.pp_print_flush ppf (); Value.string_latin1 (Buffer.contents b) );; register_fun "load_xml" string_latin1 any (fun v -> Load_xml.load_xml (Value.get_string_latin1 v));; register_fun "load_html" string_latin1 Sequence.any (fun v -> Load_xml.load_html (Value.get_string_latin1 v));; register_fun "load_file_utf8" string_latin1 string (eval_load_file ~utf8:true);; register_fun "load_file" string_latin1 string_latin1 (eval_load_file ~utf8:false);; register_fun "getenv" string_latin1 string_latin1 (fun e -> Location.protect_op "getenv"; let var = Value.get_string_latin1 e in try Value.string_latin1 (Sys.getenv var) with Not_found -> raise exn_not_found);; let argv = ref Value.Absent;; register_fun "argv" nil (Sequence.star string_latin1) (fun e -> Location.protect_op "argv"; !argv);; register_fun "print_xml" Types.any string_latin1 (fun v -> Print_xml.print_xml ~utf8:false !Eval.ns_table v);; register_fun "print_xml_utf8" Types.any string (fun v -> Print_xml.print_xml ~utf8:true !Eval.ns_table v);; register_fun "print" string_latin1 nil (fun v -> Location.protect_op "print"; print_string (Value.get_string_latin1 v); flush stdout; Value.nil );; register_fun "print_utf8" string nil (fun v -> Location.protect_op "print"; let s = Value.cduce2ocaml_string_utf8 v in print_string (U.get_str s); flush stdout; Value.nil );; unary_op_warning "int_of" string intstr int (fun v -> let (s,_) = Value.get_string_utf8 v in try Value.Integer (Intervals.V.mk (U.get_str s)) (* UTF-8 is ASCII compatible ! *) with Failure _ -> raise exn_int_of);; register_fun "atom_of" string atom (fun v -> let (s,_) = Value.get_string_utf8 v in (* TODO: check that s is a correct Name wrt XML *) Value.Atom (Atoms.V.mk Ns.empty s));; binary_op_warning2 "dump_to_file" string_latin1 string string_latin1 nil (fun f v -> Location.protect_op "dump_to_file"; let oc = open_out (Value.get_string_latin1 f) in output_string oc (Value.get_string_latin1 v); close_out oc; Value.nil);; binary_op_cst "dump_to_file_utf8" string_latin1 string nil (fun f v -> Location.protect_op "dump_to_file_utf8"; let oc = open_out (Value.get_string_latin1 f) in let (v,_) = Value.get_string_utf8 v in output_string oc (U.get_str v); close_out oc; Value.nil);; (* Integer operators *) binary_op_gen "+" (fun arg1 arg2 constr precise -> let t1 = arg1 (Types.cup int Types.Record.any) true in if Types.subtype t1 int then ( let t2 = arg2 int true in Types.interval (Intervals.add (Types.Int.get t1) (Types.Int.get t2)) ) else if Types.subtype t1 Types.Record.any then ( let t2 = arg2 Types.Record.any true in Types.Record.merge t1 t2 ) else raise (Typer.Error "The first argument mixes integers and records")) (fun v1 v2 -> match (v1,v2) with | (Value.Integer x, Value.Integer y) -> Value.Integer (Intervals.V.add x y) | (Value.Record r1, Value.Record r2) -> Value.Record (LabelMap.merge (fun x y -> y) r1 r2) | _ -> assert false);; binary_op "-" int int (fun t1 t2 -> Types.interval (Intervals.sub (Types.Int.get t1) (Types.Int.get t2))) (fun v1 v2 -> match (v1,v2) with | (Value.Integer x, Value.Integer y) -> Value.Integer (Intervals.V.sub x y) | _ -> assert false);; binary_op "*" int int (fun t1 t2 -> Types.interval (Intervals.mul (Types.Int.get t1) (Types.Int.get t2))) (fun v1 v2 -> match (v1,v2) with | (Value.Integer x, Value.Integer y) -> Value.Integer (Intervals.V.mult x y) | _ -> assert false);; binary_op_warning2 "/" int int non_zero_int int (fun v1 v2 -> match (v1,v2) with | (Value.Integer x, Value.Integer y) -> Value.Integer (Intervals.V.div x y) | _ -> assert false);; binary_op_warning2 "mod" int int non_zero_int int (fun v1 v2 -> match (v1,v2) with | (Value.Integer x, Value.Integer y) -> Value.Integer (Intervals.V.modulo x y) | _ -> assert false);; binary_op_gen "@" (fun arg1 arg2 constr precise -> let constr' = Sequence.star (Sequence.approx (Types.cap Sequence.any constr)) in let exact = Types.subtype constr' constr in if exact then let t1 = arg1 constr' precise and t2 = arg2 constr' precise in if precise then Sequence.concat t1 t2 else constr else (* Note: the knownledge of t1 may makes it useless to check t2 with 'precise' ... *) let t1 = arg1 constr' true and t2 = arg2 constr' true in Sequence.concat t1 t2) Value.concat;; unary_op_gen "flatten" Typer.flatten Value.flatten;; register_fun "raise" any Types.empty (fun v -> raise (Value.CDuceExn v));;