727 lines
27 KiB
OCaml
727 lines
27 KiB
OCaml
(***********************************************************************)
|
|
(* *)
|
|
(* Heptagon *)
|
|
(* *)
|
|
(* Gwenael Delaval, LIG/INRIA, UJF *)
|
|
(* Leonard Gerard, Parkas, ENS *)
|
|
(* Adrien Guatto, Parkas, ENS *)
|
|
(* Cedric Pasteur, Parkas, ENS *)
|
|
(* Marc Pouzet, Parkas, ENS *)
|
|
(* Nicolas Berthier, SUMO, INRIA *)
|
|
(* *)
|
|
(* Copyright 2013 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 <http://www.gnu.org/licenses/> *)
|
|
(* *)
|
|
(***********************************************************************)
|
|
|
|
(** Translation from the source language to Controllable-Nbac
|
|
|
|
@author Nicolas Berthier *)
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
open Compiler_utils
|
|
open Ctrln_utils
|
|
open Signature
|
|
open Types
|
|
open Names
|
|
open Idents
|
|
open Minils
|
|
open CtrlNbac
|
|
open AST
|
|
|
|
let (&) f g = f g
|
|
|
|
exception Untranslatable of string (* XXX not catched yet! *)
|
|
|
|
(* --- *)
|
|
|
|
let tt = mk_bcst' true
|
|
let ff = mk_bcst' false
|
|
|
|
(* --- *)
|
|
|
|
(** Private record gathering temporary generation data *)
|
|
type 'f gen_data =
|
|
{
|
|
typdefs: 'f typdefs;
|
|
decls: 'f node_decls;
|
|
base: (var_ident * ty) SMap.t;
|
|
local: (var_ident * ty) SMap.t;
|
|
contrs: (var_ident * ty) SMap.t;
|
|
output: IdentSet.t;
|
|
init_cond: 'f bexp;
|
|
init_state: 'f bexp;
|
|
assertion: 'f bexp;
|
|
invariant: 'f bexp;
|
|
reachable: 'f bexp option;
|
|
attractive: 'f bexp option;
|
|
remaining_contrs: SSet.t; (* All controllable inputs that has not yet
|
|
been assigned to a U/C group. *)
|
|
local_contr_deps: SSet.t SMap.t; (* All variables that depend on a
|
|
controllable. *)
|
|
extra_inputs: SSet.t;
|
|
uc_groups: (SSet.t * SSet.t) list;
|
|
}
|
|
|
|
(* --- *)
|
|
|
|
let mk_gen_data typdefs decls input local output init_cond =
|
|
{
|
|
typdefs;
|
|
decls;
|
|
base = input;
|
|
local;
|
|
contrs = SMap.empty;
|
|
output;
|
|
remaining_contrs = SSet.empty;
|
|
local_contr_deps = SMap.empty;
|
|
extra_inputs = SSet.empty;
|
|
uc_groups = [];
|
|
init_cond;
|
|
init_state = tt;
|
|
assertion = tt;
|
|
invariant = tt;
|
|
reachable = None;
|
|
attractive = None;
|
|
}
|
|
|
|
(* --- *)
|
|
|
|
let translate_constr { name } = mk_label & mk_symb name (* XXX use qual name? *)
|
|
let translate_constrs cl = mk_etyp (List.map translate_constr cl)
|
|
|
|
(* --- *)
|
|
|
|
let rec translate_typ typ = match Modules.unalias_type typ with
|
|
| Tid ({ qual = Pervasives; name = "bool" }) -> `Bool
|
|
| Tid ({ qual = Pervasives; name = "int" }) -> `Int
|
|
| Tid ({ qual = Pervasives; name = "float" }) -> `Real
|
|
| Tid ({ name = tn } as t) -> (match Modules.find_type t with
|
|
| Tenum _ -> `Enum (mk_typname (mk_symb tn))
|
|
| Talias t -> translate_typ t (* XXX? *)
|
|
| _ -> raise & Untranslatable ("type "^ fullname t))
|
|
| Tprod _ -> raise & Untranslatable ("product type")
|
|
| Tarray _ -> raise & Untranslatable ("array type")
|
|
| Tinvalid -> failwith "Encountered an invalid type!"
|
|
|
|
let rec fintypp typ = match Modules.unalias_type typ with
|
|
| Tid ({ qual = Pervasives; name = "bool" }) -> true
|
|
| Tid t -> (match Modules.find_type t with
|
|
| Tenum _ -> true
|
|
| Talias t -> fintypp t (* XXX? *)
|
|
| _ -> false)
|
|
| _ -> false
|
|
|
|
let ref_of_ty ty = match translate_typ ty with
|
|
| `Bool -> mk_bref
|
|
| `Enum _ -> mk_eref
|
|
| `Int | `Real -> mk_nref
|
|
|
|
(* --- *)
|
|
|
|
let simplify_static_exp se = (Static.simplify QualEnv.empty se).se_desc
|
|
|
|
let translate_static_bexp se = match simplify_static_exp se with
|
|
| Sbool true | Sconstructor { qual = Pervasives; name = "true" } -> tt
|
|
| Sbool false | Sconstructor { qual = Pervasives; name = "false" } -> ff
|
|
| _ -> failwith (Format.asprintf "Boolean static expression expected! (found@ \
|
|
`%a')" Global_printer.print_static_exp se)
|
|
|
|
let translate_static_eexp se = match simplify_static_exp se with
|
|
| Sconstructor { qual = Pervasives; name = "true" as n }
|
|
| Sconstructor { qual = Pervasives; name = "false" as n } ->
|
|
failwith ("Enum static expression expected! (found `"^n^"')")
|
|
| Sconstructor c -> `Enum (translate_constr c)
|
|
| _ -> failwith (Format.asprintf "Enum static expression expected! (found@ \
|
|
`%a')" Global_printer.print_static_exp se)
|
|
|
|
let translate_static_nexp se = match simplify_static_exp se with
|
|
| Sint v -> `Int v
|
|
| Sfloat v -> `Real v
|
|
| Sop ({ qual = Pervasives; name="~-" },[{ se_desc = Sint v }]) -> `Int (-v)
|
|
| Sop ({ qual = Pervasives; name="~-." },[{ se_desc=Sfloat v }]) -> `Real (-.v)
|
|
| _ -> failwith (Format.asprintf "Numerical static expression expected! (found\
|
|
@ `%a')" Global_printer.print_static_exp se)
|
|
|
|
(* --- *)
|
|
|
|
let rec translate_ext_bexp ~pref : _ -> 'f bexp = function
|
|
| Wconst se -> translate_static_bexp se
|
|
| Wvar id -> mk_bref' (pref & mk_symb & name id)
|
|
| Wfield _ -> failwith "TODO Unsupported Boolean `field' expression!"
|
|
| Wwhen (ev, _, _) -> translate_ext_bexp ~pref ev.w_desc
|
|
| Wreinit _ -> failwith "TODO Unsupported Boolean `reinit' expression!"
|
|
|
|
and translate_ext_eexp ~pref : _ -> 'f eexp = function
|
|
| Wconst se -> translate_static_eexp se
|
|
| Wvar id -> mk_eref' (pref & mk_symb & name id)
|
|
| Wwhen (ev, _, _) -> translate_ext_eexp ~pref ev.w_desc
|
|
| _ -> failwith "TODO Unsupported Enum expression!"
|
|
|
|
and translate_ext_nexp ~pref : _ -> 'f nexp = function
|
|
| Wconst se -> translate_static_nexp se
|
|
| Wvar id -> mk_nref' (pref & mk_symb & name id)
|
|
| Wwhen (ev, _, _) -> translate_ext_nexp ~pref ev.w_desc
|
|
| _ -> failwith "TODO Unsupported Numerical expression!"
|
|
|
|
let translate_ext ~pref ext = match translate_typ ext.w_ty with
|
|
| `Bool -> `Bexp (translate_ext_bexp ~pref ext.w_desc)
|
|
| `Enum _ -> `Eexp (translate_ext_eexp ~pref ext.w_desc)
|
|
| `Int | `Real -> `Nexp (translate_ext_nexp ~pref ext.w_desc)
|
|
|
|
(* --- *)
|
|
|
|
let translate_app ~pref op el =
|
|
let pervasives = function
|
|
| "not", [e] -> mk_neg e
|
|
|("~-" | "~-."), [e] -> mk_opp e
|
|
| "or", e::l -> mk_disj e l
|
|
| "&", e::l -> mk_conj e l
|
|
| "xor", [e;f] -> mk_xor e f
|
|
| "=>", [e;f] -> mk_disj (mk_neg e) [f]
|
|
| "=", [e;f] -> mk_eq e f
|
|
| "<>", [e;f] -> mk_ne e f
|
|
|("<" | "<."), [e;f] -> mk_lt e f
|
|
|("<=" | "<=."), [e;f] -> mk_le e f
|
|
|(">" | ">."), [e;f] -> mk_gt e f
|
|
|(">=" | ">=."), [e;f] -> mk_ge e f
|
|
|("+" | "+."), e::f::l -> mk_sum e f l
|
|
|("-" | "-."), e::f::l -> mk_sub e f l
|
|
|("*" | "*."), e::f::l -> mk_mul e f l
|
|
|("/" | "/."), e::f::l -> mk_div e f l
|
|
| name, _ -> raise (Untranslatable name)
|
|
in
|
|
match op, List.map (translate_ext ~pref) el with
|
|
| Eequal, [e;f] -> mk_eq e f
|
|
| Efun { qual = Pervasives; name }, el -> pervasives (name, el)
|
|
| Eifthenelse, [c;t;e] -> mk_cond c t e
|
|
| _ -> failwith "Unsupported application!"
|
|
|
|
(** [translate_exp gd e] translates the {e memoryless} expression [e] into its
|
|
Controllable Nbac representation. *)
|
|
let rec translate_exp ~pref ({ e_desc = desc }) = (* XXX clock? *)
|
|
match desc with
|
|
| Eextvalue ext -> translate_ext ~pref ext
|
|
| Eapp ({ a_op }, el, _) -> translate_app ~pref a_op el
|
|
| Emerge (v, (_c, e) :: l) ->
|
|
let v = pref & mk_symb & name v in
|
|
List.fold_left
|
|
(fun x (c, e) -> mk_cond
|
|
(mk_eq (mk_eref v) (mk_ecst (translate_constr c)))
|
|
(translate_ext ~pref e) x)
|
|
(translate_ext ~pref e)
|
|
l
|
|
| Ewhen (exp, _, _) -> translate_exp ~pref exp
|
|
| Efby _ -> failwith "TODO: translate_exp (fby)"
|
|
| Estruct _ -> failwith "TODO: translate_exp (struct)"
|
|
| _ -> failwith "TODO: translate_exp"
|
|
|
|
(* --- *)
|
|
|
|
let rec translate_clk ~pref on off = function
|
|
| Clocks.Cbase | Clocks.Cvar { contents = Clocks.Cindex _ } -> on
|
|
| Clocks.Cvar { contents = Clocks.Clink ck } -> translate_clk ~pref on off ck
|
|
| Clocks.Con (ck, {name = cstr}, v) ->
|
|
let v = pref & mk_symb & name v in
|
|
let c = mk_eq (mk_eref v) (mk_ecst (mk_label (mk_symb cstr))) in
|
|
translate_clk ~pref (mk_cond c on off) off ck
|
|
|
|
(* --- *)
|
|
|
|
let acc_dependencies_on vars deps_on_vars i e = fold_exp_dependencies
|
|
(fun v s ->
|
|
if SSet.mem v vars then SSet.add v s
|
|
else try SSet.union s (SMap.find v deps_on_vars) with
|
|
| Not_found -> s)
|
|
e i
|
|
|
|
(* --- *)
|
|
|
|
let add_state_var' ~pref gd id ty exp init =
|
|
let v = pref & mk_symb & name id in
|
|
let typ = translate_typ ty in
|
|
let mk_init = match typ, init with
|
|
| _, None -> (fun b -> b)
|
|
| `Bool, Some i -> mk_and' (mk_beq' (mk_bref' v) (translate_static_bexp i))
|
|
| `Enum _, Some i -> mk_and' (mk_eeq' (mk_eref' v) (translate_static_eexp i))
|
|
| #ntyp, Some i -> mk_and' (mk_neq' (mk_nref' v) (translate_static_nexp i))
|
|
in
|
|
{ gd with
|
|
decls = SMap.add v (typ, `State (exp, None), None) gd.decls;
|
|
init_state = mk_init gd.init_state; }, v
|
|
|
|
let add_state_var ~pref gd id ty exp init =
|
|
let gd, v = add_state_var' ~pref gd id ty exp init in
|
|
{ gd with base = SMap.add v (id, ty) gd.base; }
|
|
|
|
|
|
(* TODO : add_local_var instead ? (NB : state var used for simulation) *)
|
|
let add_output_var ~pref gd id ty exp =
|
|
add_state_var' ~pref gd id ty exp None |> fst
|
|
|
|
let add_local_var ~pref gd id ty exp =
|
|
let v = pref & mk_symb & name id in
|
|
let typ = translate_typ ty in
|
|
let ldeps = fold_exp_dependencies (fun v acc ->
|
|
if SSet.mem v gd.remaining_contrs then SSet.add v acc
|
|
else try SSet.union acc (SMap.find v gd.local_contr_deps) with
|
|
| Not_found -> acc)
|
|
exp
|
|
SSet.empty
|
|
in
|
|
let local_contr_deps = SMap.add v ldeps gd.local_contr_deps in
|
|
{ gd with
|
|
decls = SMap.add v (typ, `Local (exp, None), None) gd.decls;
|
|
local_contr_deps; }
|
|
|
|
(* TODO : merge with definition above ? *)
|
|
let add_output_var ~pref gd id ty exp =
|
|
add_local_var ~pref gd id ty exp
|
|
|
|
let declare_additional_input ~pref gd id =
|
|
let l = mk_symb & name id in
|
|
try
|
|
let v = pref l in
|
|
let t = SMap.find l gd.local |> snd |> translate_typ in
|
|
{ gd with
|
|
decls = SMap.add v (t, `Input one, None) gd.decls;
|
|
extra_inputs = SSet.add v gd.extra_inputs; }
|
|
with
|
|
| Not_found -> (* output of the main node. *)
|
|
assert (IdentSet.mem id gd.output);
|
|
gd
|
|
|
|
(* --- *)
|
|
|
|
let close_uc_group gd defined_contrs =
|
|
let rem = SSet.diff gd.remaining_contrs defined_contrs in
|
|
let lcd = SMap.map (SSet.inter rem) gd.local_contr_deps in
|
|
let lcd = SMap.filter (fun _ d -> not (SSet.is_empty d)) lcd in
|
|
{ gd with
|
|
remaining_contrs = rem;
|
|
extra_inputs = SSet.empty;
|
|
local_contr_deps = lcd;
|
|
uc_groups = (gd.extra_inputs, defined_contrs) :: gd.uc_groups; }
|
|
|
|
(* --- *)
|
|
|
|
let pat_ids pat =
|
|
let rec acc_pat acc = function
|
|
| Evarpat id -> ((* pref & *)(* mk_symb & name *)id) :: acc
|
|
| Etuplepat pats -> List.fold_left acc_pat acc pats
|
|
in
|
|
acc_pat [] pat |> List.rev
|
|
|
|
let translate_abstract_app ~pref gd pat _f args =
|
|
let results = pat_ids (* ~pref *) pat in
|
|
let args = List.map (translate_ext ~pref) args in
|
|
let gd =
|
|
(* in case of dependencies on remainging controllable variables, switch to
|
|
next U/C group. *)
|
|
let depc = List.fold_left
|
|
(acc_dependencies_on gd.remaining_contrs gd.local_contr_deps)
|
|
SSet.empty args
|
|
in
|
|
if SSet.is_empty depc then gd else close_uc_group gd depc
|
|
in
|
|
(* declare extra inputs. *)
|
|
List.fold_left (declare_additional_input ~pref) gd results
|
|
|
|
(* --- *)
|
|
|
|
|
|
let translate_eq ~pref (gd, equs)
|
|
({ eq_lhs = pat;
|
|
eq_rhs = { e_desc = exp; e_ty = ty } as rhs;
|
|
eq_base_ck = clk } as eq)
|
|
=
|
|
let abstract_infinite_state = !Compiler_options.abstract_infinite in
|
|
match pat with
|
|
| Evarpat id ->
|
|
begin match exp with
|
|
| Efby _ when (abstract_infinite_state && not (fintypp ty)) ->
|
|
warn ~cond:(!Compiler_options.warn_abstractions)
|
|
"Abstracting@ %a@ state@ variable@ %s@ as@ non-controllable@ \
|
|
input." Global_printer.print_type ty (name id);
|
|
(declare_additional_input ~pref gd id, eq :: equs)
|
|
| Efby (init, ev) ->
|
|
let v = pref & mk_symb & name id in
|
|
let ev = translate_ext ~pref ev in
|
|
let ev = translate_clk ~pref ev (ref_of_ty ty v) clk in
|
|
(add_state_var ~pref gd id ty ev init, eq :: equs)
|
|
| Eapp ({ a_op = (Enode f | Efun f) }, args, None) (* TODO : handle resets *)
|
|
when f.qual <> Pervasives ->
|
|
(translate_abstract_app ~pref gd pat f args, eq :: equs)
|
|
| _ when IdentSet.mem id gd.output ->
|
|
let exp = translate_exp ~pref rhs in
|
|
(add_output_var ~pref gd id ty exp, eq :: equs)
|
|
| _ ->
|
|
let exp = translate_exp ~pref rhs in
|
|
(add_local_var ~pref gd id ty exp, eq :: equs)
|
|
end
|
|
| Etuplepat _ ->
|
|
begin match exp with
|
|
| Eapp ({ a_op = (Enode f | Efun f) }, args, None) (* TODO : handle resets *)
|
|
when f.qual <> Pervasives ->
|
|
(translate_abstract_app ~pref gd pat f args, eq :: equs)
|
|
| _ -> failwith "TODO: Minils.Etuplepat construct!"
|
|
end
|
|
|
|
let translate_eqs ~pref acc equs =
|
|
let gd, equs = List.fold_left (translate_eq ~pref) acc equs in
|
|
gd, List.rev equs
|
|
|
|
(* --- *)
|
|
|
|
let prefix_vars ~pref vars : symb -> symb =
|
|
let vars = List.fold_left begin fun acc { v_ident = id } ->
|
|
let v = mk_symb & name id in
|
|
SMap.add v (mk_symb (Symb.to_string v)) acc
|
|
end (SMap.empty) vars in
|
|
fun p -> pref (try SMap.find p vars with Not_found -> p)
|
|
|
|
let declare_contr (decls, contrs, vds)
|
|
({ v_ident = id; v_type = ty } as vd) rank =
|
|
let v = mk_symb & name id in
|
|
SMap.add v (translate_typ ty, `Contr (one, rank, None), None) decls,
|
|
SMap.add v (id, ty) contrs,
|
|
vd :: vds
|
|
|
|
let declare_contrs acc cl =
|
|
fst & List.fold_left
|
|
(fun (acc, rank) c -> (declare_contr acc c rank, AST.succ rank))
|
|
(acc, one) cl
|
|
|
|
(** Contract translation *)
|
|
let translate_contract ~pref gd
|
|
({ c_local; c_eq = equs;
|
|
c_assume = a; c_objectives = objs;
|
|
c_assume_loc = a'; c_enforce_loc = g';
|
|
c_controllables = cl } as contract)
|
|
=
|
|
let pref = prefix_vars ~pref c_local in
|
|
let decls, contrs, locals = declare_contrs (gd.decls, SMap.empty, []) cl in
|
|
let c = SMap.fold (fun v _ -> SSet.add v) contrs SSet.empty in
|
|
let gd = { gd with decls; contrs; remaining_contrs = c; } in
|
|
let gd, equs' = translate_eqs ~pref (gd, []) equs in
|
|
let ak = as_bexp & mk_and (translate_ext ~pref a) (translate_ext ~pref a')
|
|
and ok = as_bexp & translate_ext ~pref g' in
|
|
|
|
let gd = { gd with
|
|
assertion = mk_and' gd.assertion ak;
|
|
invariant = mk_and' gd.invariant ok; } in
|
|
|
|
let opt_and opt_e e' =
|
|
match opt_e with
|
|
None -> Some e'
|
|
| Some e -> Some (mk_and' e e') in
|
|
|
|
let add_objective gd o =
|
|
let e = as_bexp & translate_ext ~pref o.o_exp in
|
|
match o.o_kind with
|
|
| Obj_enforce -> { gd with invariant = mk_and' gd.invariant e; }
|
|
| Obj_reachable -> { gd with reachable = opt_and gd.reachable e; }
|
|
| Obj_attractive -> { gd with attractive = opt_and gd.attractive e; } in
|
|
|
|
let gd = List.fold_left add_objective gd objs in
|
|
|
|
(gd, { contract with c_eq = equs'; }, locals)
|
|
|
|
(* --- *)
|
|
|
|
let declare_output s { v_ident = id } =
|
|
IdentSet.add id s
|
|
|
|
let declare_input m { v_ident = id; v_type = typ } =
|
|
SMap.add (mk_symb & name id) (translate_typ typ, `Input one, None) m
|
|
|
|
let register_var_typ m { v_ident = id; v_type = typ } =
|
|
SMap.add (mk_symb & name id) (id, typ) m
|
|
|
|
(* --- *)
|
|
|
|
let finalize_uc_groups gd =
|
|
let gd = if SSet.is_empty gd.remaining_contrs then gd else
|
|
(* switch to last U/C group here, and declare controller call. *)
|
|
close_uc_group gd gd.remaining_contrs
|
|
in
|
|
if SSet.is_empty gd.extra_inputs then gd else
|
|
{ gd with
|
|
extra_inputs = SSet.empty;
|
|
uc_groups = (gd.extra_inputs, SSet.empty) :: gd.uc_groups; }
|
|
|
|
(* Note uc_groups are reversed in gd BEFORE the call to this function. *)
|
|
let assign_uc_groups gd =
|
|
let gd = finalize_uc_groups gd in
|
|
let uc_groups = List.rev gd.uc_groups in (* start from the first group *)
|
|
let decls, _ =
|
|
if uc_groups = [] then
|
|
gd.decls, one (* no group to change *)
|
|
else
|
|
List.fold_left begin fun (decls, group) (u, c) ->
|
|
let decls = SSet.fold (fun u decls -> match SMap.find u decls with
|
|
| (t, `Input _, l) ->
|
|
SMap.add u (t, `Input group, l) decls
|
|
| _ -> decls) u decls
|
|
in
|
|
let decls = SSet.fold (fun c decls -> match SMap.find c decls with
|
|
| (t, `Contr (_, r, l'), l) ->
|
|
SMap.add c (t, `Contr (group, r, l'), l) decls
|
|
| _ -> decls) c decls
|
|
in
|
|
decls, AST.succ group
|
|
end (gd.decls, AST.succ one) (List.tl uc_groups)
|
|
in
|
|
{ gd with decls; uc_groups }
|
|
|
|
(* --- *)
|
|
|
|
let scmp a b = String.compare (Symb.to_string a) (Symb.to_string b)
|
|
|
|
let var_exp v ty =
|
|
mk_extvalue ~ty ~clock:Clocks.Cbase ~linearity:Linearity.Ltop (Wvar v)
|
|
|
|
let decl_arg (v, t) =
|
|
mk_arg (Some (name v)) t Linearity.Ltop Signature.Cbase
|
|
|
|
let gen_ctrlf_calls ~requal_types gd node_name equs =
|
|
|
|
let equs, _, _ = List.fold_left begin fun (equs, ubase, num) (u, c) ->
|
|
|
|
(* Controllable inputs of the current U/C group *)
|
|
let c = SSet.elements c in
|
|
let c = List.sort scmp c in (* XXX now optional (x) *)
|
|
let o = List.map (fun v -> SMap.find v gd.contrs) c in
|
|
let os = List.map decl_arg o in
|
|
let ov, ot = List.split o in
|
|
let ov = Etuplepat (List.map (fun v -> Evarpat v) ov) in
|
|
|
|
(* Accumulate state variables and all non-controllable inputs from the
|
|
beginning, plus all controllables from previous U/C groups *)
|
|
let u = SSet.fold (fun v -> SMap.add v (SMap.find v gd.local)) u ubase in
|
|
let i = SMap.bindings u in
|
|
let i = List.sort (fun (a, _) (b, _) -> scmp b a) i in (* rev. i + ibid (x) *)
|
|
let is = List.rev_map (fun (_, p) -> decl_arg p) i in
|
|
let i = List.rev_map (fun (_, (v, t)) -> var_exp v t) i in
|
|
|
|
(* Build controller call *)
|
|
let func_name = controller_node ~num node_name in
|
|
let app = Eapp (mk_app (Efun func_name), i, None) in
|
|
let exp = mk_exp ~linearity:Linearity.Ltop Clocks.Cbase (Tprod ot) app in
|
|
let equ = mk_equation false ov exp in
|
|
|
|
let is, os = if requal_types then
|
|
(* Optional requalification of types declared in the exported module: *)
|
|
let requal_arg = function
|
|
| { a_type = Tid { qual; name } } as arg when qual = node_name.qual ->
|
|
{ arg with a_type = Tid { qual = func_name.qual; name } }
|
|
| a -> a
|
|
in
|
|
List.map requal_arg is, List.map requal_arg os
|
|
else
|
|
is, os
|
|
in
|
|
|
|
(* Declare new node *)
|
|
let node_sig = Signature.mk_node Location.no_location ~extern:false is os
|
|
false false [] in
|
|
Modules.add_value func_name node_sig;
|
|
|
|
(* Augment base non-controllble inputs with current controllables *)
|
|
let u = List.fold_left (fun u v -> SMap.add v (SMap.find v gd.contrs) u) u c in
|
|
|
|
(equ :: equs, u, num + 1)
|
|
end (equs, gd.base, 0) gd.uc_groups in
|
|
|
|
equs
|
|
|
|
(* --- *)
|
|
|
|
(** Node translation. Note the given node is not expored if it does not comprize a
|
|
contract. *)
|
|
let translate_node ~requal_types typdefs = function
|
|
| ({ n_contract = None } as node) -> node, None
|
|
| ({ n_name; n_params } as node) when n_params <> [] ->
|
|
warn ~cond:(!Compiler_options.warn_untranslatable)
|
|
"Unsupported@ translation@ of@ parametric@ node@ `%s'@ with@ \
|
|
contract@ into@ Controllable-Nbac!" (Names.fullname n_name);
|
|
node, None
|
|
| ({ n_name; n_input; n_output; n_local; n_equs;
|
|
n_contract = Some contr } as node) ->
|
|
|
|
enter_node n_name; (* for optional sink symbol generation. *)
|
|
|
|
let pref p = p in
|
|
let local = List.fold_left register_var_typ SMap.empty n_local in
|
|
let input = List.fold_left register_var_typ SMap.empty n_input in
|
|
let output = List.fold_left declare_output IdentSet.empty n_output in
|
|
let decls = List.fold_left declare_input SMap.empty n_input in
|
|
|
|
let init_cond_var = mk_symb init_cond_str in
|
|
let init_cond = mk_bref' init_cond_var in (* XXX what about gd.base? *)
|
|
let init_cond_spec = (`Bool, `State (`Bexp ff, None), None) in
|
|
let decls = SMap.add init_cond_var init_cond_spec decls in
|
|
|
|
let gd = mk_gen_data typdefs decls input local output init_cond in
|
|
let gd, contract, locals' = translate_contract ~pref gd contr in
|
|
let gd, equs' = translate_eqs ~pref (gd, []) n_equs in
|
|
let gd = assign_uc_groups gd in
|
|
let equs' = gen_ctrlf_calls ~requal_types gd n_name equs' in
|
|
|
|
(* Sink state *)
|
|
let sink_state_var = mk_symb sink_state_str in
|
|
let sink_state = mk_bref' sink_state_var in
|
|
let sink_state_spec = (`Bool, `State (`Bexp gd.invariant, None), None) in
|
|
let gd = {gd with decls = SMap.add sink_state_var sink_state_spec gd.decls } in
|
|
|
|
let ctrln_node_desc =
|
|
{ cn_typs = typdefs;
|
|
cn_decls = gd.decls;
|
|
cn_init = mk_and' (mk_and' gd.init_state init_cond) sink_state;
|
|
cn_assertion = (* mk_or' init_cond *)gd.assertion;
|
|
cn_invariant = Some (mk_or' init_cond sink_state);
|
|
cn_reachable = gd.reachable;
|
|
cn_attractive = gd.attractive; }
|
|
and node =
|
|
{ node with
|
|
n_equs = equs';
|
|
n_local = List.rev_append locals' n_local;
|
|
n_contract = Some contract; }
|
|
in
|
|
|
|
(node, Some (n_name, (`Desc ctrln_node_desc : 'f AST.node)))
|
|
|
|
(* --- *)
|
|
|
|
(** Moves all type declarations into the given module, declare aliases for them
|
|
(in cases). Also requalifies constructor names in the program, as well as
|
|
types of expressions to avoid some errors in code generation later on. *)
|
|
let requal_declared_types prog =
|
|
|
|
let cmodul = types_modul prog.p_modname in
|
|
let requal m = m = prog.p_modname in
|
|
|
|
let requal_constr ({ qual; name } as cstr) =
|
|
if requal qual then { qual = cmodul; name } else cstr in
|
|
|
|
let requal_type = function (* requalify enum and alias types. *)
|
|
| Tid ({ qual; name } as ty) as t when requal qual ->
|
|
(match Modules.find_type ty with
|
|
| Tenum _ | Talias _ -> Tid { qual = cmodul; name }
|
|
| _ -> t)
|
|
| t -> t
|
|
in
|
|
|
|
let requal_type_dec = function
|
|
| { t_name = tn; t_desc } as t when requal tn.qual ->
|
|
let new_type = match t_desc with
|
|
| Type_enum cl -> Signature.Tenum (List.map requal_constr cl)
|
|
| Type_alias t -> Signature.Talias (requal_type t)
|
|
| _ -> raise Errors.Fallback
|
|
in
|
|
let tn' = { tn with qual = cmodul } in
|
|
let t = { t with t_name = tn; t_desc = Type_alias (Tid tn') } in
|
|
Modules.replace_type tn (Signature.Talias (Tid tn'));
|
|
Modules.add_type tn' new_type;
|
|
t
|
|
| _ -> raise Errors.Fallback
|
|
in
|
|
|
|
let open Mls_mapfold in
|
|
let open Global_mapfold in
|
|
let funcs = { Mls_mapfold.defaults with
|
|
|
|
type_dec = (fun _ () td -> requal_type_dec td, ());
|
|
|
|
edesc = (fun funs () -> function
|
|
| Ewhen (e, c, x) ->
|
|
Ewhen (exp_it funs () e |> fst, requal_constr c,
|
|
var_ident_it funs.global_funs () x |> fst), ()
|
|
| Emerge (i, l) ->
|
|
Emerge (var_ident_it funs.global_funs () i |> fst,
|
|
List.map (fun (c, x) -> requal_constr c,
|
|
extvalue_it funs () x |> fst) l), ()
|
|
| _ -> raise Errors.Fallback);
|
|
|
|
extvalue_desc = (fun funs () -> function
|
|
| Wwhen (w, c, v) ->
|
|
Wwhen (extvalue_it funs () w |> fst, requal_constr c,
|
|
var_ident_it funs.global_funs () v |> fst), ()
|
|
| _ -> raise Errors.Fallback);
|
|
|
|
global_funs = { Global_mapfold.defaults with
|
|
|
|
ty = (fun _ () ty -> requal_type ty, ());
|
|
|
|
ck = (fun funs () -> function
|
|
| Clocks.Con (ck, c, i) ->
|
|
Clocks.Con (ck_it funs () ck |> fst, requal_constr c,
|
|
var_ident_it funs () i |> fst), ()
|
|
| _ -> raise Errors.Fallback);
|
|
|
|
static_exp_desc = (fun _ () -> function
|
|
| Sconstructor c -> Sconstructor (requal_constr c), ()
|
|
| _ -> raise Errors.Fallback);
|
|
|
|
};
|
|
} in
|
|
|
|
program funcs () prog |> fst
|
|
|
|
(* --- *)
|
|
|
|
(** [gen p] translates all type definitions, plus the nodes comprizing a
|
|
contract, into Controllable-Nbac.
|
|
|
|
@return a Controllable-Nbac program comprizing one process for each node
|
|
necessitating controller synthesis), and a new Minils program, in which
|
|
those nodes have been transformed so that they "call" their respective
|
|
controller.
|
|
|
|
XXX The [requalify_declared_types] argument is here to avoid cyclic
|
|
dependencies between modules due to type declarations. Yet, a better idea
|
|
might be to integrate the generated controllers into the original program
|
|
later on. *)
|
|
let gen ?(requalify_declared_types = false) ({ p_desc } as p) =
|
|
|
|
let requal_types = requalify_declared_types in
|
|
|
|
let _cnp_typs, nodes, descs =
|
|
List.fold_left begin fun (typdefs, nodes, descs) -> function
|
|
| Pnode n ->
|
|
begin match translate_node ~requal_types typdefs n with
|
|
| node, Some n -> (typdefs, n :: nodes, Pnode node :: descs)
|
|
| node, None -> (typdefs, nodes, Pnode node :: descs)
|
|
end
|
|
| Ptype { t_name = ({ name }); t_desc = Type_enum cl } as ty ->
|
|
let tn = mk_typname & mk_symb name and typ = translate_constrs cl in
|
|
let typdefs = declare_typ tn typ typdefs in
|
|
(typdefs, nodes, ty :: descs)
|
|
| p -> (typdefs, nodes, p :: descs)
|
|
end (empty_typdefs, [], []) p_desc
|
|
in
|
|
|
|
let cnp_nodes = List.rev nodes
|
|
and p_desc = List.rev descs in
|
|
let prog = { p with p_desc } in
|
|
let prog =
|
|
if requalify_declared_types
|
|
then requal_declared_types prog
|
|
else prog
|
|
in
|
|
cnp_nodes, prog
|