| 1 |
open Value |
open Value |
| 2 |
open Run_dispatch |
open Run_dispatch |
| 3 |
|
open Ident |
| 4 |
|
|
| 5 |
module Env = Map.Make (struct type t = string let compare = compare end) |
exception MultipleDeclaration of id |
| 6 |
|
module Env = Map.Make (Ident.Id) |
| 7 |
type env = t Env.t |
type env = t Env.t |
| 8 |
|
|
| 9 |
let global_env = ref Env.empty |
let global_env = State.ref "Eval.global_env" Env.empty |
|
let enter_global x v = global_env := Env.add x v !global_env |
|
| 10 |
|
|
| 11 |
|
let enter_global x v = |
| 12 |
|
if Env.mem x !global_env then |
| 13 |
|
raise (MultipleDeclaration x); |
| 14 |
|
global_env := Env.add x v !global_env |
| 15 |
|
|
|
let exn_int_of = CDuceExn (Pair (Atom (Types.mk_atom "Invalid_argument"), |
|
|
string "int_of")) |
|
| 16 |
|
|
| 17 |
|
let exn_int_of = CDuceExn (Pair ( |
| 18 |
|
Atom (Atoms.mk_ascii "Invalid_argument"), |
| 19 |
|
string_latin1 "int_of")) |
| 20 |
|
|
| 21 |
|
|
| 22 |
|
let exn_load_file_utf8 = CDuceExn (Pair ( |
| 23 |
|
Atom (Atoms.mk_ascii "load_file_utf8"), |
| 24 |
|
string_latin1 "File is not a valid UTF-8 stream")) |
| 25 |
|
|
|
(* Evaluation of expressions *) |
|
| 26 |
|
|
| 27 |
|
(* Evaluation of expressions *) |
| 28 |
|
|
| 29 |
let rec eval env e0 = |
let rec eval env e0 = |
| 30 |
match e0.Typed.exp_descr with |
match e0.Typed.exp_descr with |
| 35 |
| Typed.Apply (f,arg) -> eval_apply (eval env f) (eval env arg) |
| Typed.Apply (f,arg) -> eval_apply (eval env f) (eval env arg) |
| 36 |
| Typed.Abstraction a -> |
| Typed.Abstraction a -> |
| 37 |
let env = |
let env = |
| 38 |
List.fold_left |
IdSet.fold |
| 39 |
(fun accu x -> |
(fun accu x -> |
| 40 |
try Env.add x (Env.find x env) accu |
try Env.add x (Env.find x env) accu |
| 41 |
with Not_found -> accu (* global *)) |
with Not_found -> accu (* global *)) |
| 50 |
self |
self |
| 51 |
(* Optimizations: |
(* Optimizations: |
| 52 |
- for the non-recursive case, use eval_branches |
- for the non-recursive case, use eval_branches |
| 53 |
- for the recursive case, could cheat bt pathing self afterwards: |
- for the recursive case, could cheat by patching self afterwards: |
| 54 |
(Obj.magic self).(1) <- .... |
(Obj.magic self).(1) <- .... |
| 55 |
*) |
*) |
| 56 |
| Typed.RecordLitt r -> Record (List.map (fun (l,e) -> (l, eval env e)) r) |
| Typed.RecordLitt r -> Record (LabelMap.map (eval env) r) |
| 57 |
| Typed.Pair (e1,e2) -> Pair (eval env e1, eval env e2) |
| Typed.Pair (e1,e2) -> Pair (eval env e1, eval env e2) |
| 58 |
|
| Typed.Xml (e1,e2) -> Xml (eval env e1, eval env e2) |
| 59 |
| Typed.Cst c -> const c |
| Typed.Cst c -> const c |
| 60 |
| Typed.Match (arg,brs) -> eval_branches env brs (eval env arg) |
| Typed.Match (arg,brs) -> eval_branches env brs (eval env arg) |
| 61 |
| Typed.Map (arg,brs) -> eval_map env brs (eval env arg) |
| Typed.Map (false,arg,brs) -> eval_map env brs (eval env arg) |
| 62 |
|
| Typed.Map (true,_,_) -> assert false |
| 63 |
|
| Typed.Xtrans (arg,brs) -> eval_xtrans env brs (eval env arg) |
| 64 |
| Typed.Op ("raise", [e]) -> raise (CDuceExn (eval env e)) |
| Typed.Op ("raise", [e]) -> raise (CDuceExn (eval env e)) |
| 65 |
| Typed.Try (arg,brs) -> |
| Typed.Try (arg,brs) -> |
| 66 |
(try eval env arg with CDuceExn v -> eval_branches env brs v) |
(try eval env arg with CDuceExn v -> eval_branches env brs v) |
| 67 |
|
| Typed.Op ("flatten", [{Typed.exp_descr=Typed.Map (true,arg,brs)}]) -> |
| 68 |
|
eval_transform env brs (eval env arg) |
| 69 |
| Typed.Op ("flatten", [e]) -> eval_flatten (eval env e) |
| Typed.Op ("flatten", [e]) -> eval_flatten (eval env e) |
| 70 |
| Typed.Op ("@", [e1; e2]) -> eval_concat (eval env e1) (eval env e2) |
| Typed.Op ("@", [e1; e2]) -> eval_concat (eval env e1) (eval env e2) |
| 71 |
| Typed.Op ("+", [e1; e2]) -> eval_add (eval env e1) (eval env e2) |
| Typed.Op ("+", [e1; e2]) -> eval_add (eval env e1) (eval env e2) |
| 72 |
| Typed.Op ("*", [e1; e2]) -> eval_mul (eval env e1) (eval env e2) |
| Typed.Op ("*", [e1; e2]) -> eval_mul (eval env e1) (eval env e2) |
| 73 |
| Typed.Op ("-", [e1; e2]) -> eval_sub (eval env e1) (eval env e2) |
| Typed.Op ("-", [e1; e2]) -> eval_sub (eval env e1) (eval env e2) |
| 74 |
| Typed.Op ("/", [e1; e2]) -> eval_div (eval env e1) (eval env e2) |
| Typed.Op ("/", [e1; e2]) -> eval_div (eval env e1) (eval env e2) |
| 75 |
|
| Typed.Op ("mod", [e1; e2]) -> eval_mod (eval env e1) (eval env e2) |
| 76 |
| Typed.Op ("load_xml", [e]) -> eval_load_xml (eval env e) |
| Typed.Op ("load_xml", [e]) -> eval_load_xml (eval env e) |
| 77 |
|
| Typed.Op ("load_html", [e]) -> eval_load_html (eval env e) |
| 78 |
|
| Typed.Op ("load_file", [e]) -> eval_load_file ~utf8:false (eval env e) |
| 79 |
|
| Typed.Op ("load_file_utf8", [e]) -> eval_load_file ~utf8:true (eval env e) |
| 80 |
|
| Typed.Op ("print_xml", [e]) -> Print_xml.print_xml ~utf8:false (eval env e) |
| 81 |
|
| Typed.Op ("print_xml_utf8", [e]) -> Print_xml.print_xml ~utf8:true (eval env e) |
| 82 |
|
| Typed.Op ("print", [e]) -> eval_print (eval env e) |
| 83 |
| Typed.Op ("int_of", [e]) -> eval_int_of (eval env e) |
| Typed.Op ("int_of", [e]) -> eval_int_of (eval env e) |
| 84 |
|
| Typed.Op ("atom_of", [e]) -> eval_atom_of (eval env e) |
| 85 |
|
| Typed.Op ("string_of", [e]) -> eval_string_of (eval env e) |
| 86 |
|
| Typed.Op ("dump_to_file", [e1; e2]) -> |
| 87 |
|
eval_dump_to_file (eval env e1) (eval env e2) |
| 88 |
|
| Typed.Op ("dump_to_file_utf8", [e1; e2]) -> |
| 89 |
|
eval_dump_to_file_utf8 (eval env e1) (eval env e2) |
| 90 |
|
| Typed.Op ("=",[e1; e2]) -> eval_equal (eval env e1) (eval env e2) |
| 91 |
|
| Typed.Op ("<",[e1; e2]) -> eval_lt (eval env e1) (eval env e2) |
| 92 |
|
| Typed.Op ("<=",[e1; e2]) -> eval_lte (eval env e1) (eval env e2) |
| 93 |
|
| Typed.Op (">",[e1; e2]) -> eval_gt (eval env e1) (eval env e2) |
| 94 |
|
| Typed.Op (">=",[e1; e2]) -> eval_gte (eval env e1) (eval env e2) |
| 95 |
| Typed.Dot (e, l) -> eval_dot l (eval env e) |
| Typed.Dot (e, l) -> eval_dot l (eval env e) |
| 96 |
| Typed.DebugTyper t -> failwith "Evaluating a ! expression" |
| Typed.RemoveField (e, l) -> eval_remove_field l (eval env e) |
| 97 |
| Typed.Op (o,_) -> failwith ("Unknown operator " ^ o) |
| Typed.Op (o,_) -> failwith ("Unknown operator " ^ o) |
| 98 |
|
|
| 99 |
|
|
| 100 |
and eval_apply f arg = match f with |
and eval_apply f arg = match f with |
| 101 |
| Abstraction (_,clos) -> clos arg |
| Abstraction (_,clos) -> clos arg |
| 102 |
| _ -> assert false |
| _ -> eval_concat f arg |
| 103 |
|
|
| 104 |
and eval_branches' env_ref brs arg = |
and eval_branches' env_ref brs arg = |
| 105 |
eval_branches !env_ref brs arg |
eval_branches !env_ref brs arg |
| 107 |
and eval_branches env brs arg = |
and eval_branches env brs arg = |
| 108 |
let (disp, rhs) = Typed.dispatcher brs in |
let (disp, rhs) = Typed.dispatcher brs in |
| 109 |
let (code, bindings) = run_dispatcher disp arg in |
let (code, bindings) = run_dispatcher disp arg in |
| 110 |
let (bind, e) = rhs.(code) in |
match rhs.(code) with |
| 111 |
|
| Patterns.Compile.Match (bind,e) -> |
| 112 |
let env = |
let env = |
| 113 |
List.fold_left (fun env (x,i) -> |
List.fold_left (fun env (x,i) -> |
| 114 |
if (i = -1) then Env.add x arg env |
if (i == -1) then Env.add x arg env |
| 115 |
else Env.add x bindings.(i) env) env bind in |
else Env.add x bindings.(i) env) env (IdMap.get bind) in |
| 116 |
eval env e |
eval env e |
| 117 |
|
| Patterns.Compile.Fail -> Value.Absent |
| 118 |
|
|
| 119 |
and eval_let_decl env l = |
and eval_let_decl env l = |
| 120 |
let v = eval env l.Typed.let_body in |
let v = eval env l.Typed.let_body in |
| 121 |
let (disp,bind) = Typed.dispatcher_let_decl l in |
let (disp,bind) = Typed.dispatcher_let_decl l in |
| 122 |
let (_,bindings) = run_dispatcher disp v in |
let (_,bindings) = run_dispatcher disp v in |
| 123 |
List.map (fun (x,i) -> (x, if (i = -1) then v else bindings.(i))) bind |
List.map (fun (x,i) -> (x, if (i == -1) then v else bindings.(i))) (IdMap.get bind) |
| 124 |
|
|
| 125 |
and eval_map env brs = function |
and eval_map env brs = function |
| 126 |
| Pair (x,y) -> Pair (eval_branches env brs x, eval_map env brs y) |
| Pair (x,y) -> |
| 127 |
| String (_,_,_,_) as v -> eval_map env brs (normalize v) |
let x = eval_branches env brs x in |
| 128 |
|
Pair (x, eval_map env brs y) |
| 129 |
|
| String_latin1 (_,_,_,_) | String_utf8 (_,_,_,_) as v -> |
| 130 |
|
eval_map env brs (normalize v) |
| 131 |
| q -> q |
| q -> q |
| 132 |
|
|
| 133 |
and eval_flatten = function |
and eval_flatten = function |
| 134 |
| Pair (x,y) -> eval_concat x (eval_flatten y) |
| Pair (x,y) -> eval_concat x (eval_flatten y) |
| 135 |
| q -> q |
| q -> q |
| 136 |
|
|
| 137 |
|
and eval_transform env brs = function |
| 138 |
|
| Pair (x,y) -> |
| 139 |
|
let x = match eval_branches env brs x with Value.Absent -> Value.nil | x -> x in |
| 140 |
|
eval_concat x (eval_transform env brs y) |
| 141 |
|
| String_latin1 (_,_,_,q) | String_utf8 (_,_,_,q) as v -> |
| 142 |
|
if Types.Char.is_empty (brs.Typed.br_accept) |
| 143 |
|
then eval_transform env brs q |
| 144 |
|
else eval_transform env brs (normalize v) |
| 145 |
|
| q -> q |
| 146 |
|
|
| 147 |
|
and eval_xtrans env brs = function |
| 148 |
|
| String_utf8 (s,i,j,q) as v -> |
| 149 |
|
if Types.Char.is_empty (brs.Typed.br_accept) |
| 150 |
|
then String_utf8 (s,i,j, eval_xtrans env brs q) |
| 151 |
|
else eval_xtrans env brs (normalize v) |
| 152 |
|
| String_latin1 (s,i,j,q) as v -> |
| 153 |
|
if Types.Char.is_empty (brs.Typed.br_accept) |
| 154 |
|
then String_latin1 (s,i,j, eval_xtrans env brs q) |
| 155 |
|
else eval_xtrans env brs (normalize v) |
| 156 |
|
| Pair (x,y) -> |
| 157 |
|
(match eval_branches env brs x with |
| 158 |
|
| Absent -> |
| 159 |
|
let x = match x with |
| 160 |
|
| Xml (tag, Pair (attr, child)) -> |
| 161 |
|
let child = eval_xtrans env brs child in |
| 162 |
|
Xml (tag, Pair (attr, child)) |
| 163 |
|
| Xml (_,_) -> assert false |
| 164 |
|
| x -> x in |
| 165 |
|
let y = eval_xtrans env brs y in |
| 166 |
|
Pair (x,y) |
| 167 |
|
| x -> |
| 168 |
|
let y = eval_xtrans env brs y in |
| 169 |
|
eval_concat x y) |
| 170 |
|
| q -> q |
| 171 |
|
|
| 172 |
and eval_concat l1 l2 = match l1 with |
and eval_concat l1 l2 = match l1 with |
| 173 |
| Pair (x,y) -> Pair (x, eval_concat y l2) |
| Pair (x,y) -> Pair (x, eval_concat y l2) |
| 174 |
| String (s,i,j,q) -> String (s,i,j, eval_concat q l2) |
| String_latin1 (s,i,j,q) -> String_latin1 (s,i,j, eval_concat q l2) |
| 175 |
|
| String_utf8 (s,i,j,q) -> String_utf8 (s,i,j, eval_concat q l2) |
| 176 |
| q -> l2 |
| q -> l2 |
| 177 |
|
|
| 178 |
and eval_dot l = function |
and eval_dot l = function |
| 179 |
| Record r -> List.assoc l r |
| Record r -> LabelMap.assoc l r |
| 180 |
|
| _ -> assert false |
| 181 |
|
|
| 182 |
|
and eval_remove_field l = function |
| 183 |
|
| Record r -> Record (LabelMap.remove l r) |
| 184 |
| _ -> assert false |
| _ -> assert false |
| 185 |
|
|
| 186 |
and eval_add x y = match (x,y) with |
and eval_add x y = match (x,y) with |
| 187 |
| (Integer x, Integer y) -> Integer (Big_int.add_big_int x y) |
| (Integer x, Integer y) -> Integer (Intervals.vadd x y) |
| 188 |
|
| Record r1, Record r2 -> Record (LabelMap.merge (fun x y -> y) r1 r2) |
| 189 |
| _ -> assert false |
| _ -> assert false |
| 190 |
|
|
| 191 |
and eval_mul x y = match (x,y) with |
and eval_mul x y = match (x,y) with |
| 192 |
| (Integer x, Integer y) -> Integer (Big_int.mult_big_int x y) |
| (Integer x, Integer y) -> Integer (Intervals.vmult x y) |
| 193 |
| _ -> assert false |
| _ -> assert false |
| 194 |
|
|
| 195 |
and eval_sub x y = match (x,y) with |
and eval_sub x y = match (x,y) with |
| 196 |
| (Integer x, Integer y) -> Integer (Big_int.sub_big_int x y) |
| (Integer x, Integer y) -> Integer (Intervals.vsub x y) |
| 197 |
| _ -> assert false |
| _ -> assert false |
| 198 |
|
|
| 199 |
and eval_div x y = match (x,y) with |
and eval_div x y = match (x,y) with |
| 200 |
| (Integer x, Integer y) -> Integer (Big_int.div_big_int x y) |
| (Integer x, Integer y) -> Integer (Intervals.vdiv x y) |
| 201 |
|
| _ -> assert false |
| 202 |
|
|
| 203 |
|
and eval_mod x y = match (x,y) with |
| 204 |
|
| (Integer x, Integer y) -> Integer (Intervals.vmod x y) |
| 205 |
| _ -> assert false |
| _ -> assert false |
| 206 |
|
|
| 207 |
and eval_load_xml e = |
and eval_load_xml e = |
| 208 |
Load_xml.run (get_string e) |
Load_xml.load_xml (get_string_latin1 e) |
| 209 |
|
(* Note: loading iso-8859-1 (even ASCII) files with utf-8 internal |
| 210 |
|
encoding has a non negligible overhead with PXP *) |
| 211 |
|
|
| 212 |
|
and eval_load_html e = |
| 213 |
|
Load_xml.load_html (get_string_latin1 e) |
| 214 |
|
|
| 215 |
|
and eval_load_file ~utf8 e = |
| 216 |
|
Location.protect_op "load_file"; |
| 217 |
|
let ic = open_in (get_string_latin1 e) in |
| 218 |
|
let len = in_channel_length ic in |
| 219 |
|
let s = String.create len in |
| 220 |
|
really_input ic s 0 len; |
| 221 |
|
close_in ic; |
| 222 |
|
if utf8 then |
| 223 |
|
if U.check s |
| 224 |
|
then Value.string_utf8 (U.mk s) |
| 225 |
|
else raise exn_load_file_utf8 |
| 226 |
|
else Value.string_latin1 s |
| 227 |
|
|
| 228 |
and eval_int_of e = |
and eval_int_of e = |
| 229 |
let s = get_string e in |
let s = get_string_latin1 e in |
| 230 |
try Integer (Big_int.big_int_of_string s) |
try Integer (Intervals.mk s) |
| 231 |
with Failure _ -> raise exn_int_of |
with Failure _ -> raise exn_int_of |
| 232 |
|
|
| 233 |
and get_string e = |
and eval_atom_of e = |
| 234 |
let rec compute_len accu = function |
let (s,_) = get_string_utf8 e in (* TODO: check that s is a correct Name wrt XML *) |
| 235 |
| Pair (_,y) -> compute_len (accu + 1) y |
Atom (Atoms.mk s) |
| 236 |
| String (i,j,_,y) -> compute_len (accu + j - i) y |
|
| 237 |
| _ -> accu in |
and eval_print v = |
| 238 |
let rec fill pos s = function |
Location.protect_op "print"; |
| 239 |
| Pair (Char x,y) -> s.[pos] <- Chars.Unichar.to_char x; fill (pos + 1) s y |
print_string (get_string_latin1 v); |
| 240 |
| String (i,j,src,y) -> |
flush stdout; |
| 241 |
String.blit src i s pos (j - i); fill (pos + j - i) s y |
Value.nil |
| 242 |
| _ -> s in |
|
| 243 |
fill 0 (String.create (compute_len 0 e)) e |
and eval_dump_to_file f v = |
| 244 |
|
Location.protect_op "dump_to_file"; |
| 245 |
|
let oc = open_out (get_string_latin1 f) in |
| 246 |
|
output_string oc (get_string_latin1 v); |
| 247 |
|
close_out oc; |
| 248 |
|
Value.nil |
| 249 |
|
and eval_dump_to_file_utf8 f v = |
| 250 |
|
Location.protect_op "dump_to_file_utf8"; |
| 251 |
|
let oc = open_out (get_string_latin1 f) in |
| 252 |
|
let (v,_) = get_string_utf8 v in |
| 253 |
|
output_string oc (U.get_str v); |
| 254 |
|
close_out oc; |
| 255 |
|
Value.nil |
| 256 |
|
|
| 257 |
|
|
| 258 |
|
and eval_string_of v = |
| 259 |
|
let b = Buffer.create 16 in |
| 260 |
|
let ppf = Format.formatter_of_buffer b in |
| 261 |
|
Value.print ppf v; |
| 262 |
|
Format.pp_print_flush ppf (); |
| 263 |
|
string_latin1 (Buffer.contents b) |
| 264 |
|
|
| 265 |
|
and eval_equal v1 v2 = |
| 266 |
|
let c = Value.compare v1 v2 in |
| 267 |
|
Value.vbool (Value.compare v1 v2 == 0) |
| 268 |
|
|
| 269 |
|
and eval_lt v1 v2 = |
| 270 |
|
let c = Value.compare v1 v2 in |
| 271 |
|
Value.vbool (Value.compare v1 v2 < 0) |
| 272 |
|
|
| 273 |
|
and eval_lte v1 v2 = |
| 274 |
|
let c = Value.compare v1 v2 in |
| 275 |
|
Value.vbool (Value.compare v1 v2 <= 0) |
| 276 |
|
|
| 277 |
|
and eval_gt v1 v2 = |
| 278 |
|
let c = Value.compare v1 v2 in |
| 279 |
|
Value.vbool (Value.compare v1 v2 > 0) |
| 280 |
|
|
| 281 |
|
and eval_gte v1 v2 = |
| 282 |
|
let c = Value.compare v1 v2 in |
| 283 |
|
Value.vbool (Value.compare v1 v2 >= 0) |