|
| 1 | +open Ast |
| 2 | +open Ds |
| 3 | + |
| 4 | +(* params * body * super * fields *) |
| 5 | +type method_decl = string list*Ast.expr*string*string list |
| 6 | + |
| 7 | +type method_env = (string*method_decl) list |
| 8 | + |
| 9 | +type class_decl = string*string list*method_env |
| 10 | + |
| 11 | +type class_env = ((string*class_decl) list) |
| 12 | + |
| 13 | +(* Global holding the store *) |
| 14 | +let g_store = Store.empty_store 20 (NumVal 0) |
| 15 | + |
| 16 | +(* Global holding class declarations *) |
| 17 | +let g_class_env : class_env ref = ref [] |
| 18 | + |
| 19 | + |
| 20 | +(* Helper functions for SOOL *) |
| 21 | + |
| 22 | +(* |
| 23 | + * Return all visible fields from class c_name |
| 24 | + * Note: Should produce an error if the super class does not exist, this is pending |
| 25 | +*) |
| 26 | +let rec get_fields cs c_name class_decls = |
| 27 | + match class_decls with |
| 28 | + | [] -> [] |
| 29 | + | Class (name,super,fields,_methods)::_ when name=c_name -> |
| 30 | + fields :: get_fields cs super cs |
| 31 | + | Class (_,_,_,_)::cs' -> get_fields cs c_name cs' |
| 32 | + |
| 33 | +(* |
| 34 | + * Return all visible methods from class c_name |
| 35 | + * Note: Should produce an error if the super class does not exist, this is pending |
| 36 | +*) |
| 37 | +let rec get_methods cs c_name fss = function |
| 38 | + | [] -> [] |
| 39 | + | Class (name,super,_fields,methods)::_ when name=c_name -> |
| 40 | + (List.map (fun (Method(n,pars,body)) |
| 41 | + -> (n,(pars,body,super,List.flatten fss))) |
| 42 | +methods) @ get_methods cs super (List.tl fss) cs |
| 43 | + | Class (_,_,_,_)::cs' -> get_methods cs c_name fss cs' |
| 44 | + |
| 45 | +(* |
| 46 | + * Initialize contents of g_class_env variable |
| 47 | +*) |
| 48 | +let initialize_class_env cs = |
| 49 | + let rec initialize_class_env' cs = function |
| 50 | + | [] -> () |
| 51 | + | Class (name,super,fields,methods)::cs' -> |
| 52 | + let fss = fields :: get_fields cs super cs |
| 53 | + in let ms = (List.map (fun (Method(n,pars,body)) |
| 54 | + -> (n,(pars,body,super,List.flatten fss))) |
| 55 | + methods) @ get_methods cs super (List.tl fss) cs |
| 56 | + in |
| 57 | + g_class_env := (name,(super,List.flatten fss,ms))::!g_class_env; |
| 58 | + initialize_class_env' cs cs' |
| 59 | + in g_class_env := []; |
| 60 | + initialize_class_env' cs cs |
| 61 | + |
| 62 | +let lookup_class : string -> class_env -> class_decl ea_result = fun c_name c_env -> |
| 63 | + if List.mem_assoc c_name c_env |
| 64 | + then return @@ List.assoc c_name c_env |
| 65 | +else error "lookup_class: not found" |
| 66 | + (* try return @@ List.assoc c_name c_env |
| 67 | + with Not_found -> error @@ "lookup_class: class "^c_name^" not found" *) |
| 68 | + |
| 69 | + |
| 70 | +(* ************************************* |
| 71 | +***************************************** |
| 72 | +****************************************** *) |
| 73 | + |
| 74 | + |
| 75 | +(* let fields_of_class_decl : class_decl -> string list ea_result = function |
| 76 | + | (_, fields, _) -> return fields |
| 77 | + | _ -> error "Error: expected class_decl" *) |
| 78 | + |
| 79 | +let rec new_env : string list -> env ea_result = fun x -> |
| 80 | + return @@ List.fold_left (fun env str -> |
| 81 | + (ExtendEnv(str, RefVal(Store.new_ref g_store (NumVal 0)), env))) EmptyEnv x |
| 82 | + |
| 83 | + |
| 84 | +let slice fs env = |
| 85 | + let rec slice' fs acc env = |
| 86 | + match fs, env with |
| 87 | + | [],_ -> acc |
| 88 | + | id::ids, ExtendEnv(id',ev,tail) when id=id' -> |
| 89 | + slice' ids (ExtendEnv(id',ev,acc)) tail |
| 90 | + | _,_ -> failwith "slice: ids different or lists have different lengths" |
| 91 | + in |
| 92 | + return (slice' (List.rev fs) EmptyEnv env) |
| 93 | + |
| 94 | + |
| 95 | + |
| 96 | +let lookup_method : string -> string -> class_env -> |
| 97 | + method_decl option = fun c_name m_name c_env -> |
| 98 | + match run @@ lookup_class c_name c_env with |
| 99 | + | Ok (_,_,m_env) -> begin |
| 100 | + if List.mem_assoc m_name m_env |
| 101 | + then Some(List.assoc m_name m_env) |
| 102 | + else None |
| 103 | + (* try Some(List.assoc m_name m_env) |
| 104 | + with Not_found -> None *) |
| 105 | + end |
| 106 | + | _ -> None |
| 107 | + |
| 108 | +(* Helper function for records *) |
| 109 | +let rec addIds fs evs = |
| 110 | + match fs,evs with |
| 111 | + | [],[] -> [] |
| 112 | + | (id,_)::t1, v::t2 -> (id,v):: addIds t1 t2 |
| 113 | + | _,_ -> failwith "error: lists have different sizes" |
| 114 | + |
| 115 | +let rec apply_method : string -> exp_val -> exp_val list -> |
| 116 | + method_decl -> exp_val ea_result = fun m_name self args (pars,body,super,fs) -> |
| 117 | + let l = Store.new_ref g_store self |
| 118 | + and l_args = List.map (fun ev -> RefVal (Store.new_ref g_store ev)) args |
| 119 | + in let l' = Store.new_ref g_store (StringVal super) |
| 120 | + in |
| 121 | + if List.length args<> List.length pars |
| 122 | + then error (m_name ^": args and params have different lengths") |
| 123 | + else |
| 124 | + obj_of_objectVal self >>= fun (_c_name,env) -> |
| 125 | + slice fs env >>+ |
| 126 | + extend_env_list ("_super"::"_self"::pars) ((RefVal l') |
| 127 | + ::(RefVal l)::l_args) >>+ |
| 128 | + eval_expr body |
| 129 | +and |
| 130 | + apply_proc ev1 ev2 = |
| 131 | + match ev1 with |
| 132 | + | ProcVal(par,body,en) -> |
| 133 | + return en >>+ |
| 134 | + extend_env par (RefVal (Store.new_ref g_store ev2)) >>+ |
| 135 | + eval_expr body |
| 136 | + | _ -> error "apply_proc: Not a procVal" |
| 137 | +and |
| 138 | + eval_expr : expr -> exp_val ea_result = fun e -> |
| 139 | + match e with |
| 140 | + | Int(n) -> return @@ NumVal n |
| 141 | + | Var(id) -> |
| 142 | + apply_env id >>= |
| 143 | + int_of_refVal >>= fun l -> |
| 144 | + (match Store.deref g_store l with |
| 145 | + | None -> error "Index out of bounds" |
| 146 | + | Some ev -> return ev) |
| 147 | + | Add(e1,e2) -> |
| 148 | + eval_expr e1 >>= |
| 149 | + int_of_numVal >>= fun n1 -> |
| 150 | + eval_expr e2 >>= |
| 151 | + int_of_numVal >>= fun n2 -> |
| 152 | + return @@ NumVal (n1+n2) |
| 153 | + | Sub(e1,e2) -> |
| 154 | + eval_expr e1 >>= |
| 155 | + int_of_numVal >>= fun n1 -> |
| 156 | + eval_expr e2 >>= |
| 157 | + int_of_numVal >>= fun n2 -> |
| 158 | + return @@ NumVal (n1-n2) |
| 159 | + | Mul(e1,e2) -> |
| 160 | + eval_expr e1 >>= |
| 161 | + int_of_numVal >>= fun n1 -> |
| 162 | + eval_expr e2 >>= |
| 163 | + int_of_numVal >>= fun n2 -> |
| 164 | + return @@ NumVal (n1*n2) |
| 165 | + | Div(e1,e2) -> |
| 166 | + eval_expr e1 >>= |
| 167 | + int_of_numVal >>= fun n1 -> |
| 168 | + eval_expr e2 >>= |
| 169 | + int_of_numVal >>= fun n2 -> |
| 170 | + if n2==0 |
| 171 | + then error "Division by zero" |
| 172 | + else return @@ NumVal (n1/n2) |
| 173 | + | Let(v,def,body) -> |
| 174 | + eval_expr def >>= fun ev -> |
| 175 | + let l = Store.new_ref g_store ev |
| 176 | + in extend_env v (RefVal l) >>+ |
| 177 | + eval_expr body |
| 178 | + | ITE(e1,e2,e3) -> |
| 179 | + eval_expr e1 >>= |
| 180 | + bool_of_boolVal >>= fun b -> |
| 181 | + if b |
| 182 | + then eval_expr e2 |
| 183 | + else eval_expr e3 |
| 184 | + | IsZero(e) -> |
| 185 | + eval_expr e >>= |
| 186 | + int_of_numVal >>= fun n -> |
| 187 | + return @@ BoolVal (n = 0) |
| 188 | + | Pair(e1,e2) -> |
| 189 | + eval_expr e1 >>= fun ev1 -> |
| 190 | + eval_expr e2 >>= fun ev2 -> |
| 191 | + return @@ PairVal(ev1,ev2) |
| 192 | + | Fst(e) -> |
| 193 | + eval_expr e >>= |
| 194 | + pair_of_pairVal >>= fun p -> |
| 195 | + return @@ fst p |
| 196 | + | Snd(e) -> |
| 197 | + eval_expr e >>= |
| 198 | + pair_of_pairVal >>= fun p -> |
| 199 | + return @@ snd p |
| 200 | + | Proc(id,e) -> |
| 201 | + lookup_env >>= fun en -> |
| 202 | + return (ProcVal(id,e,en)) |
| 203 | + | App(e1,e2) -> |
| 204 | + eval_expr e1 >>= fun v1 -> |
| 205 | + eval_expr e2 >>= fun v2 -> |
| 206 | + apply_proc v1 v2 |
| 207 | + | Letrec(id,par,e,target) -> |
| 208 | + let l = Store.new_ref g_store UnitVal in |
| 209 | + extend_env id (RefVal l) >>+ |
| 210 | + (lookup_env >>= fun env -> |
| 211 | + (let[@warning "-8"] Some _ = Store.set_ref g_store l (ProcVal(par,e,env)) |
| 212 | + in eval_expr target) |
| 213 | + ) |
| 214 | + (* Mutable references operations *) |
| 215 | + | Set(id,e) -> |
| 216 | + eval_expr e >>= fun ev -> |
| 217 | + apply_env id >>= |
| 218 | + int_of_refVal >>= fun l -> |
| 219 | + (match Store.set_ref g_store l ev with |
| 220 | + | None -> error "Index out of bounds" |
| 221 | + | Some _ -> return UnitVal) |
| 222 | + | BeginEnd([]) -> |
| 223 | + return UnitVal |
| 224 | + | BeginEnd(es) -> |
| 225 | + sequence (List.map eval_expr es) >>= fun vs -> |
| 226 | + return (List.hd (List.rev vs)) |
| 227 | + (* Record operations *) |
| 228 | + | Record(fs) -> |
| 229 | + sequence (List.map (fun (_, e) -> eval_expr e) fs) >>= fun evs -> |
| 230 | + return (RecordVal (addIds fs evs)) |
| 231 | + | Proj(e,id) -> |
| 232 | + eval_expr e >>= |
| 233 | + fields_of_recordVal >>= fun fs -> |
| 234 | + (match List.assoc_opt id fs with |
| 235 | + | None -> error "not found" |
| 236 | + | Some ev -> return ev) |
| 237 | + |
| 238 | + (* SOOL operations *) |
| 239 | + | NewObject(c_name,es) -> |
| 240 | + sequence (List.map eval_expr es) >>= fun args -> |
| 241 | + lookup_class c_name !g_class_env >>= fun (_, fields, methods) -> |
| 242 | + new_env fields >>= fun en -> begin |
| 243 | + try begin |
| 244 | + let self = (ObjectVal(c_name,en)) in |
| 245 | + let m = (List.assoc "initialize" methods) in |
| 246 | + apply_method "initialize" self args m >>= fun _ -> |
| 247 | + return self |
| 248 | + end |
| 249 | + with Not_found -> return @@ ObjectVal(c_name,en) |
| 250 | + end |
| 251 | + |
| 252 | + | Send(e,m_name,es) -> |
| 253 | + eval_expr e >>= fun s -> |
| 254 | + obj_of_objectVal s >>= fun (c_name, _) -> |
| 255 | + sequence (List.map eval_expr es) >>= fun args -> begin |
| 256 | + match lookup_method c_name m_name !g_class_env with |
| 257 | + | None -> error "Method not found" |
| 258 | + | Some m -> apply_method m_name s args m |
| 259 | + end |
| 260 | + | Self -> |
| 261 | + eval_expr (Var "_self") |
| 262 | + | Super(m_name,es) -> |
| 263 | + sequence (List.map eval_expr es) >>= fun args -> |
| 264 | + eval_expr (Var "_super") >>= |
| 265 | + string_of_stringVal >>= fun c_name -> |
| 266 | + eval_expr (Var "_self") >>= fun self ->begin |
| 267 | + match lookup_method c_name m_name !g_class_env with |
| 268 | + | None -> error "Method not found" |
| 269 | + | Some m -> apply_method m_name self args m |
| 270 | + end |
| 271 | + |
| 272 | + (* List operations* *) |
| 273 | + | List(es) -> |
| 274 | + sequence (List.map eval_expr es) >>= fun args -> |
| 275 | + return (ListVal args) |
| 276 | + | Cons(e1,e2) -> |
| 277 | + eval_expr e1 >>= fun ev -> |
| 278 | + eval_expr e2 >>= |
| 279 | + list_of_listVal >>= fun l -> |
| 280 | + return (ListVal (ev::l)) |
| 281 | + | Hd(e) -> |
| 282 | + eval_expr e >>= |
| 283 | + list_of_listVal >>= fun l -> |
| 284 | + return (List.hd l) |
| 285 | + | Tl(e) -> |
| 286 | + eval_expr e >>= |
| 287 | + list_of_listVal >>= fun l -> |
| 288 | + return (ListVal (List.tl l)) |
| 289 | + | EmptyPred(e) -> |
| 290 | + eval_expr e >>= |
| 291 | + list_of_listVal >>= fun l -> |
| 292 | + return (BoolVal (l=[])) |
| 293 | + (* Debug *) |
| 294 | + | Debug(_e) -> |
| 295 | + string_of_env >>= fun str_env -> |
| 296 | + let str_store = Store.string_of_store string_of_expval g_store |
| 297 | + in (print_endline (str_env^"\n"^str_store); |
| 298 | + error "Reached breakpoint") |
| 299 | + | _ -> error ("eval_expr: Not implemented: "^string_of_expr e) |
| 300 | +and |
| 301 | + eval_prog : prog -> exp_val ea_result = fun (AProg(cs, e)) -> |
| 302 | + initialize_class_env cs; (* Step 1 *) |
| 303 | + eval_expr e (* Step 2 *) |
| 304 | + |
| 305 | + |
| 306 | +(* Parse a string into an ast *) |
| 307 | + |
| 308 | +let parse s = |
| 309 | + let lexbuf = Lexing.from_string s in |
| 310 | + let ast = Parser.prog Lexer.read lexbuf in |
| 311 | + ast |
| 312 | + |
| 313 | +let lexer s = |
| 314 | + let lexbuf = Lexing.from_string s |
| 315 | + in Lexer.read lexbuf |
| 316 | + |
| 317 | + |
| 318 | +(* Interpret an expression *) |
| 319 | +let interp (s:string) : exp_val result = |
| 320 | + let c = s |> parse |> eval_prog |
| 321 | + in run c |
| 322 | + |
| 323 | +let read_file (filename:string) : string = |
| 324 | + let lines = ref [] in |
| 325 | + let chan = open_in filename in |
| 326 | + try |
| 327 | + while true do |
| 328 | + lines := input_line chan :: !lines |
| 329 | + done; |
| 330 | + "" (* never reaches this line *) |
| 331 | + with End_of_file -> |
| 332 | + close_in chan; |
| 333 | + String.concat "" (List.rev !lines) |
| 334 | + |
| 335 | +(* Interpret an expression read from a file with optional extension .rec *) |
| 336 | +let interpf (s:string) : exp_val result = |
| 337 | + let s = String.trim s (* remove leading and trailing spaces *) |
| 338 | + in let file_name = (* allow rec to be optional *) |
| 339 | + match String.index_opt s '.' with None -> s^".sool" | _ -> s |
| 340 | + in |
| 341 | + interp @@ read_file file_name |
| 342 | + |
| 343 | +let interpp () : exp_val result = |
| 344 | + interpf "ex1" |
| 345 | + |
| 346 | + |
| 347 | +let parsef (s:string) : Ast.prog = |
| 348 | + let s = String.trim s (* remove leading and trailing spaces *) |
| 349 | + in let file_name = (* allow rec to be optional *) |
| 350 | + match String.index_opt s '.' with None -> s^".sool" | _ -> s |
| 351 | + in |
| 352 | + parse @@ read_file file_name |
0 commit comments