open Ast
open Data
open Util
open Ast_util
(* Data structures for the interpret *)
type scope = string
type svalue =
| VInt of int
| VBool of bool
| VReal of float
| VState of id
| VPrevious of value
| VBusy (* intermediate value : calculating ! for detection of cycles *)
and value = svalue list
type state = svalue VarMap.t
(* functions for recursively getting variables *)
type calc_fun =
| F of (state -> calc_map -> state)
and calc_map = calc_fun VarMap.t
let get_var (st: state) (c: calc_map) (id: id) : (state * svalue) =
let st =
if VarMap.mem id st then st
else match VarMap.find id c with
| F f ->
(* Format.printf "%s[ " id; *)
let r = f (VarMap.add id VBusy st) c in
(* Format.printf "]%s " id; *)
r
in
match VarMap.find id st with
| VBusy -> combinatorial_cycle id
| v -> st, v
(* pretty-printing *)
let rec str_of_value = 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_of_value v) "" p
^ "]"
| VBusy -> "#"
let print_state st =
VarMap.iter (fun id v -> Format.printf "%s = %s@." id (str_of_value v)) st
(* Expression evaluation *)
type eval_env = {
p: prog;
c: calc_map;
scopes: string list;
}
(*
eval_expr : eval_env -> string -> expr ext -> (state * value)
*)
let rec eval_expr env st exp =
let sub_eval = eval_expr env in
let scope = List.hd env.scopes in
match fst exp with
| AST_identifier (id, _) ->
let rec aux = function
| [] ->
no_variable id
| sc::q ->
try let st, v = get_var st env.c (sc^"/"^id) in st, [v]
with Not_found -> aux q
in loc_error (snd exp) aux env.scopes
| AST_idconst (id, _) ->
let st, v = get_var st env.c ("cst/"^id) in st, [v]
(* on numerical values *)
| AST_int_const (i, _) -> st, [VInt (int_of_string i)]
| AST_real_const (r, _) -> st, [VReal (float_of_string r)]
| AST_unary(AST_UPLUS, e) -> sub_eval st e
| AST_unary(AST_UMINUS, e) ->
begin match sub_eval st e with
| st, [VInt x] -> st, [VInt (-x)]
| st, [VReal x] -> st, [VReal(-. x)]
| _ -> type_error "Unary on non-numerical."
end
| AST_binary(op, e1, e2) ->
let st, v1 = sub_eval st e1 in
let st, v2 = sub_eval st e2 in
let iop, fop = match op with
| AST_PLUS -> (+), (+.)
| AST_MINUS -> (-), (-.)
| AST_MUL -> ( * ), ( *. )
| AST_DIV -> (/), (/.)
| AST_MOD -> (mod), mod_float
in
begin match v1, v2 with
| [VInt a], [VInt b] -> st, [VInt (iop a b)]
| [VReal a], [VReal b] -> st, [VReal(fop a b)]
| _ -> type_error "Invalid arguments for numerical binary."
end
(* on boolean values *)
| AST_bool_const b -> st, [VBool b]
| AST_binary_rel(op, e1, e2) ->
let st, v1 = sub_eval st e1 in
let st, v2 = sub_eval st e2 in
let r = match op with
| AST_EQ -> (=) | AST_NE -> (<>)
| AST_LT -> (<) | AST_LE -> (<=)
| AST_GT -> (>) | AST_GE -> (>=)
in
begin match v1, v2 with
| [VInt a], [VInt b] -> st, [VBool (r a b)]
| [VReal a], [VReal b] -> st, [VBool (r a b)]
| [VBool a], [VBool b] -> st, [VBool (r a b)]
| _ -> type_error "Invalid arguments for binary relation."
end
| AST_binary_bool(op, e1, e2) ->
let st, v1 = sub_eval st e1 in
let st, v2 = sub_eval st e2 in
let r = match op with
| AST_AND -> (&&) | AST_OR -> (||)
in
begin match v1, v2 with
| [VBool a], [VBool b] -> st, [VBool (r a b)]
| _ -> type_error "Invalid arguments for boolean relation."
end
| AST_not(e) ->
begin match sub_eval st e with
| st, [VBool b] -> st, [VBool (not b)]
| _ -> type_error "Invalid arguments for boolean negation."
end
(* temporal primitives *)
| AST_pre(exp, n) ->
begin try match VarMap.find (scope^"/"^n) st with
| VPrevious x -> st, x
| _ -> st, []
with Not_found -> st, []
end
| AST_arrow(before, after) ->
begin try match VarMap.find (scope^"/init") st with
| VBool true -> sub_eval st before
| VBool false -> sub_eval st after
| _ -> assert false
with Not_found -> error ("Internal: could not find init for scope " ^ scope)
end
(* other *)
| AST_if(cond, a, b) ->
let st, cv = sub_eval st cond in
begin match cv with
| [VBool true] -> sub_eval st a
| [VBool false] -> sub_eval st b
| _ -> type_error "Invalid condition in if."
end
| AST_instance((f, _), args, nid) ->
let (n, _) = find_node_decl env.p f in
List.fold_left
(fun (st, vs) (_, (id,_), _) ->
let st, v = get_var st env.c (scope^"/"^nid^"/"^id) in
st, vs @ [v])
(st, []) n.ret
(* Constant calculation *)
let rec calc_const p d st c =
let env = { p = p; c = c; scopes = ["cst"] } in
match eval_expr env st d.value with
| st, [v] -> VarMap.add ("cst/"^d.c_name) v st
| _ -> type_error ("Cannot assign tuple to constant" ^ d.c_name)
let program_consts p =
let cdecl = extract_const_decls p in
let ccmap = List.fold_left
(fun c (d, _) -> VarMap.add ("cst/"^d.c_name) (F (calc_const p d)) c)
VarMap.empty cdecl
in
List.fold_left
(fun st (d, _) -> let st, _ = get_var st ccmap ("cst/"^d.c_name) in st)
VarMap.empty
cdecl
(* get initial state for program *)
let program_init_state p root_node =
let (n, _) = find_node_decl p root_node in
let rec aux st p scope eqs =
let st = VarMap.add (scope^"/init") (VBool true) st in
let add_subscopes =
List.fold_left (fun st (ss_id, ss_eqs, _) -> aux st p (scope^"/"^ss_id) ss_eqs)
in
let add_eq st eq = match fst eq with
| AST_assign(_, e) -> add_subscopes st (extract_instances p e)
| AST_assume _ | AST_guarantee _ -> st
| AST_automaton (aid, astates, ret) ->
let (initial, _) = List.find (fun (s, _) -> s.initial) astates in
let st = VarMap.add (scope^":"^aid^".state") (VState initial.name) st in
let add_astate st ((astate:Ast.state), _) =
aux st p (scope^":"^aid^"."^astate.name) astate.body
in
List.fold_left add_astate st astates
| AST_activate _ -> not_implemented "program_init_state activate"
in
List.fold_left add_eq st eqs
in
aux (program_consts p) p "" n.body
(* Program execution *)
(* build calc map *)
let rec build_prog_ccmap p scopes eqs st =
let scope = List.hd scopes in
let add_eq c eq = match fst eq with
| AST_assign(vars, e) ->
let calc st c =
let env = { p = p; c = c; scopes = scopes } in
let st, vals = eval_expr env st e in
List.fold_left2
(fun st (id,_) v -> VarMap.add (scope^"/"^id) v st)
st vars vals
in
let c = List.fold_left (fun c (id, _) -> VarMap.add (scope^"/"^id) (F calc) c) c vars in
let add_subscope c (ss_id, ss_eqs, ss_args) =
let c = VarMap.merge disjoint_union c
(build_prog_ccmap p [scope^"/"^ss_id] ss_eqs st)
in
let add_v c ((_, (id, _), _), eq) =
let calc st c =
let env = { p = p; c = c; scopes = scopes } in
let st, vals = eval_expr env st eq in
match vals with
| [v] -> VarMap.add (scope^"/"^ss_id^"/"^id) v st
| _ -> type_error "invalid arity"
in
VarMap.add (scope^"/"^ss_id^"/"^id) (F calc) c
in
List.fold_left add_v c ss_args
in
List.fold_left add_subscope c (extract_instances p e)
| AST_assume _ | AST_guarantee _ -> c
| AST_automaton _ -> not_implemented "build_prog_ccmap for automaton"
| AST_activate _ -> not_implemented "build_prog_ccmap for activate"
in
List.fold_left add_eq VarMap.empty eqs
let extract_next_state active env eqs st =
let csts = VarMap.filter
(fun k _ -> String.length k > 4 && String.sub k 0 4 = "cst/")
st
in
let rec aux active scopes eqs st nst =
let scope = List.hd env.scopes in
let r = VarMap.add (scope^"/init")
(if active then VBool false
else try VarMap.find (scope^"/init") st with Not_found -> assert false)
nst
in
let add_subscopes active =
List.fold_left (fun (st, nst) (ss_id, ss_eqs, _) ->
aux active [scope^"/"^ss_id] ss_eqs st nst)
in
let add_eq (st, nst) eq = match fst eq with
| AST_assign(vars, e) ->
let st, nst = add_subscopes active (st, nst) (extract_instances env.p e) in
List.fold_left (fun (st, nst) (pn, pe) ->
let st, v = if active then eval_expr { env with scopes = scopes } st pe
else st,
try match VarMap.find (scope^"/"^pn) st with VPrevious x -> x | _ -> []
with Not_found -> []
in
st, VarMap.add (scope^"/"^pn) (VPrevious v) nst)
(st, nst) (extract_pre e)
| AST_assume _ | AST_guarantee _ -> st, nst
| AST_automaton _ -> not_implemented "extract_next_state automaton"
| AST_activate _ -> not_implemented "extract_next_state activate"
in
List.fold_left add_eq (st, r) eqs
in aux active env.scopes eqs st csts
let program_step p st inputs root_node =
let (n, _) = find_node_decl p root_node in
let st = List.fold_left
(fun st (n, v) -> VarMap.add ("/"^n) v st) st inputs in
let ccmap = build_prog_ccmap p [""] n.body st in
let st = List.fold_left
(fun st (_, (id, _), _) -> let st, _ = get_var st ccmap ("/"^id) in st)
st n.ret in
let outputs = List.map
(fun (_, (id, _), _) -> let _, v = get_var st ccmap ("/"^id) in (id, v))
n.ret in
let st, next_st = extract_next_state true { p = p; scopes = [""]; c = ccmap } n.body st in
st, outputs, next_st