open Ast (* Gestion des erreurs *) exception Error of string exception LocError of loc * string exception NoCorrespondingPrototype exception AmbiguousOverload let ty_assert x k = if not x then raise (Error (k)) let ty_error k = raise (Error (k)) let err_add_loc loc f = try f() with | Error(k) -> raise (LocError(loc, k)) | LocError(_, _) as e -> raise e | NoCorrespondingPrototype -> raise (LocError (loc, "No corresponding prototype")) | AmbiguousOverload -> raise (LocError (loc, "Ambiguous overload")) | Assert_failure (k, a, b) -> raise (LocError (loc, "(unexpected) Assertion failure: "^k^" at "^(string_of_int a)^":"^(string_of_int b))) | Not_found -> raise (LocError (loc, "(unexpected) Not found")) | Invalid_argument(k) -> raise (LocError (loc, "(unexpected) Invalid argument: "^k)) | Match_failure(k, a, b) -> raise (LocError (loc, "(unexpected) Match failure: "^k^" at "^(string_of_int a)^":"^(string_of_int b))) | Stack_overflow -> raise (LocError (loc, "(unexpected) Stack overflow")) | _ -> raise (LocError (loc, "(unexpected) Other error")) (* AST typés *) module Smap = Map.Make(String) type typ = | T_Int | Typenull | T_Void | TClass of tident | TPoint of typ type type_ref = typ * bool (* type d'une variable, avec ref? *) type texpression = { te_loc: loc; te_desc: texpr_desc; type_expr : typ*bool*bool; (* Type, référence?, valeur gauche? *) } and texpr_desc = | TEInt of int | TENull | TEThis | TEIdent of ident | TEAssign of texpression * texpression | TECallFun of ident * (texpression * bool) list * bool (* changé : te -> ident *) (* calls to non-virtual methods are compiled using TECallFun, with the object cons'ed at the begining of the arguments expression list *) (* for each argument, bool is is argument passed by reference ? *) (* final bool : is returned value a reference ? *) | TECallVirtual of texpression * int * (texpression * bool) list * bool (* object * index in vtable * arguments * is return value a reference? *) | TEUnary of unop * texpression | TEBinary of texpression * binop * texpression | TEMember of texpression * int (* object * position of member *) | TEPointerCast of texpression * int (* object * position of member *) | TENew of tcls * ident * (texpression * bool) list and tstr_expression = | TSEExpr of texpression | TSEStr of string and tstatement = | TSEmpty | TSExpr of texpression | TSIf of texpression * tstatement * tstatement | TSWhile of texpression * tstatement | TSFor of texpression list * texpression option * texpression list * tstatement | TSBlock of tblock | TSReturn of texpression option | TSDeclare of typ * ident | TSDeclareAssignExpr of type_ref * ident * texpression | TSDeclareAssignConstructor of tcls * ident * ident * (texpression * bool) list (* Class name of variable, variable name, constructor name, constructor arguments *) | TSWriteCout of tstr_expression list and tblock = tstatement list and tproto = { tp_virtual : (tcls_hier * int) option; (* only used for class methods ; if none then not virtual *) tp_name : ident; tp_unique_ident : ident; (* label de la fonction dans le code assembleur *) tp_class : tident option; (* p_class = none : standalone function *) tp_ret_type : type_ref option; (* p_class = some and p_ret_type = none : constructor *) tp_args : (type_ref * ident) list; } and tcls_supers = tcls_hier list and tcls_hier = { h_class : tident; mutable h_pos : int; mutable h_vtable : (int * tproto) list; (* only to be muted during class definition parsing *) h_supers : tcls_supers } and tcls = { tc_name : tident; tc_size : int; tc_hier : tcls_hier; (* tous les supers à tous les niveaux, plus la classe actuelle *) tc_members : (typ * int) Smap.t; (* type du membre * position du membre dans les données de l'objet *) tc_methods : tproto list; } type env = { e_globals : typ Smap.t; e_funs : tproto list; e_classes : tcls Smap.t; } and benv = { b_pe : env; b_locals : type_ref Smap.t; b_class : tcls option; } type tdeclaration = | TDGlobal of (typ * ident) | TDFunction of (tproto * tblock) | TDClass of tcls type tprogram = { prog_decls : tdeclaration list; prog_env : env; prog_main : ident; } (* Quelques fonctions utiles : *) let tproto_numbering = ref 1 let tproto_unique_number () = let k = !tproto_numbering in tproto_numbering := k + 1; string_of_int k let get_c env i = try Smap.find i env.e_classes with Not_found -> ty_error ("No such class: " ^ i) let rec bf env t = let rec aux = function (* true si bien formé *) | T_Int -> true | TClass n -> Smap.mem n env.e_classes | TPoint t -> aux t | _ -> false in aux t let num = function | T_Int -> true | Typenull -> true | TPoint _ -> true | _ -> false (* !! modifier si on peut pas être un type num peut pas aller avec une ref *) let build_type_or_ref vt = (* vt -> typ,bool = tr, true si ref *) let rec see = function | TPtr vt -> TPoint (see vt) | TVoid -> T_Void | TInt -> T_Int | TRef _ -> ty_error ("Unexpected reference type - no pointers on references allowed") | TIdent tid -> TClass tid in match vt with | TRef (TRef vt) -> ty_error ("Double references not allowed") (* ... *) | TRef vt -> (see vt),true (* indique qu'il s'agit d'une ref *) | vt -> (see vt),false let rec subtype env a b = match a, b with | T_Int, T_Int -> true | T_Void, T_Void -> true | Typenull, TPoint(_) -> true | TPoint(ka), TPoint(kb) -> subtype env ka kb | TClass(i), TClass(j) -> let c = get_c env i in let rec find_in_hier h = h.h_class = j || (List.length (List.filter find_in_hier h.h_supers) = 1) in find_in_hier c.tc_hier | _ -> false let relative_class_position env i j = let c = get_c env i in let rec find_in_hier h = h.h_class = j || (List.length (List.filter find_in_hier h.h_supers) = 1) and get_in_hier h = if h.h_class = j then h.h_pos else match List.filter find_in_hier h.h_supers with | [a] -> get_in_hier a | _ -> assert false in get_in_hier c.tc_hier let rec upcast env exp dt = (* présupposé : exp.type_expr <= dt *) match exp.type_expr, dt with | (T_Int, _, _), T_Int -> exp | (T_Void, _, _), T_Void -> exp | (Typenull, _, _), TPoint(_) -> exp | (TClass(i), a, b), TClass(j) when a||b -> begin match relative_class_position env i j with | 0 -> exp | pos -> { type_expr = (TClass(j), false, true); te_loc = exp.te_loc; te_desc = TEMember(exp, pos) } end | (TPoint(TClass(i)), a, b), TPoint(TClass(j)) -> begin match relative_class_position env i j with | 0 -> exp | pos -> { type_expr = (TPoint(TClass(j)), false, true); te_loc = exp.te_loc; te_desc = TEPointerCast(exp, pos) } end | (TPoint(ka), _, _), TPoint(kb) -> exp | _ -> assert false let type_size env t = match t with | T_Int | Typenull | TPoint(_) -> 4 | T_Void -> 0 | TClass(c) -> let c = get_c env c in c.tc_size (* pour la surcharge de fonctions *) let possible_protos env arg_type_list fun_list = List.filter (fun proto -> try List.for_all2 (fun (t_a, t_a_ref) (t_p, t_p_ref) -> if t_p_ref && (not t_a_ref) then false else subtype env t_a t_p) arg_type_list (List.map fst proto.tp_args) with Invalid_argument _ -> false) fun_list let closest_proto env arg_type_list fun_list = match possible_protos env arg_type_list fun_list with | [] -> raise NoCorrespondingPrototype | [p] -> p | _ -> raise AmbiguousOverload let find_protos_in_class env cls name = let rec aux s = match List.filter (fun p -> p.tp_name = name) (get_c env s.h_class).tc_methods with | [] -> List.fold_left (fun q r -> match q, (aux r) with | [], l -> l | l, [] -> l | _, _ -> raise AmbiguousOverload) [] s.h_supers | k -> k in aux (get_c env cls).tc_hier let find_cls_mem env cls_name mem_name = let rec aux s = begin try let mty, mi = Smap.find mem_name (get_c env s.h_class).tc_members in Some (mty, mi + s.h_pos) with Not_found -> List.fold_left (fun q r -> match q, (aux r) with | Some l, None -> Some l | None, Some l -> Some l | None, None -> None | _, _ -> ty_error ("Ambiguous reference to member " ^ mem_name)) None s.h_supers end in match aux (get_c env cls_name).tc_hier with | Some k -> k | None -> raise Not_found let find_cls_superclass env cls_name superclass = let rec aux s = if s.h_class = superclass then Some s else List.fold_left (fun q r -> match q, aux r with | Some l, None | None, Some l -> Some l | None, None -> None | _, _ -> ty_error ("Ambiguous reference to superclass " ^ superclass)) None s.h_supers in match aux (get_c env cls_name).tc_hier with | Some k -> k | None -> raise Not_found (* -------------------------------------------- *) (* On passe aux choses sérieuses *) let rec type_expr env e = (* expression -> texpression *) err_add_loc e.e_loc (fun () -> let d,(ty,b1,b2) = compute_type env e in { te_loc = e.e_loc; te_desc = d; type_expr = (ty,b1,b2) } ) and get_expr0 env e = (* expression -> texpression,(ty,b1,b2) *) let te = type_expr env e in (te,te.type_expr) and get_expr env e = (* expression -> texpression,(ty,b) *) let te = type_expr env e in let (ty,b,_) = te.type_expr in (te,(ty,b)) and compute_type env e = let ttype = (TClass(match env.b_class with | Some c -> c.tc_name | None -> "#")) in let e_this = { te_loc = e.e_loc; te_desc = TEThis; type_expr = TPoint(ttype), false, true } in let e_this_not_ptr = { te_loc = e.e_loc; te_desc = TEUnary(Deref, e_this); type_expr = ttype, false, true; } in match e.e_desc with (* expression -> te_desc,(typ,ref?,left?) *) | EInt n -> TEInt n, (T_Int,false,false) (* false, : pas une ref, pas une val gauche*) | EBool b -> let n = (if b then 1 else 0) in TEInt n, (T_Int,false,false) | ENull -> TENull, (Typenull,false,false) | EIdent i -> begin try let t, r = Smap.find i env.b_locals in TEIdent i, (t, r, true) with Not_found -> try match env.b_class with | Some k -> let mty, mi = find_cls_mem env.b_pe k.tc_name i in TEMember(e_this_not_ptr, mi), (mty, false, true) | None -> raise Not_found with Not_found -> try let t = Smap.find i env.b_pe.e_globals in TEIdent i, (t, false, true) with Not_found -> ty_error ("Undeclared identifier: " ^ i) end | EQIdent(c, i) -> begin match env.b_class with | Some k -> let sc = try find_cls_superclass env.b_pe k.tc_name c with Not_found -> ty_error (c ^ " is no superclass of current class " ^ k.tc_name) in let mty, mi = find_cls_mem env.b_pe sc.h_class i in TEMember(e_this_not_ptr, mi + sc.h_pos), (mty, false, true) | None -> ty_error "Qualified identifier invalid in function belonging to no class." end | EAssign (e1,e2) -> let te1,(ty1,r3,b3) = get_expr0 env e1 in let te2,(ty2,_,_) = get_expr0 env e2 in ty_assert (b3 || r3) "Can only assign to lvalue"; ty_assert (num ty1) "Cannot assign to non-numeric type (pointer type is numeric)"; ty_assert (subtype env.b_pe ty2 ty1) "Incompatible types in assign"; (* type num et ref compatibles ?*) (TEAssign (te1,upcast env.b_pe te2 ty1) ),(ty1,false,false) | EUnary (op,e) -> let te,(ty,b1,b2) = get_expr0 env e in (match op with | PreIncr | PostIncr | PreDecr | PostDecr -> ty_assert (b2 = true) "Can only increment/decrement lvalue"; ty_assert (ty = T_Int) "Can only increment/decrement integers"; TEUnary(op,te),(T_Int,b1,false) | Plus | Minus | Not -> ty_assert (ty = T_Int) "Can only apply unary plus/minus/not to integers"; TEUnary(op,te),(T_Int,false,false) | Ref -> ty_assert b2 "Can only reference lvalues"; TEUnary(op,te),(TPoint ty,false,false) (* verif *) | Deref -> let t = (match ty with | TPoint t -> t | _ -> ty_error "Can only dereference pointer" ) in TEUnary(op,te), (t,false,true) ) | EBinary (e1,op,e2) -> let te1,(ty1,_,b1) = get_expr0 env e1 in let te2,(ty2,_,b2) = get_expr0 env e2 in (match op with | Equal | NotEqual -> ty_assert ((subtype env.b_pe ty1 ty2) || (subtype env.b_pe ty2 ty1)) "Can only apply == or != to two values of compatible type"; ty_assert (num ty1) "Can only apply == or != to pointers"; let te1 = if subtype env.b_pe ty1 ty2 then upcast env.b_pe te1 ty2 else te1 in let te2 = if subtype env.b_pe ty2 ty1 then upcast env.b_pe te2 ty1 else te2 in TEBinary(te1,op,te2),(T_Int,false,false) | Lt | Le | Gt | Ge | Add | Sub | Mul | Div | Modulo | Land | Lor -> ty_assert (ty1 = T_Int) "Left operand of binop is not integer"; ty_assert (ty2 = T_Int) "Right operand of binop is not integer"; TEBinary(te1,op,te2),(T_Int,false,false) ) | ECall (e,e_list) -> let args_values = List.map (get_expr0 env) e_list in let args_types = List.map (fun (e, (t, r, l)) -> t, r||l) args_values in let obj, tproto = (match e.e_desc with | EIdent i -> let funs = List.filter (fun p -> p.tp_name = i) env.b_pe.e_funs in begin match env.b_class with | None -> None, closest_proto env.b_pe args_types funs | Some k -> begin try let proto = closest_proto env.b_pe args_types (find_protos_in_class env.b_pe k.tc_name i) in let upcasted = if proto.tp_virtual = None then e_this_not_ptr else upcast env.b_pe e_this_not_ptr (TClass (match proto.tp_class with | None -> assert false | Some k -> k)) in Some upcasted, proto with NoCorrespondingPrototype -> None, closest_proto env.b_pe args_types funs end end | EMember(e, i) -> let e = type_expr env e in begin match e.type_expr with | TClass(k), a, b when a || b -> let proto = closest_proto env.b_pe args_types (find_protos_in_class env.b_pe k i) in let upcasted = if proto.tp_virtual = None then e else upcast env.b_pe e (TClass (match proto.tp_class with | None -> assert false | Some k -> k)) in Some upcasted, proto | _ -> ty_error "Invalid argument type for method call (not a class, or not a lvalue)" end | EQIdent(c, i) -> begin match env.b_class with | Some k -> let sc = try find_cls_superclass env.b_pe k.tc_name c with Not_found -> ty_error (c ^ " is no superclass of current class " ^ k.tc_name) in let proto = closest_proto env.b_pe args_types (find_protos_in_class env.b_pe sc.h_class i) in let upcasted = if proto.tp_virtual = None then upcast env.b_pe e_this_not_ptr (TClass(c)) else upcast env.b_pe e_this_not_ptr (TClass (match proto.tp_class with | None -> assert false | Some k -> k)) in Some upcasted, proto | None -> ty_error "Qualified identifier in a function belonging to no class." end | _ -> ty_error "Calling something that is neither a function nor a method") in let l_te = List.map fst args_values in let l_te = List.map2 (fun k ((ty, r), _) -> upcast env.b_pe k ty, r) l_te tproto.tp_args in let ty,b = match tproto.tp_ret_type with | None -> ty_error "Constructor cannot be called as function" | Some (ty,b) -> ty,b in begin match tproto.tp_virtual, obj with | None, None -> TECallFun(tproto.tp_unique_ident,l_te,b),(ty,b,false) | None, Some(obj)-> TECallFun(tproto.tp_unique_ident,(obj, true)::l_te,b),(ty,b,false) | Some(hier, idx), Some(obj) -> TECallVirtual(upcast env.b_pe obj (TClass hier.h_class), idx, l_te,b),(ty,b,false) | _ -> ty_error "(should not happen) Virtual function applied to no object..." end | EMember (e, id) -> let e, (ty, r, l) = get_expr0 env e in begin match ty with | TClass(c_name) -> begin try let mty, mi = find_cls_mem env.b_pe c_name id in TEMember(e, mi), (mty, false, true) with | Not_found -> ty_error ("Class " ^ c_name ^ " has no member " ^ id) end | _ -> ty_error "Cannot get member of expression that is not a class" end | ENew (cls_name, args) -> let c = get_c env.b_pe cls_name in let args_values = List.map (get_expr0 env) args in let args_types = List.map (fun (e, (t, r, l)) -> t, r||l) args_values in let candidates = List.filter (fun p -> p.tp_ret_type = None) c.tc_methods in begin match candidates with | [] -> assert false (* default constructor should always be in list *) | _ -> let p = closest_proto env.b_pe args_types candidates in (* closest_proto makes sure the prototypes match, no problem here *) let l_te = List.map fst args_values in let l_te = List.map2 (fun k ((ty, r), _) -> upcast env.b_pe k ty, r) l_te p.tp_args in TENew(c, p.tp_unique_ident, l_te), (TPoint(TClass(cls_name)), false, false) end | EThis -> begin match env.b_class with | Some c -> TEThis, (TPoint(TClass(c.tc_name)), false, true) | None -> ty_error "Cannot use this outside of method" end (* Statements *) let rec type_stm ret_type env s = err_add_loc s.s_loc (fun () -> compute_type_stm ret_type env s) and compute_type_stm ret_type env s = match s.s_desc with (* statement -> ts_desc,stm_type *) | SEmpty -> TSEmpty,env | SExpr e -> let te,(ty,_) = get_expr env e in (* verif ty est bien typé *) (TSExpr te) , env | SBlock b -> build_block ret_type env b | SReturn None -> let ty, ref = ret_type in ty_assert (ty = T_Void) "Function must return non-void value"; (TSReturn None) , env | SReturn (Some e0) -> let te,(ty,r,l) = get_expr0 env e0 in let rty, rref = ret_type in ty_assert (rty = ty) "Invalid return type"; ty_assert (if rref then r||l else true) "Function must return reference"; TSReturn (Some te), env | SIf (e,s1,s2) -> let te,(ty,_) = get_expr env e in let ts1,ty1 = type_stm ret_type env s1 in (* vérifs règle *) let ts2,ty2 = type_stm ret_type env s2 in ty_assert (ty = T_Int) "Condition in if statement must be integer"; (TSIf (te,ts1,ts2)) , env | SFor (el1,eopt,el3,s) -> let tel1 = List.map (type_expr env) el1 in (* et fait les vérifs pr e1 et e3 ? *) let tel3 = List.map (type_expr env) el3 in let teopt = (match eopt with | None -> None | Some e -> let te,(ty,_) = get_expr env e in ty_assert (ty = T_Int) "Condition in for statement must be integer"; Some te) in ignore( type_stm ret_type env s ); (* vérifie i *) let ts, _ = type_stm ret_type env s in (* fait le truc d'avant aussi *) TSFor (tel1,teopt,tel3,ts) , env (* traduire règles restantes du for*) | SWhile(e,s) -> let ts,tys = type_stm ret_type env s in let te,(ty,_) = get_expr env e in ty_assert (ty = T_Int) "Condition in while statement must be integer"; TSWhile(te,ts),env (* pq while n'est pas dans les règles données ? *) | SDeclare(vt,i) -> let ty,b = build_type_or_ref vt in ty_assert (bf env.b_pe ty) "Malformed type"; ty_assert (not b) "Reference must be assigned at declaration"; ty_assert (not (Smap.mem i env.b_locals) ) "Variable redefinition"; let env0 = { env with b_locals = Smap.add i (ty,b) env.b_locals } in TSDeclare( ty ,i) , env0 | SDeclareAssignExpr(vt,i,e) -> let ty,b = build_type_or_ref vt in ty_assert (bf env.b_pe ty) "Malformed type"; ty_assert (not (Smap.mem i env.b_locals)) "Variable redefinition"; let te,(tye,r,l) = get_expr0 env e in ty_assert (if b then r || l else true) "Can only assigne lvalue/reference to reference type var"; ty_assert (subtype env.b_pe tye ty) "Invalid data type for assign."; let env0 = { env with b_locals = Smap.add i (ty,b) env.b_locals } in TSDeclareAssignExpr( (ty,b) ,i,upcast env.b_pe te ty) , env0 | SDeclareAssignConstructor(vt,i,ti,e_l) -> let ty, b = build_type_or_ref vt in ty_assert (bf env.b_pe ty) "Malformed type"; ty_assert (not (Smap.mem i env.b_locals)) "Variable redefinition"; ty_assert (not b) "Cannot have reference on a newly created object"; ty_assert (ty = (TClass ti)) "Invalid type for constructor"; let c = get_c env.b_pe ti in let args_values= List.map (get_expr0 env) e_l in let args_types = List.map (fun (e, (t, r, l)) -> t, r||l) args_values in let candidates = List.filter (fun p -> p.tp_ret_type = None) c.tc_methods in begin match candidates with | [] -> assert false (* ... *) | _ -> let p = closest_proto env.b_pe args_types candidates in (* closest_proto makes sure the prototypes match, no problem here *) let l_te = List.map fst args_values in let l_te = List.map2 (fun k ((ty, r), _) -> upcast env.b_pe k ty, r) l_te p.tp_args in let env0 = { env with b_locals = Smap.add i (ty,b) env.b_locals } in TSDeclareAssignConstructor(c, i, p.tp_unique_ident, l_te), env0 end | SWriteCout(str_e_list) -> let args = List.map (fun e -> match e.se_desc with | SEExpr e0 -> let te,(ty,_) = get_expr env {e_loc = e.se_loc; e_desc = e0} in ty_assert (ty = T_Int) "Expected integer or string in cout<<"; TSEExpr te | SEStr s -> TSEStr(s) (* osef *) ) str_e_list in TSWriteCout(args) , env and build_block ret_type env b = (* utilisé ds compute_type_stm et def_global_fun *) let two_stms (env,l) s = let ts,env2 = type_stm ret_type env s in (env2,(ts::l)) in let ty_final,ts_list = List.fold_left two_stms (env,[]) b in (* verif si b bien typé (règle i1;i2) et construit le te-block*) TSBlock (List.rev ts_list),env and get_block ret_type env b = match fst (build_block ret_type env b) with | TSBlock tb -> tb | _ -> assert false (* Déclarations de fonction *) let parse_args env a = let args = List.map (fun (t, i) -> let t, r = build_type_or_ref t in ty_assert (bf env t) ("Malformed argument type for argument " ^ i ^ "."); (t, r), i) a in let rec aux = function | [] -> () | p::q -> ty_assert (not (List.mem p q)) ("Argument name appears twice : " ^ p); aux q in aux (List.map snd args); args let get_fun env p b = (* p : proto b : block -> tp, tb, env2*) assert (p.p_class = None); let name = p.p_name in let ty_args = parse_args env p.p_args in (* Check there is not already a function with similar prototype *) let args_type = List.map fst ty_args in ty_assert (not (List.exists (fun p -> p.tp_name = name && (List.map fst p.tp_args) = args_type) env.e_funs)) ("Redefinition of function: " ^ name); let ret_type = build_type_or_ref (match p.p_ret_type with | Some k -> k | None -> ty_error "Internal error (function with no return type)" ) in (* Add to env *) let tproto = { tp_name = name ; tp_unique_ident = name ^ (tproto_unique_number()); tp_class = None ; tp_virtual = None ; tp_ret_type = Some ret_type ; tp_args = ty_args; } in let env2 = { env with e_funs = tproto::(env.e_funs) } in (* Build local env *) let locales = List.fold_left (* tr = (ty,ref?) *) (fun envir (tr,i) -> Smap.add i tr envir) Smap.empty ty_args in (* contexte ds l'instruction *) let contexte = { b_pe = env2; b_locals = locales; b_class = None } in let tb = get_block ret_type contexte b in (* vérif instructions typées*) tproto,tb, env2 (* Déclarations de classes *) let compute_tclass env c = let cls_name = c.c_name in ty_assert (not (Smap.mem cls_name env.e_classes)) ("Redeclaration of class " ^cls_name^"."); (* artifice pour que la classe en train d'être définie puisse être utilisée par elle-même *) let forward_def = { tc_name = cls_name; tc_size = 0; tc_hier = { h_class = cls_name; h_pos = 0; h_vtable = []; h_supers = [] } ; tc_members = Smap.empty; tc_methods = []; } in let forward_env = { env with e_classes = (Smap.add cls_name forward_def env.e_classes); } in let super_list = match c.c_supers with | None -> [] | Some l -> l in let hier, used = let rec move_super diff s = { s with h_pos = s.h_pos + diff; h_supers = List.map (move_super diff) s.h_supers } in let sup, used = List.fold_left (fun (sup, u) n -> let c = get_c env n in (move_super u c.tc_hier)::sup, u + c.tc_size) ([], 4) super_list in { h_class = cls_name; h_pos = 0; h_vtable = []; h_supers = sup }, used in let (mem, mem_u), meth = List.fold_left (fun ((mem, mem_u), meth) n -> match n with | CVar(t, i) -> let t, r = build_type_or_ref t in ty_assert (not r) "Class members cannot be references."; ty_assert (bf forward_env t) ("Malformed type for member " ^ i ^ "."); ty_assert (t <> TClass(cls_name)) "Class cannot contain itself as a member."; ty_assert (not (Smap.mem i mem)) ("Redefinition of class member " ^ i ^ " in class " ^ cls_name ^ "."); let size = type_size env t in ((Smap.add i (t, mem_u) mem, mem_u + size), meth) | CMethod(proto, virt) -> let m = err_add_loc proto.p_loc (fun () -> ty_assert (proto.p_class = None) "Overqualification in prototype."; ty_assert (proto.p_ret_type <> None || proto.p_name = cls_name) "Invalid name for constructor"; (* Make sure prototype is well formed *) let args = parse_args forward_env proto.p_args in (* Make sure method is compatible with other declarations in this class *) ty_assert (not (List.exists (fun p -> p.tp_name = proto.p_name && (List.map fst p.tp_args) = (List.map fst args)) meth)) ("Redefinition of function " ^ proto.p_name ^ " with same argument types."); (* Check return type *) let ret_type = match proto.p_ret_type with | Some k -> Some (build_type_or_ref k) | None -> None in (* If method is redefined from a virtual method of a parent class, it becomes virtual with same offset Else if method is virtual, it gets new offset ! Else method is not virtual, everything is simple. *) let rec check_in_super (s:tcls_hier) = match List.fold_left (fun k s -> let r = check_in_super s in match k, r with | None, None -> None | None, Some k -> Some k | Some k, None -> None | Some k, Some r -> ty_error ("Ambiguous redefinition of " ^ proto.p_name)) None s.h_supers with | Some k -> Some k | None -> List.fold_left (fun f (i, p) -> if (p.tp_name = proto.p_name && (List.map fst p.tp_args) = (List.map fst args)) then Some (s, i) else f) None s.h_vtable in let super = match check_in_super hier with | None -> if virt then (* allocate new spot in vtable of this object *) Some (hier, List.fold_left (fun n (x, _) -> max n (x+4)) 0 hier.h_vtable) else None | Some k -> Some k in (* Build proto *) let tproto = { tp_virtual = super; tp_name = proto.p_name; tp_unique_ident = proto.p_name ^ (tproto_unique_number()) ; tp_class = Some(cls_name); tp_ret_type = ret_type; tp_args = args; } in (* Add to vtable *) begin match super with | None -> () | Some (c, i) -> c.h_vtable <- (i, tproto)::(List.remove_assoc i c.h_vtable) end; tproto) in (mem, mem_u), m::meth ) ((Smap.empty, used), []) c.c_members in (* make sure class has default constructor *) let meth = if List.exists (fun p -> p.tp_ret_type = None && p.tp_name = cls_name) meth then meth else { tp_virtual = None; tp_name = cls_name; tp_unique_ident = cls_name ^ "0"; tp_class = Some cls_name; tp_ret_type = None; tp_args = [] }::meth in (* if vtable is empty, remove it *) let mem, mem_u = if hier.h_vtable = [] then let rec mv_h h = h.h_pos <- h.h_pos - 4; List.iter mv_h h.h_supers in List.iter mv_h hier.h_supers; Smap.map (fun (ty, pos) -> (ty, pos-4)) mem, mem_u - 4 else mem, mem_u in { tc_name = cls_name; tc_size = mem_u; tc_hier = hier; tc_members = mem; tc_methods = meth; } let get_method env proto block = (* return : TDFunction *) match proto.p_class with | None -> assert false | Some(cls_name) -> try let c = get_c env cls_name in let args = parse_args env proto.p_args in let ret_type = match proto.p_ret_type with | Some k -> Some (build_type_or_ref k) | None -> None in (* Find prototype in class *) begin try let cproto = List.find (fun p -> p.tp_args = args && p.tp_ret_type = ret_type && p.tp_name = proto.p_name) c.tc_methods in let locals = List.fold_left (fun env (tr, i) -> Smap.add i tr env) Smap.empty args in let contexte = { b_pe = env; b_locals = locals; b_class = Some c; } in let tb = get_block (match ret_type with | None -> T_Void, false | Some k -> k) contexte block in cproto, tb with | Not_found -> ty_error ("Implementation corresponds to no declared method of class " ^ cls_name) end with | Not_found -> ty_error (cls_name ^ " is not defined.") (* Partie générique *) let compute_decl env d = err_add_loc (d.d_loc) (fun () -> match d.d_desc with | DGlobal(t,i) -> let tr, r = build_type_or_ref t in ty_assert (bf env tr) ("Malformed type for global var " ^ i); ty_assert (not r) "Global cannot be reference"; ty_assert (not (Smap.mem i env.e_globals)) ("Redeclaration of " ^ i); ty_assert (not (List.exists (fun p -> p.tp_name = i) env.e_funs)) ("Redeclaration of: " ^ i ^ ", was previously a function"); (TDGlobal(tr,i)) , { env with e_globals = (Smap.add i tr env.e_globals); } (* on voudrait une liste de ident pr decl plsr en meme temps *) | DFunction (p,b) -> ty_assert (not (Smap.mem p.p_name env.e_globals)) ("Redeclaration of: " ^ p.p_name ^ ", was previously a global variable"); begin match p.p_class with | None -> let tp, tb, env2 = get_fun env p b in TDFunction(tp, tb), env2 | Some _ -> let tp, tb = get_method env p b in (TDFunction(tp, tb)), env(* env is not modified *) end | DClass c -> let tc = compute_tclass env c in (TDClass tc), { env with e_classes = Smap.add c.c_name tc env.e_classes; } ) let prog p = let decls, env = ( List.fold_left (fun (decls, env) decl -> let decl_p, env2 = compute_decl env decl in decl_p::decls, env2) ([],{ e_globals = Smap.empty; e_funs = []; e_classes = Smap.empty }) p ) in let p = try List.find (fun tp -> tp.tp_class = None && tp.tp_name = "main" && tp.tp_args = [] && tp.tp_ret_type = Some (T_Int,false)) env.e_funs with Not_found -> ty_error "No correct main function in program." in { prog_decls = List.rev decls; prog_env = env; prog_main = p.tp_unique_ident }