open Ast
open Util
open Ast_util
open Formula
open Typing
open Interpret (* used for constant evaluation ! *)
(* Transform SCADE program to logical formula. *)
(* node * prefix * equations *)
type scope = id * id * eqn ext list
type transform_data = {
rp : rooted_prog;
consts : I.value VarMap.t;
(* future : the automata state *)
}
(* Numerical types / Enumerated types *)
type ne_expr =
| EE of enum_expr
| NE of num_expr
(* f_of_neexpr :
transform_data -> (string, string) -> (ne_expr list -> bool_expr) -> expr -> bool_expr
*)
let rec f_of_neexpr td (node, prefix) where expr =
let sub = f_of_neexpr td (node, prefix) in
let le = loc_error (snd expr) in
match fst expr with
(* ident *)
| AST_identifier(id, _) ->
let qid = node^"/"^id in
begin match type_var td.rp node id with
| TInt | TReal -> where [NE (NIdent qid)]
| TEnum _ -> where [EE (EIdent qid)]
end
| AST_idconst(id, _) ->
begin let x = VarMap.find ("cst/"^id) td.consts in
try where [NE (NIntConst (I.as_int x))]
with _ -> try where [NE (NRealConst (I.as_real x))]
with _ -> try where [EE (EItem (if I.as_bool x then bool_true else bool_false))]
with _ -> le error "Invalid data for supposedly numerical/boolean constant."
end
(* numerical *)
| AST_int_const(i, _) -> where [NE(NIntConst(int_of_string i))]
| AST_real_const(r, _) -> where [NE(NRealConst(float_of_string r))]
| AST_bool_const b -> where [EE(EItem (if b then bool_true else bool_false))]
| AST_unary(op, e) ->
sub (function
| [NE x] -> where [NE(NUnary(op, x))]
| _ -> le invalid_arity "Unary operator") e
| AST_binary(op, a, b) ->
sub (function
| [NE x] ->
sub (function
| [NE y] -> where [NE(NBinary(op, x, y))]
| _ -> le invalid_arity "binary operator") b
| _ -> le invalid_arity "binary operator") a
(* temporal *)
| AST_pre(expr, id) ->
let typ = type_expr td.rp node expr in
where
(List.mapi
(fun i t -> let x = id^"."^(string_of_int i) in
match t with
| TInt | TReal -> NE(NIdent x)
| TEnum _ -> EE(EIdent x))
typ)
| AST_arrow(a, b) ->
if td.rp.init_scope (node^"/")
then
f_or
(f_and (f_e_eq (EIdent (node^"/"^prefix^"init")) (EItem bool_true))
(sub where a))
(f_and (f_e_eq (EIdent (node^"/"^prefix^"init")) (EItem bool_false))
(sub where b))
else
f_or
(f_and (BRel(AST_EQ, NIdent(node^"/"^prefix^"time"), NIntConst 0))
(sub where a))
(f_and (BRel(AST_GE, NIdent(node^"/"^prefix^"time"), NIntConst 1))
(sub where b))
(* other *)
| AST_if(c, a, b) ->
f_or
(f_and (f_of_expr td (node, prefix) c) (sub where a))
(f_and (BNot(f_of_expr td (node, prefix) c)) (sub where b))
| AST_instance ((f, _), args, nid) ->
let (n, _) = find_node_decl td.rp.p f in
where
(List.map
(fun (_, id, t) -> let x = node^"/"^nid^"/"^id in
match t with
| AST_TINT | AST_TREAL -> NE(NIdent x)
| _ -> EE(EIdent x))
n.ret)
| AST_tuple l ->
let rec rl l x = match l with
| [] -> where x
| p::q ->
sub (fun y -> rl q (x@y)) p
in rl l []
(* boolean values treated as enumerated types *)
| _ when type_expr td.rp node expr = [bool_type] ->
f_or
(f_and (f_of_expr td (node, prefix) expr) (where [EE (EItem bool_true)]))
(f_and (f_of_expr td (node, prefix) (AST_not(expr), snd expr))
(where [EE (EItem bool_false)]))
| _ -> le type_error "Expected numerical/enumerated value"
(*
f_of_expr :
transform_data -> (string, string) -> expr -> bool_expr
f_of_bexpr :
transform_data -> (string, string) -> (bool_expr -> bool_expr) -> expr -> bool_expr
*)
and f_of_bexpr td (node, prefix) where expr =
let sub = f_of_bexpr td (node, prefix) in
match fst expr with
| AST_bool_const b -> where (BConst b)
| AST_binary_bool(AST_AND, a, b) -> f_and (sub where a) (sub where b)
| AST_binary_bool(AST_OR, a, b) -> f_or (sub where a) (sub where b)
| AST_not(a) -> BNot(sub where a)
| AST_binary_rel(rel, a, b) ->
where
(f_of_neexpr td (node, prefix)
(function
| [NE x; NE y] -> BRel(rel, x, y)
| [EE x; EE y] ->
let eop = match rel with
| AST_EQ -> E_EQ
| AST_NE -> E_NE
| _ -> type_error "Invalid operator on enumerated values."
in f_e_op eop x y
| _ -> invalid_arity "Binary operator")
(AST_tuple [a; b], snd expr))
(* Temporal *)
| AST_arrow(a, b) ->
f_or
(f_and (BRel(AST_EQ, NIdent(node^"/"^prefix^"time"), NIntConst 0))
(sub where a))
(f_and (BRel(AST_GE, NIdent(node^"/"^prefix^"time"), NIntConst 1))
(sub where b))
(* Enumerations... *)
| _ when type_expr td.rp node expr = [bool_type] ->
let ff = function
| [EE x] ->
f_or
(f_and (f_e_eq x (EItem bool_true)) (where (BConst true)))
(f_and (f_e_eq x (EItem bool_false)) (where (BConst false)))
| _ -> assert false
in
f_of_neexpr td (node, prefix) ff expr
| _ -> type_error "Expected boolean value."
and f_of_expr td (node, prefix) expr =
f_of_bexpr td (node, prefix) (fun x -> x) expr
(*
Translate program into one big formula
*)
let rec f_of_scope active td (node, prefix, eqs) assume_guarantees =
let expr_eq e =
let instance_eq (_, id, eqs, args) =
let eq = f_of_scope active td (node^"/"^id, "", eqs) assume_guarantees in
if active then
let arg_eq ((_,argname,_), expr) =
f_of_neexpr td (node, prefix) (function
| [NE v] -> BRel(AST_EQ, NIdent(node^"/"^id^"/"^argname), v)
| [EE v] -> f_e_eq (EIdent(node^"/"^id^"/"^argname)) v
| _ -> invalid_arity "in argument")
expr
in f_and eq (f_and_list (List.map arg_eq args))
else
eq
in
let eq_i = f_and_list (List.map instance_eq (extract_instances td.rp.p e)) in
let pre_expr (id, expr) =
if active then
f_of_neexpr td (node, prefix) (fun elist ->
list_fold_op f_and
(List.mapi
(fun i v -> let x = ("N"^id^"."^(string_of_int i)) in
match v with
| NE v -> BRel(AST_EQ, NIdent x, v)
| EE v -> f_e_eq (EIdent x) v)
elist))
expr
else
let typ = type_expr td.rp node expr in
list_fold_op f_and
(List.mapi
(fun i t -> let x = string_of_int i in
match t with
| TInt | TReal -> BRel(AST_EQ, NIdent("N"^id^"."^x), NIdent (id^"."^x))
| TEnum _ -> f_e_eq (EIdent("N"^id^"."^x)) (EIdent (id^"."^x)))
typ)
in
let eq_p = f_and_list (List.map pre_expr (extract_pre e)) in
f_and eq_i eq_p
in
let do_eq eq = match fst eq with
| AST_assign(ids, e) ->
let assign_eq =
if active then
let apply_f vs =
let rels =
List.map2 (fun (id, _) -> function
| NE v -> BRel(AST_EQ, NIdent (node^"/"^id), v)
| EE v -> f_e_eq (EIdent (node^"/"^id)) v)
ids vs
in
f_and_list rels
in
f_of_neexpr td (node, prefix) apply_f e
else
BConst true
in
f_and (expr_eq e) assign_eq
| AST_assume (_, e) ->
let assume_eq =
if active then
f_of_expr td (node, prefix) e
else
BConst true
in
f_and (expr_eq e) assume_eq
| AST_guarantee ((id, _), e) ->
let gn = node^"/g_"^id in
let guarantee_eq =
if active && assume_guarantees then
f_and (f_of_expr td (node, prefix) e)
(BEnumCons(E_EQ, gn, EItem bool_true))
else
f_or
(f_and (f_of_expr td (node, prefix) e)
(BEnumCons(E_EQ, gn, EItem bool_true)))
(f_and (BNot (f_of_expr td (node, prefix) e))
(BEnumCons(E_EQ, gn, EItem bool_false)))
in
f_and (expr_eq e) guarantee_eq
| AST_activate (b, _) ->
let rec cond_eq = function
| AST_activate_body b -> BConst true
| AST_activate_if(c, a, b) ->
f_and (expr_eq c)
(f_and (cond_eq a) (cond_eq b))
in
let rec do_tree_act = function
| AST_activate_body b ->
f_of_scope true td (node, b.act_id^".", b.body) assume_guarantees
| AST_activate_if(c, a, b) ->
f_or
(f_and (f_of_expr td (node, prefix) c)
(f_and (do_tree_act a) (do_tree_inact b)))
(f_and (BNot(f_of_expr td (node, prefix) c))
(f_and (do_tree_act b) (do_tree_inact a)))
and do_tree_inact = function
| AST_activate_body b ->
f_of_scope false td (node, b.act_id^".", b.body) assume_guarantees
| AST_activate_if(_, a, b) ->
f_and (do_tree_inact a) (do_tree_inact b)
in
f_and (cond_eq b) (if active then do_tree_act b else do_tree_inact b)
| AST_automaton (aid, states, _) ->
let stv = node^"/"^aid^".state" in
let nstv = "N"^node^"/"^aid^".state" in
let st_eq_inact (st, _) =
f_and
(f_of_scope false td
(node, aid^"."^st.st_name^".", st.body) assume_guarantees)
(f_and_list (List.map (fun (c, _, _) -> expr_eq c) st.until))
in
if active then
let st_eq_act (st, l) =
let act_eq =
let st_eq = f_of_scope true td
(node, aid^"."^st.st_name^".", st.body) assume_guarantees in
let rec aux = function
| [] -> BEnumCons(E_EQ, nstv, EItem st.st_name)
| (c, (target, l), rst)::q ->
if rst then loc_error l error "Resetting transitions not supported.";
f_or
(f_and (f_of_expr td (node, prefix) c)
(BEnumCons(E_EQ, nstv, EItem target)))
(f_and (BNot (f_of_expr td (node, prefix) c)) (aux q))
in f_and st_eq (aux st.until)
in
f_or
(f_and (BEnumCons(E_EQ, stv, EItem st.st_name)) act_eq)
(f_and (BEnumCons(E_NE, stv, EItem st.st_name))
(st_eq_inact (st, l)))
in
f_and_list (List.map st_eq_act states)
else
f_and_list (List.map st_eq_inact states)
in
let time_incr_eq =
if active then
f_and
(if not (td.rp.no_time_scope (node^"/"))
then BRel(AST_EQ, NIdent("N"^node^"/"^prefix^"time"),
NBinary(AST_PLUS, NIntConst 1, NIdent(node^"/"^prefix^"time")))
else BConst true)
(if td.rp.init_scope (node^"/")
then f_e_eq (EIdent("N"^node^"/"^prefix^"init")) (EItem bool_false)
else BConst true)
else
f_and
(if not (td.rp.no_time_scope (node^"/"))
then BRel(AST_EQ,
NIdent("N"^node^"/"^prefix^"time"),
NIdent(node^"/"^prefix^"time"))
else BConst true)
(if td.rp.init_scope (node^"/")
then f_e_eq (EIdent("N"^node^"/"^prefix^"init"))
(EIdent (node^"/"^prefix^"init"))
else BConst true)
in
List.fold_left f_and
time_incr_eq
(List.map do_eq eqs)
and f_of_prog rp assume_guarantees =
let td = {
rp = rp;
consts = I.consts rp;
} in
f_of_scope true td td.rp.root_scope assume_guarantees
(*
Translate init state into a formula
*)
let rec init_f_of_scope rp (node, prefix, eqs) =
let expr_eq e =
let instance_eq (_, id, eqs, args) =
init_f_of_scope rp (node^"/"^id, "", eqs)
in
List.fold_left (fun x i -> f_and (instance_eq i) x)
(BConst true) (extract_instances rp.p e)
in
let do_eq eq = match fst eq with
| AST_assign(_, e) | AST_assume(_, e) | AST_guarantee(_, e) ->
expr_eq e
| AST_activate (b, _) ->
let rec cond_eq = function
| AST_activate_body b ->
init_f_of_scope rp (node, b.act_id^".", b.body)
| AST_activate_if(c, a, b) ->
f_and (expr_eq c)
(f_and (cond_eq a) (cond_eq b))
in
cond_eq b
| AST_automaton (aid, states, _) ->
let (st, _) = List.find (fun (st, _) -> st.initial) states in
let init_eq = BEnumCons(E_EQ, node^"/"^aid^".state", EItem st.st_name) in
let state_eq (st, _) =
let sc_f =
init_f_of_scope rp (node, aid^"."^st.st_name^".", st.body)
in List.fold_left (fun f (c, _, _) -> f_and f (expr_eq c)) sc_f st.until
in
List.fold_left f_and init_eq
(List.map state_eq states)
in
let time_eq =
f_and
(if not (rp.no_time_scope (node^"/"))
then
BRel(AST_EQ,
NIdent(node^"/"^prefix^"time"),
NIntConst 0)
else BConst true)
(if rp.init_scope (node^"/")
then
f_e_eq (EIdent(node^"/"^prefix^"init")) (EItem bool_true)
else BConst true)
in
List.fold_left f_and time_eq (List.map do_eq eqs)
and init_f_of_prog rp =
init_f_of_scope rp rp.root_scope
(*
Get expressions for guarantee violation
*)
let rec g_of_scope td (node, prefix, eqs) cond =
let expr_g e =
let instance_g (_, id, eqs, args) =
g_of_scope td (node^"/"^id, "", eqs) cond
in
List.fold_left (fun x i -> (instance_g i) @ x)
[] (extract_instances td.rp.p e)
in
let do_eq eq = match fst eq with
| AST_assign(_, e) | AST_assume(_, e) ->
expr_g e
| AST_guarantee((id, _), e) ->
(id, f_and cond (f_of_expr td (node, prefix) (AST_not(e), snd e)))
:: (expr_g e)
| AST_activate (b, _) ->
let rec cond_g cond = function
| AST_activate_body b ->
g_of_scope td (node, b.act_id^".", b.body) cond
| AST_activate_if(c, a, b) ->
(cond_g (f_and cond (f_of_expr td (node, prefix) c)) a) @
(cond_g (f_and cond (f_of_expr td (node, prefix) (AST_not(c), snd c))) b) @
(expr_g c)
in
cond_g cond b
| AST_automaton (aid, states, _) ->
let st_g (st, _) =
g_of_scope td (node, aid^"."^st.st_name^".", st.body)
(f_and cond (BEnumCons(E_EQ, node^"/"^aid^".state", EItem st.st_name)))
in
List.flatten (List.map st_g states)
in
List.flatten (List.map do_eq eqs)
and guarantees_of_prog rp =
let td = {
rp = rp;
consts = I.consts rp;
} in
g_of_scope td rp.root_scope (BConst true)