open Ast
open Util
open Ast_util
open Interface
module I : INTERPRET = struct
(* Values for variables *)
exception Bad_datatype
type value =
| VInt of int
| VBool of bool
| VReal of float
| VState of id
| VPrevious of value list
| VBusy (* intermediate value : calculating ! *)
| VCalc of (unit -> value)
let int_value i = VInt i
let bool_value b = VBool b
let real_value r = VReal r
let as_int = function
| VInt i -> i
| _ -> raise Bad_datatype
let as_bool = function
| VBool b -> b
| _ -> raise Bad_datatype
let as_real = function
| VReal r -> r
| _ -> raise Bad_datatype
let rec str_repr_of_val = function
| VInt i -> string_of_int i
| VReal r -> string_of_float r
| VBool b -> if b then "true" else "false"
| VState s -> "state " ^ s
| VPrevious p ->
"[" ^
List.fold_left (fun s v ->
(if s = "" then "" else s ^ ", ") ^ str_repr_of_val v)
"" p
^ "]"
| VBusy -> "#"
| VCalc _ -> "()"
(* States of the interpret *)
(* path to node * subscope prefix in node * equations in scope *)
type scope = id * id * eqn ext list
type state = {
p : prog;
root_scope : scope;
outputs : id list;
save : value VarMap.t;
}
let print_state fmt st =
VarMap.iter
(fun id v -> Format.fprintf fmt "%s = %s@." id (str_repr_of_val v))
st.save
type io = (id * value) list
(* Internal type env : contains the environment for state calculation *)
type env = {
st : state;
vars : (id, value) Hashtbl.t;
}
(*
get_var : env -> id -> id -> value
Gets the value of a variable relative to a node path.
*)
let get_var env node id =
let p = node^"/"^id in
try match Hashtbl.find env.vars p with
| VCalc f ->
Hashtbl.replace env.vars p VBusy;
f ()
| VBusy -> combinatorial_cycle p
| x -> x
with
| Not_found -> no_variable p
(*
eval_expr : env -> (id * id) -> expr ext -> value list
*)
let rec eval_expr env (node, prefix) exp =
let sub_eval = eval_expr env (node, prefix) in
match fst exp with
| AST_identifier (id, _) ->
[loc_error (snd exp) (get_var env node) id]
| AST_idconst (id, _) ->
begin try [VarMap.find ("cst/"^id) env.st.save]
with Not_found -> loc_error (snd exp) no_variable id end
(* on numerical values *)
| AST_int_const (i, _) -> [VInt (int_of_string i)]
| AST_real_const (r, _) -> [VReal (float_of_string r)]
| AST_unary(AST_UPLUS, e) -> sub_eval e
| AST_unary(AST_UMINUS, e) ->
begin match sub_eval e with
| [VInt x] -> [VInt (-x)]
| [VReal x] -> [VReal(-. x)]
| _ -> type_error "Unary on non-numerical."
end
| AST_binary(op, e1, e2) ->
let iop, fop = match op with
| AST_PLUS -> (+), (+.)
| AST_MINUS -> (-), (-.)
| AST_MUL -> ( * ), ( *. )
| AST_DIV -> (/), (/.)
| AST_MOD -> (mod), mod_float
in
begin match sub_eval e1, sub_eval e2 with
| [VInt a], [VInt b] -> [VInt (iop a b)]
| [VReal a], [VReal b] -> [VReal(fop a b)]
| _ -> type_error "Invalid arguments for numerical binary."
end
(* on boolean values *)
| AST_bool_const b -> [VBool b]
| AST_binary_rel(op, e1, e2) ->
let r = match op with
| AST_EQ -> (=) | AST_NE -> (<>)
| AST_LT -> (<) | AST_LE -> (<=)
| AST_GT -> (>) | AST_GE -> (>=)
in
begin match sub_eval e1, sub_eval e2 with
| [VInt a], [VInt b] -> [VBool (r a b)]
| [VReal a], [VReal b] -> [VBool (r a b)]
| [VBool a], [VBool b] -> [VBool (r a b)]
| _ -> type_error "Invalid arguments for binary relation."
end
| AST_binary_bool(op, e1, e2) ->
let r = match op with
| AST_AND -> (&&) | AST_OR -> (||)
in
begin match sub_eval e1, sub_eval e2 with
| [VBool a], [VBool b] -> [VBool (r a b)]
| _ -> type_error "Invalid arguments for boolean relation."
end
| AST_not(e) ->
begin match sub_eval e with
| [VBool b] -> [VBool (not b)]
| _ -> type_error "Invalid arguments for boolean negation."
end
(* temporal primitives *)
| AST_pre(exp, n) ->
begin try match VarMap.find (node^"/"^prefix^n) env.st.save with
| VPrevious x -> x
| _ -> assert false
with Not_found -> []
end
| AST_arrow(before, after) ->
begin try match VarMap.find (node^"/"^prefix^"init") env.st.save with
| VBool true -> sub_eval before
| VBool false -> sub_eval after
| _ -> assert false
with Not_found -> assert false
end
(* other *)
| AST_if(cond, a, b) ->
begin match sub_eval cond with
| [VBool true] -> sub_eval a
| [VBool false] -> sub_eval b
| _ -> type_error "Invalid condition in if."
end
| AST_instance((f, _), args, nid) ->
let (n, _) = find_node_decl env.st.p f in
List.map
(fun (_, (id, _), _) -> get_var env (node^"/"^nid) id)
n.ret
(*
reset_scope : env -> scope -> unit
Sets all the init variables to true, and all the state variables
to initial state. Does this recursively.
*)
let rec reset_scope env (node, prefix, eqs) =
Hashtbl.replace env.vars (node^"/"^prefix^"init") (VBool true);
let do_exp e =
List.iter
(fun (id, eqs, _) -> reset_scope env (node^"/"^id, "", eqs))
(extract_instances env.st.p e)
in
let do_eq (e, _) = match e with
| AST_assign(_, e) | AST_assume(_, e) | AST_guarantee(_, e) -> do_exp e
| AST_automaton (aid, states, _) ->
let (init_state, _) = List.find (fun (st, _) -> st.initial) states in
Hashtbl.replace env.vars (node^"/"^aid^".state") (VState init_state.st_name);
List.iter
(fun (st, _) -> reset_scope env (node, aid^"."^st.st_name^".", st.body))
states
| AST_activate (x, _) ->
let rec aux = function
| AST_activate_if (_, a, b) -> aux a; aux b
| AST_activate_body b ->
reset_scope env (node, b.act_id^".", b.body)
in aux x
in
List.iter do_eq eqs
(*
activate_scope : env -> scope -> unit
Adds functions for calculating the variables in this scope.
For assign : simple !
For automaton : lazily select+activate automaton state (would enable
implementation of strong transitions)
For activate : lazily select+activate active block
*)
let rec activate_scope env (node, prefix, eqs) =
Hashtbl.replace env.vars (node^"/"^prefix^"act") (VBool true);
let do_expr e =
List.iter
(fun (id, eqs, args) ->
activate_scope env (node^"/"^id, "", eqs);
let do_arg ((_,(name,_),_), expr) =
let apath = node^"/"^id^"/"^name in
let calc () =
match eval_expr env (node, prefix) expr with
| [v] -> Hashtbl.replace env.vars apath v; v
| _ -> loc_error (snd expr) error "Unsupported arity for argument."
in
Hashtbl.replace env.vars apath (VCalc calc)
in
List.iter do_arg args)
(extract_instances env.st.p e)
in
let do_eq (e, _) = match e with
| AST_assign(vars, e) ->
do_expr e;
let calc i () =
let vals = eval_expr env (node, prefix) e in
List.iter2 (fun (id, _) v ->
Hashtbl.replace env.vars (node^"/"^id) v) vars vals;
List.nth vals i
in
List.iteri
(fun i (id, _) ->
Hashtbl.replace env.vars (node^"/"^id) (VCalc (calc i)))
vars
| AST_assume(_,e) | AST_guarantee(_,e) -> do_expr e
| _ -> not_implemented "activate_scope"
in
List.iter do_eq eqs
(*
is_scope_active : env -> scope -> bool
*)
let is_scope_active env (node, prefix, _) =
try as_bool (Hashtbl.find env.vars (node^"/"^prefix^"act"))
with Not_found -> false
(*
do_weak_transitions : env -> scope -> unit
*)
let do_weak_transitions env (node, prefix, eqs) =
not_implemented "do_weak_transitions"
(*
extract_st : env -> state
*)
let extract_st env =
let rec aux (node, prefix, eqs) save =
let init =
try as_bool (Hashtbl.find env.vars (node^"/"^prefix^"init"))
with Not_found ->
let pre_init = as_bool (VarMap.find (node^"/"^prefix^"init") env.st.save)
in pre_init && (not (is_scope_active env (node, prefix, eqs)))
in
let save = VarMap.add (node^"/"^prefix^"init") (VBool init) save in
let save_expr save e =
let save = List.fold_left
(fun save (id, expr) ->
VarMap.add
(node^"/"^prefix^id)
(VPrevious (eval_expr env (node, prefix) expr))
save)
save (extract_pre e) in
let save = List.fold_left
(fun save (n, eqs, _) ->
aux (node^"/"^n, "", eqs) save)
save (extract_instances env.st.p e)
in save
in
let save_eq save eq = match fst eq with
| AST_assign(_, e) | AST_assume(_, e) | AST_guarantee(_, e) ->
save_expr save e
| AST_automaton(aid, states, _) -> not_implemented "save_eq automaton"
| AST_activate(r, _) -> not_implemented "save_eq activate"
in
List.fold_left save_eq save eqs
in
let consts = VarMap.filter (fun k _ -> k.[0] <> '/') env.st.save in
{ env.st with save = aux env.st.root_scope consts }
(*
init_state : prog -> id -> state
*)
let init_state p root =
let (n, _) = find_node_decl p root in
let root_scope = ("", "", n.body) in
let st = {
p = p;
root_scope = root_scope;
save = VarMap.empty;
outputs = (List.map (fun (_,(n,_),_) -> n) n.ret);
} in
let env = { st = st; vars = Hashtbl.create 42 } in
(* calculate constants *)
List.iter (function
| AST_const_decl(d, l) ->
let cpath = "cst/" ^ d.c_name in
Hashtbl.replace env.vars cpath (VCalc (fun () ->
match eval_expr env ("cst", "") d.value with
| [v] -> Hashtbl.replace env.vars cpath v; v
| _ -> loc_error l error "Arity error in constant expression."))
| _ -> ())
p;
List.iter (function
| AST_const_decl(d, l) -> ignore (get_var env "cst" d.c_name)
| _ -> ())
p;
reset_scope env root_scope;
{ st with save = Hashtbl.fold VarMap.add env.vars VarMap.empty }
(*
step : state -> io -> (state * io)
*)
let step st i =
let vars = Hashtbl.create 10 in
List.iter (fun (k, v) -> Hashtbl.replace vars ("/"^k) v) i;
let env = { st = st; vars = vars } in
activate_scope env st.root_scope;
(*
Hashtbl.iter (fun k v -> Format.printf "%s : %s@." k (str_repr_of_val v)) env.vars;
*)
let out = List.map (fun id -> id, get_var env "" id) st.outputs in
(* do_weak_transitions env st.root_scope; *)
extract_st env, out
end