(***********************************************************************) (* *) (* Heptagon *) (* *) (* Gwenael Delaval, LIG/INRIA, UJF *) (* Leonard Gerard, Parkas, ENS *) (* Adrien Guatto, Parkas, ENS *) (* Cedric Pasteur, Parkas, ENS *) (* Marc Pouzet, Parkas, ENS *) (* *) (* Copyright 2012 ENS, INRIA, UJF *) (* *) (* This file is part of the Heptagon compiler. *) (* *) (* Heptagon is free software: you can redistribute it and/or modify it *) (* under the terms of the GNU General Public License as published by *) (* the Free Software Foundation, either version 3 of the License, or *) (* (at your option) any later version. *) (* *) (* Heptagon is distributed in the hope that it will be useful, *) (* but WITHOUT ANY WARRANTY; without even the implied warranty of *) (* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *) (* GNU General Public License for more details. *) (* *) (* You should have received a copy of the GNU General Public License *) (* along with Heptagon. If not, see *) (* *) (***********************************************************************) (** Scoping. Introduces unique indexes for local names and replace global names by qualified names *) (* [local_const] is the environnement with local constant variables, that is for now only the statics node parameters. It is built with [build_const]. When qualifying a constant var, it is first check in the local_const env, so qualified with [local_qn] if not found we try to qualify it with the global env. *) (* The global environement is initialized by the scoping pass. This allow at the same time to qualify types, constants, constructors, fields and node calls, according to the current module definitions and opened modules. *) (* [env] of type Rename.t is the renaming environnement used to map a var name to a var ident. It is initialized at node declaration level with the inputs and outputs, and then appended with the local var declarations at each block level with the [build] function. It checks that if a var is used with a last, it is declared as a last.*) (* convention : Static operators get static params and static args. This scoping set the static params as the first static args : op (a3) ==> op (a2,a3) ==> op (a1,a2,a3) *) open Location open Types open Hept_parsetree open Names open Idents open Format open Static open Global_printer open Modules module Error = struct type error = | Evar_unbound of name | Equal_notfound of name*qualname | Equal_unbound of name*name | Enot_last of name | Evariable_already_defined of name | Econst_variable_already_defined of name | Estatic_exp_expected | Eredefinition of qualname | Elinear_type_no_memalloc let message loc kind = begin match kind with | Evar_unbound name -> eprintf "%aThe variable %s is unbound.@." print_location loc name | Equal_notfound (s,q) -> eprintf "%aThe qualified %s %a can't be found.@." print_location loc s print_qualname q | Equal_unbound (s,n) -> eprintf "%aUnbound %s %a.@." print_location loc s print_name n | Enot_last name -> eprintf "%aThe variable %s should be declared as a last.@." print_location loc name | Evariable_already_defined name -> eprintf "%aThe variable %s is already defined.@." print_location loc name | Econst_variable_already_defined name -> eprintf "%aThe const variable %s is already defined.@." print_location loc name | Estatic_exp_expected -> eprintf "%aA static expression was expected.@." print_location loc | Eredefinition qualname -> eprintf "%aName %a was already defined.@." print_location loc print_qualname qualname | Elinear_type_no_memalloc -> eprintf "%aLinearity annotations cannot be used without memory allocation.@." print_location loc end; raise Errors.Error exception ScopingError of error let error kind = raise (ScopingError(kind)) end open Error let safe_add loc add n x = try ((add n x) : unit) with Modules.Already_defined -> message loc (Eredefinition n) (** {3 Qualify when ToQ and check when Q according to the global env } *) let _qualify_with_error s qfun cqfun q = match q with | ToQ name -> (try qfun name with Not_found -> error (Equal_unbound (s,name))) | Q q -> if cqfun q then q else error (Equal_notfound (s,q)) let qualify_value = _qualify_with_error "value" qualify_value check_value let qualify_type = _qualify_with_error "type" qualify_type check_type let qualify_constrs = _qualify_with_error "constructor" qualify_constrs check_constrs let qualify_field = _qualify_with_error "field" qualify_field check_field (** Qualify a var name as a constant variable, if not in local_const or global_const then raise Not_found *) let qualify_var_as_const local_const c = if NamesSet.mem c local_const then local_qn c else qualify_const c (** Qualify with [Names.local_qualname] when in local_const, otherwise qualify according to the global env *) let qualify_const local_const c = match c with | ToQ c -> (try qualify_var_as_const local_const c with Not_found -> error (Equal_unbound ("constant",c ))) | Q q -> if check_const q then q else raise Not_static module Rename = struct open Error include (Map.Make (struct type t = string let compare = String.compare end)) (** Rename a var *) let var loc env n = try fst (find n env) with Not_found -> message loc (Evar_unbound n) (** Rename a last *) let last loc env n = try let id, last = find n env in if not last then message loc (Enot_last n) else id with Not_found -> message loc (Evar_unbound n) (** Adds a name to the list of used names and idents. *) let add_used_name env n = add n (ident_of_name n, false) env (** Add a var *) let add_var loc env n = if mem n env then message loc (Evariable_already_defined n) else add n (ident_of_name n, false) env (** Add a last *) let add_last loc env n = if mem n env then message loc (Evariable_already_defined n) else add n (ident_of_name n, true) env (** Add a var dec *) let add env vd = let add = match vd.v_last with | Var -> add_var | Last _ -> add_last in add vd.v_loc env vd.v_name (** Append a list of var dec *) let append env vd_list = List.fold_left add env vd_list end let mk_app ?(params=[]) ?(unsafe=false) ?(inlined = false) op = { Heptagon.a_op = op; Heptagon.a_params = params; Heptagon.a_unsafe = unsafe; Heptagon.a_inlined = inlined } let mk_signature name ~extern ins outs stateful params constraints loc = { Heptagon.sig_name = name; Heptagon.sig_inputs = ins; Heptagon.sig_stateful = stateful; Heptagon.sig_outputs = outs; Heptagon.sig_params = params; Heptagon.sig_param_constraints = constraints; Heptagon.sig_external = extern; Heptagon.sig_loc = loc } (** Function to build the defined static parameters set *) let build_const loc vd_list = let _add_const_var loc c local_const = if NamesSet.mem c local_const then Error.message loc (Error.Econst_variable_already_defined c) else NamesSet.add c local_const in let build local_const vd = _add_const_var loc vd.v_name local_const in List.fold_left build NamesSet.empty vd_list (** { 3 Translate the AST into Heptagon. } *) let translate_iterator_type = function | Imap -> Heptagon.Imap | Imapi -> Heptagon.Imapi | Ifold -> Heptagon.Ifold | Ifoldi -> Heptagon.Ifoldi | Imapfold -> Heptagon.Imapfold let rec translate_static_exp se = try let se_d = translate_static_exp_desc se.se_loc se.se_desc in Types.mk_static_exp Types.Tinvalid ~loc:se.se_loc se_d with | ScopingError err -> message se.se_loc err and translate_static_exp_desc loc ed = let t = translate_static_exp in match ed with | Svar (Q q) -> Types.Svar q | Svar (ToQ _) -> assert false | Sint i -> Types.Sint i | Sfloat f -> Types.Sfloat f | Sbool b -> Types.Sbool b | Sstring s -> Types.Sstring s | Sconstructor c -> Types.Sconstructor (qualify_constrs c) | Sfield c -> Types.Sfield (qualify_field c) | Stuple se_list -> Types.Stuple (List.map t se_list) | Sarray_power (se,sn) -> Types.Sarray_power (t se, List.map t sn) | Sarray se_list -> Types.Sarray (List.map t se_list) | Srecord se_f_list -> let qualf (f, se) = (qualify_field f, t se) in Types.Srecord (List.map qualf se_f_list) | Sop (fn, se_list) -> Types.Sop (qualify_value fn, List.map t se_list) let expect_static_exp e = match e.e_desc with | Econst se -> translate_static_exp se | _ -> message e.e_loc Estatic_exp_expected let rec translate_type loc ty = try (match ty with | Tprod ty_list -> Types.Tprod(List.map (translate_type loc) ty_list) | Tid ln -> Types.Tid (qualify_type ln) | Tarray (ty, e) -> let ty = translate_type loc ty in Types.Tarray (ty, expect_static_exp e) | Tinvalid -> Types.Tinvalid ) with | ScopingError err -> message loc err let rec translate_some_clock loc env ck = match ck with | None -> Clocks.fresh_clock() | Some(ck) -> translate_clock loc env ck and translate_clock loc env ck = match ck with | Cbase -> Clocks.Cbase | Con(ck,c,x) -> Clocks.Con(translate_clock loc env ck, qualify_constrs c, Rename.var loc env x) let rec translate_ct loc env ct = match ct with | Ck ck -> Clocks.Ck (translate_clock loc env ck) | Cprod c_l -> Clocks.Cprod (List.map (translate_ct loc env) c_l) let rec translate_exp env e = try { Heptagon.e_desc = translate_desc e.e_loc env e.e_desc; Heptagon.e_ty = Types.invalid_type; Heptagon.e_linearity = Linearity.Ltop; Heptagon.e_level_ck = Clocks.Cbase; Heptagon.e_ct_annot = Misc.optional (translate_ct e.e_loc env) e.e_ct_annot; Heptagon.e_loc = e.e_loc } with ScopingError(error) -> message e.e_loc error and translate_desc loc env = function | Econst c -> Heptagon.Econst (translate_static_exp c) | Evar x -> Heptagon.Evar (Rename.var loc env x) | Elast x -> Heptagon.Elast (Rename.last loc env x) | Epre (None, e) -> Heptagon.Epre (None, translate_exp env e) | Epre (Some c, e) -> Heptagon.Epre (Some (expect_static_exp c), translate_exp env e) | Efby (e1, e2) -> Heptagon.Efby (translate_exp env e1, translate_exp env e2) | Estruct f_e_list -> let f_e_list = List.map (fun (f,e) -> qualify_field f, translate_exp env e) f_e_list in Heptagon.Estruct f_e_list | Eapp ({ a_op = op; a_params = params; a_inlined = inl }, e_list) -> let e_list = List.map (translate_exp env) e_list in let params = List.map (expect_static_exp) params in let app = mk_app ~params:params ~inlined:inl (translate_op op) in Heptagon.Eapp (app, e_list, None) | Eiterator (it, { a_op = op; a_params = params }, n_list, pe_list, e_list) -> let e_list = List.map (translate_exp env) e_list in let pe_list = List.map (translate_exp env) pe_list in let n_list = List.map expect_static_exp n_list in let params = List.map (expect_static_exp) params in let app = mk_app ~params:params (translate_op op) in Heptagon.Eiterator (translate_iterator_type it, app, n_list, pe_list, e_list, None) | Ewhen (e, c, x) -> let x = Rename.var loc env x in let e = translate_exp env e in let c = qualify_constrs c in Heptagon.Ewhen (e, c, x) | Emerge (x, c_e_list) -> let x = Rename.var loc env x in let c_e_list = let fun_c_e (c, e) = let e = translate_exp env e in let c = qualify_constrs c in (c, e) in List.map fun_c_e c_e_list in Heptagon.Emerge (x, c_e_list) | Esplit (x, e1) -> let x = translate_exp env (mk_exp (Evar x) loc) in let e1 = translate_exp env e1 in Heptagon.Esplit(x, e1) and translate_op = function | Earrow -> Heptagon.Earrow | Eifthenelse -> Heptagon.Eifthenelse | Efield -> Heptagon.Efield | Efield_update -> Heptagon.Efield_update | Etuple -> Heptagon.Etuple | Earray -> Heptagon.Earray | Eselect -> Heptagon.Eselect | Eupdate -> Heptagon.Eupdate | Earray_fill -> Heptagon.Earray_fill | Eselect_slice -> Heptagon.Eselect_slice | Econcat -> Heptagon.Econcat | Eselect_dyn -> Heptagon.Eselect_dyn | Eselect_trunc -> Heptagon.Eselect_trunc | Efun ln -> Heptagon.Efun (qualify_value ln) | Enode ln -> Heptagon.Enode (qualify_value ln) | Ereinit -> Heptagon.Ereinit and translate_pat loc env = function | Evarpat x -> Heptagon.Evarpat (Rename.var loc env x) | Etuplepat l -> Heptagon.Etuplepat (List.map (translate_pat loc env) l) let rec translate_eq env eq = let init = match eq.eq_desc with | Eeq(_, init, _) -> init | _ -> Linearity.Lno_init in { Heptagon.eq_desc = translate_eq_desc eq.eq_loc env eq.eq_desc ; Heptagon.eq_stateful = false; Heptagon.eq_inits = init; Heptagon.eq_loc = eq.eq_loc; } and translate_eq_desc loc env = function | Eswitch(e, switch_handlers) -> let sh = List.map (translate_switch_handler loc env) switch_handlers in Heptagon.Eswitch (translate_exp env e, sh) | Eeq(p, _, e) -> Heptagon.Eeq (translate_pat loc env p, translate_exp env e) | Epresent (present_handlers, b) -> Heptagon.Epresent (List.map (translate_present_handler env) present_handlers , fst (translate_block env b)) | Eautomaton state_handlers -> Heptagon.Eautomaton (List.map (translate_state_handler env) state_handlers) | Ereset (b, e) -> let b, _ = translate_block env b in Heptagon.Ereset (b, translate_exp env e) | Eblock b -> let b, _ = translate_block env b in Heptagon.Eblock b and translate_block env b = let env = Rename.append env b.b_local in { Heptagon.b_local = translate_vd_list env b.b_local; Heptagon.b_equs = List.map (translate_eq env) b.b_equs; Heptagon.b_defnames = Env.empty; Heptagon.b_stateful = false; Heptagon.b_loc = b.b_loc; }, env and translate_state_handler env sh = let b, env = translate_block env sh.s_block in { Heptagon.s_state = sh.s_state; Heptagon.s_block = b; Heptagon.s_until = List.map (translate_escape env) sh.s_until; Heptagon.s_unless = List.map (translate_escape env) sh.s_unless; } and translate_escape env esc = { Heptagon.e_cond = translate_exp env esc.e_cond; Heptagon.e_reset = esc.e_reset; Heptagon.e_next_state = esc.e_next_state } and translate_present_handler env ph = { Heptagon.p_cond = translate_exp env ph.p_cond; Heptagon.p_block = fst (translate_block env ph.p_block) } and translate_switch_handler loc env sh = try { Heptagon.w_name = qualify_constrs sh.w_name; Heptagon.w_block = fst (translate_block env sh.w_block) } with | ScopingError err -> message loc err and translate_var_dec env vd = (* env is initialized with the declared vars before their translation *) { Heptagon.v_ident = Rename.var vd.v_loc env vd.v_name; Heptagon.v_type = translate_type vd.v_loc vd.v_type; Heptagon.v_linearity = Linearity.check_linearity vd.v_linearity; Heptagon.v_last = translate_last vd.v_last; Heptagon.v_clock = translate_some_clock vd.v_loc env vd.v_clock; Heptagon.v_loc = vd.v_loc } (** [env] should contain the declared variables prior to this translation *) and translate_vd_list env = List.map (translate_var_dec env) and translate_last = function | Var -> Heptagon.Var | Last (None) -> Heptagon.Last None | Last (Some e) -> Heptagon.Last (Some (expect_static_exp e)) let translate_contract env opt_ct = match opt_ct with | None -> None, env | Some ct -> let env' = Rename.append env ct.c_controllables in let b, env = translate_block env ct.c_block in Some { Heptagon.c_assume = translate_exp env ct.c_assume; Heptagon.c_enforce = translate_exp env ct.c_enforce; Heptagon.c_assume_loc = translate_exp env ct.c_assume_loc; Heptagon.c_enforce_loc = translate_exp env ct.c_enforce_loc; Heptagon.c_controllables = translate_vd_list env' ct.c_controllables; Heptagon.c_block = b }, env' let params_of_var_decs env p_l = let pofvd env vd = let env = Rename.add_used_name env vd.v_name in Signature.mk_param vd.v_name (translate_type vd.v_loc vd.v_type), env in Misc.mapfold pofvd env p_l let translate_constrnt e = expect_static_exp e (* let args_of_var_decs = let arg_of_vd vd = if Linearity.is_linear vd.v_linearity && not !Compiler_options.do_mem_alloc then message vd.v_loc Elinear_type_no_memalloc else Signature.mk_arg ~linearity:vd.v_linearity (Some vd.v_name) (translate_type vd.v_loc vd.v_type) in List.map arg_of_vd *) let translate_node node = let n = current_qual node.n_name in Idents.enter_node n; let params, env = params_of_var_decs Rename.empty node.n_params in let constraints = List.map translate_constrnt node.n_constraints in let env = Rename.append env (node.n_input) in (* inputs should refer only to inputs *) let inputs = translate_vd_list env node.n_input in (* Inputs and outputs define the initial local env *) let env0 = Rename.append env node.n_output in let outputs = translate_vd_list env0 node.n_output in (* Enrich env with controllable variables (used in block) *) let contract, env = translate_contract env0 node.n_contract in let b, _ = translate_block env node.n_block in (* add the node signature to the environment *) let nnode = { Heptagon.n_name = n; Heptagon.n_stateful = node.n_stateful; Heptagon.n_unsafe = node.n_unsafe; Heptagon.n_input = inputs; Heptagon.n_output = outputs; Heptagon.n_contract = contract; Heptagon.n_block = b; Heptagon.n_loc = node.n_loc; Heptagon.n_params = params; Heptagon.n_param_constraints = constraints; } in safe_add node.n_loc add_value n (Hept_utils.signature_of_node nnode); nnode let translate_typedec ty = let n = current_qual ty.t_name in let tydesc = match ty.t_desc with | Type_abs -> safe_add ty.t_loc add_type n Signature.Tabstract; Heptagon.Type_abs | Type_alias t -> let t = translate_type ty.t_loc t in safe_add ty.t_loc add_type n (Signature.Talias t); Heptagon.Type_alias t | Type_enum(tag_list) -> let tag_list = List.map current_qual tag_list in List.iter (fun tag -> add_constrs tag n) tag_list; safe_add ty.t_loc add_type n (Signature.Tenum tag_list); Heptagon.Type_enum tag_list | Type_struct(field_ty_list) -> let translate_field_type (f,t) = let f = current_qual f in let t = translate_type ty.t_loc t in add_field f n; Signature.mk_field f t in let field_list = List.map translate_field_type field_ty_list in safe_add ty.t_loc add_type n (Signature.Tstruct field_list); Heptagon.Type_struct field_list in { Heptagon.t_name = n; Heptagon.t_desc = tydesc; Heptagon.t_loc = ty.t_loc } let translate_const_dec cd = let c_name = current_qual cd.c_name in let c_type = translate_type cd.c_loc cd.c_type in let c_value = expect_static_exp cd.c_value in replace_const c_name (Signature.mk_const_def c_type c_value); { Heptagon.c_name = c_name; Heptagon.c_type = c_type; Heptagon.c_value = c_value; Heptagon.c_loc = cd.c_loc; } let translate_program p = let translate_program_desc pd = match pd with | Ppragma _ -> Misc.unsupported "pragma in scoping" | Pconst c -> Heptagon.Pconst (translate_const_dec c) | Ptype t -> Heptagon.Ptype (translate_typedec t) | Pnode n -> Heptagon.Pnode (translate_node n) in let desc = List.map translate_program_desc p.p_desc in { Heptagon.p_modname = Names.modul_of_string p.p_modname; Heptagon.p_opened = p.p_opened; Heptagon.p_desc = desc; } let translate_signature s = let rec translate_some_clock ck = match ck with | None -> Signature.Cbase | Some ck -> translate_clock ck and translate_clock ck = match ck with | Cbase -> Signature.Cbase | Con(ck,c,x) -> Signature.Con(translate_clock ck, qualify_constrs c, x) and translate_arg a = Signature.mk_arg a.a_name (translate_type s.sig_loc a.a_type) a.a_linearity (translate_some_clock a.a_clock) in let n = current_qual s.sig_name in let i = List.map translate_arg s.sig_inputs in let o = List.map translate_arg s.sig_outputs in let p, _ = params_of_var_decs Rename.empty s.sig_params in let c = List.map translate_constrnt s.sig_param_constraints in let sig_node = Signature.mk_node ~extern:s.sig_external s.sig_loc i o s.sig_stateful s.sig_unsafe p in Check_signature.check_signature sig_node; safe_add s.sig_loc add_value n sig_node; mk_signature n i o s.sig_stateful p c s.sig_loc ~extern:s.sig_external let translate_interface_desc = function | Itypedef tydec -> Heptagon.Itypedef (translate_typedec tydec) | Iconstdef const_dec -> Heptagon.Iconstdef (translate_const_dec const_dec) | Isignature s -> Heptagon.Isignature (translate_signature s) let translate_interface i = let desc = List.map translate_interface_desc i.i_desc in { Heptagon.i_modname = Names.modul_of_string i.i_modname; Heptagon.i_opened = i.i_opened; Heptagon.i_desc = desc; }