open Builtin_defs (* 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 ] let () = List.iter (fun (n,t) -> Typer.register_global_types [ Ident.U.mk n, Location.mknoloc (Ast.Internal t)]) types (* Operators *) let binary_op_gen name typ run = Typer.register_binary_op name (fun _ -> { Typed.bin_op_typer = typ; Typed.bin_op_eval = run }) let unary_op_gen name typ run = Typer.register_unary_op name (fun _ -> { Typed.un_op_typer = typ; Typed.un_op_eval = run }) let binary_op name t1 t2 f run = binary_op_gen name (fun loc arg1 arg2 constr precise -> f (arg1 t1 true) (arg2 t2 true)) run let binary_op_cst name t1 t2 t run = binary_op_gen name (fun loc arg1 arg2 constr precise -> ignore (arg1 t1 false); ignore (arg2 t2 false); t) run let binary_op_warning2 name t1 t2 w2 t run = binary_op_gen name (fun loc arg1 arg2 constr precise -> ignore (arg1 t1 false); let r = arg2 t2 true in if not (Types.subtype r w2) then Typer.warning loc "This operator may fail"; t) run let unary_op_warning name targ w t run = Typer.register_unary_op name (fun _ -> { Typed.un_op_typer = (fun loc arg constr precise -> let res = arg targ true in if not (Types.subtype res w) then Typer.warning loc "This operator may fail"; t); Typed.un_op_eval = run }) let unary_op_cst name targ t run = Typer.register_unary_op name (fun _ -> { Typed.un_op_typer = (fun loc arg constr precise -> ignore (arg targ false); t); Typed.un_op_eval = run }) open Ident let exn_load_file_utf8 = Value.CDuceExn ( Value.Pair ( Value.Atom (Atoms.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.mk_ascii "Invalid_argument"), Value.string_latin1 "int_of")) let exn_not_found = Value.CDuceExn (Value.Atom (Atoms.mk_ascii "Not_found")) let eval_load_file ~utf8 e = Location.protect_op "load_file"; let ic = open_in (Value.get_string_latin1 e) in let len = in_channel_length ic in let s = String.create len in really_input ic s 0 len; close_in ic; if utf8 then if U.check s then Value.string_utf8 (U.mk s) else 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 *) unary_op_cst "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) );; unary_op_cst "load_xml" string any (fun v -> Load_xml.load_xml (Value.get_string_latin1 v));; unary_op_cst "load_html" string any (fun v -> Load_xml.load_html (Value.get_string_latin1 v));; unary_op_cst "load_file_utf8" string string (eval_load_file ~utf8:true);; unary_op_cst "load_file" string string_latin1 (eval_load_file ~utf8:false);; unary_op_cst "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);; Typer.register_unary_op "print_xml" (fun tenv -> let ns_table = Typer.get_ns_table tenv in { Typed.un_op_typer = (fun loc arg constr precise -> ignore (arg Types.any false); string_latin1); Typed.un_op_eval = Print_xml.print_xml ~utf8:false ns_table });; Typer.register_unary_op "print_xml_utf8" (fun tenv -> let ns_table = Typer.get_ns_table tenv in { Typed.un_op_typer = (fun loc arg constr precise -> ignore (arg Types.any false); string); Typed.un_op_eval = Print_xml.print_xml ~utf8:true ns_table });; unary_op_warning "print" string string_latin1 nil (fun v -> Location.protect_op "print"; print_string (Value.get_string_latin1 v); 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.mk (U.get_str s)) (* UTF-8 is ASCII compatible ! *) with Failure _ -> raise exn_int_of);; unary_op_cst "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.mk Ns.empty s));; binary_op_warning2 "dump_to_file" string 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 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 loc 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 Typer.error loc "The first argument mixes integers and records") (fun v1 v2 -> match (v1,v2) with | (Value.Integer x, Value.Integer y) -> Value.Integer (Intervals.vadd 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.vsub x y) | _ -> assert false);; binary_op_cst "*" int int int (fun v1 v2 -> match (v1,v2) with | (Value.Integer x, Value.Integer y) -> Value.Integer (Intervals.vmult x y) | _ -> assert false);; binary_op_cst "/" int int int (fun v1 v2 -> match (v1,v2) with | (Value.Integer x, Value.Integer y) -> Value.Integer (Intervals.vdiv x y) | _ -> assert false);; binary_op_cst "mod" int int int (fun v1 v2 -> match (v1,v2) with | (Value.Integer x, Value.Integer y) -> Value.Integer (Intervals.vmod x y) | _ -> assert false);; binary_op_gen "@" (fun loc 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;; unary_op_cst "raise" any Types.empty (fun v -> raise (Value.CDuceExn v))