open Ast
open Ast_util
open Formula
open Typing
open Cmdline
open Util
open Num_domain
open Enum_domain
module I (ED : ENUM_ENVIRONMENT_DOMAIN) (ND : NUMERICAL_ENVIRONMENT_DOMAIN) : sig
type st
val do_prog : cmdline_opt -> rooted_prog -> unit
end = struct
(* **************************
Disjunction domain
************************** *)
type case_v = ED.t * ND.t
type case = ED.item list
type dd_v = {
d_vars : id list;
data : (case, case_v) Hashtbl.t;
}
(*
print_dd : Formatter.t -> dd_v -> unit
*)
let print_aux fmt d_vars env =
let print_it q (enum, num) =
Format.fprintf fmt "@[<hov 4>";
List.iter2
(fun id v -> Format.fprintf fmt "%a ≡ %s,@ "
Formula_printer.print_id id v)
d_vars q;
Format.fprintf fmt "@ %a,@ %a@]@."
ED.print enum ND.print num;
in
Hashtbl.iter print_it env
let print_dd fmt dd = print_aux fmt dd.d_vars dd.data
(*
dd_bot : id list -> dd_v
*)
let dd_bot d_vars = { d_vars; data = Hashtbl.create 12 }
(*
dd_add_case : dd_v -> case_v -> unit
*)
let dd_add_case env (enum, num) =
let rec aux enum vals = function
| [] ->
let case = List.rev vals in
begin try let e0, n0 = Hashtbl.find env.data case in
Hashtbl.replace env.data case (ED.join enum e0, ND.join num n0);
with Not_found ->
Hashtbl.add env.data case (enum, num);
end
| p::q ->
List.iter
(fun v -> match ED.apply_cons enum (E_EQ, p, EItem v) with
| [en] -> aux en (v::vals) q
| _ -> assert false)
(ED.project enum p)
in aux enum [] env.d_vars
(*
dd_singleton : id list -> case_v -> dd_v
*)
let dd_singleton d_vars d =
let v = { d_vars; data = Hashtbl.create 1 } in
dd_add_case v d;
v
(*
dd_extract_case : dd_v -> case -> dd_v
*)
let dd_extract_case v case =
try dd_singleton v.d_vars (Hashtbl.find v.data case)
with Not_found -> dd_bot v.d_vars
(*
dd_apply_ncons : dd_v -> num_cons list -> dd_v
*)
let dd_apply_ncl v ncl =
let vv = dd_bot v.d_vars in
Hashtbl.iter (fun case (enum, num) ->
let num = ND.apply_cl num ncl in
if not (ND.is_bot num) then
Hashtbl.add vv.data case (enum, num))
v.data;
vv
(*
dd_apply_econs : dd_v -> enum_cons -> dd_v
*)
let dd_apply_econs v cons =
let vv = dd_bot v.d_vars in
Hashtbl.iter (fun case (enum, num) ->
List.iter
(fun enum -> dd_add_case vv (enum, num))
(ED.apply_cons enum cons))
v.data;
vv
(*
dd_join : dd_v -> dd_v -> dd_v
*)
let dd_join a b =
let vv = { d_vars = a.d_vars; data = Hashtbl.copy a.data } in
Hashtbl.iter (fun case (enum, num) ->
try let e2, n2 = Hashtbl.find vv.data case in
Hashtbl.replace vv.data case (ED.join e2 enum, ND.join n2 num)
with Not_found -> Hashtbl.replace vv.data case (enum, num))
b.data;
vv
(*
dd_meet : dd_v -> dd_v -> dd_v
*)
let dd_meet a b =
let vv = dd_bot a.d_vars in
Hashtbl.iter (fun case (enum, num) ->
try let e2, n2 = Hashtbl.find b.data case in
let en, nn = ED.meet enum e2, ND.meet n2 num in
if not (ND.is_bot nn) then Hashtbl.add vv.data case (en, nn)
with _ -> ())
a.data;
vv
(*
dd_apply_cl : dd_v -> conslist -> dd_v
*)
let rec apply_cl env (ec, nc, r) =
match r with
| CLTrue ->
let env_ec = List.fold_left dd_apply_econs env ec in
let env_nc = dd_apply_ncl env_ec nc in
(* Format.printf "[[ %a -> %a ]]@."
print_dd env print_dd env_nc; *)
env_nc
| CLFalse -> dd_bot env.d_vars
| CLAnd(a, b) ->
let env = apply_cl env (ec, nc, a) in
apply_cl env (ec, nc, b)
| CLOr((eca, nca, ra), (ecb, ncb, rb)) ->
dd_join (apply_cl env (ec@eca, nc@nca, ra))
(apply_cl env (ec@ecb, nc@ncb, rb))
(* ***************************
Abstract interpretation
*************************** *)
type st = {
rp : rooted_prog;
opt : cmdline_opt;
all_vars : (bool * id * typ) list;
enum_vars : (id * ED.item list) list;
num_vars : (id * bool) list;
(* program expressions *)
cl : conslist;
guarantees : (id * bool_expr) list;
(* abstract interpretation *)
cycle : (id * id * typ) list; (* s'(x) = s(y) *)
forget : (id * typ) list; (* s'(x) not specified *)
d_vars : id list; (* disjunction variables when calculating *)
acc_d_vars : id list; (* disjunction variables in accumulator *)
top : case_v;
env : (case, case_v) Hashtbl.t;
mutable delta : case list;
widen : (case, int) Hashtbl.t;
}
(*
print_st : Formatter.t -> st -> unit
*)
let print_st fmt st = print_aux fmt st.acc_d_vars st.env
(*
add_case : st -> case_v -> unit
*)
let add_case st (enum, num) =
let rec aux enum vals = function
| [] ->
let case = List.rev vals in
begin try let e0, n0 = Hashtbl.find st.env case in
if (not (ED.subset enum e0)) || (not (ND.subset num n0)) then begin
begin try
let n = Hashtbl.find st.widen case in
let jw = if n < st.opt.widen_delay then ND.join else ND.widen in
Hashtbl.replace st.env case (ED.join e0 enum, jw n0 num);
Hashtbl.replace st.widen case (n+1);
with Not_found ->
Hashtbl.replace st.env case (ED.join enum e0, ND.join num n0);
Hashtbl.add st.widen case 0;
end;
if not (List.mem case st.delta) then st.delta <- case::st.delta
end
with Not_found ->
Hashtbl.add st.env case (enum, num);
if not (List.mem case st.delta) then st.delta <- case::st.delta
end
| p::q ->
List.iter
(fun v -> match ED.apply_cons enum (E_EQ, p, EItem v) with
| [en] -> aux en (v::vals) q
| _ -> assert false)
(ED.project enum p)
in aux enum [] st.acc_d_vars
(*
dd_apply_cl : dd_v -> conslist -> dd_v
*)
let rec apply_cl_get_all_cases (enum, num) (ec, nc, r) =
match r with
| CLTrue ->
let enums = List.fold_left
(fun enums ec -> List.flatten
(List.map (fun e -> ED.apply_cons e ec) enums))
[enum] ec in
let num = ND.apply_cl num nc in
List.map (fun ec -> ec, num) enums
| CLFalse -> []
| CLAnd(a, b) ->
let envs = apply_cl_get_all_cases (enum, num) (ec, nc, a) in
List.flatten
(List.map (fun e -> apply_cl_get_all_cases e (ec, nc, b)) envs)
| CLOr((eca, nca, ra), (ecb, ncb, rb)) ->
apply_cl_get_all_cases (enum, num) (ec@eca, nc@nca, ra) @
apply_cl_get_all_cases (enum, num) (ec@ecb, nc@ncb, rb)
(*
pass_cycle : st -> case_v -> case_v
*)
let pass_cycle st (enum, num) =
let assign_e, assign_n = List.fold_left
(fun (ae, an) (a, b, t) -> match t with
| TEnum _ -> (a, b)::ae, an
| TInt | TReal -> ae, (a, NIdent b)::an)
([], []) st.cycle in
let enum = ED.assign enum assign_e in
let num = ND.assign num assign_n in
List.fold_left
(fun (enum, num) (var, t) ->
let e, n = match t with
| TEnum _ -> ED.forgetvar enum var, num
| TReal | TInt -> enum, ND.forgetvar num var
in e, n)
(enum, num) st.forget
let dd_pass_cycle st (v : dd_v) =
let vv = dd_bot st.acc_d_vars in
Hashtbl.iter (fun _ x -> dd_add_case vv (pass_cycle st x)) v.data;
vv
(*
init_state : int -> rooted_prog -> st
*)
let init_state opt rp =
let init_f, f = Transform.f_of_prog rp false in
Format.printf "Init formula: %a@.@." Formula_printer.print_expr init_f;
Format.printf "Cycle formula:@.%a@.@." Formula_printer.print_expr f;
let init_cl = conslist_of_f init_f in
let cl = Formula.conslist_of_f f in
Format.printf "Cycle conslist:@.%a@.@." Formula_printer.print_conslist cl;
let guarantees = Transform.guarantees_of_prog rp in
Format.printf "Guarantees:@.";
List.iter (fun (id, f) ->
Format.printf " %s: %a@." id Formula_printer.print_expr f)
guarantees;
Format.printf "@.";
(* add variables from LASTs *)
let last_vars = uniq_sorted
(List.sort compare (Transform.extract_last_vars f)) in
let last_vars = List.map
(fun id ->
let (_, _, ty) = List.find (fun (_, u, _) -> id = "L"^u) rp.all_vars
in false, id, ty)
last_vars in
let all_vars = last_vars @ rp.all_vars in
Format.printf "Vars: @[<hov>%a@]@.@."
(print_list Ast_printer.print_typed_var ", ")
all_vars;
let num_vars, enum_vars = List.fold_left
(fun (nv, ev) (_, id, t) -> match t with
| TEnum ch -> nv, (id, ch)::ev
| TInt -> (id, false)::nv, ev
| TReal -> (id, true)::nv, ev)
([], []) all_vars in
(* calculate cycle variables and forget variables *)
let cycle = List.fold_left
(fun q (_, id, ty) ->
if id.[0] = 'L' then
(id, String.sub id 1 (String.length id - 1), ty)::q
else q)
[] all_vars
in
let forget = List.map (fun (_, id, ty) -> (id, ty)) rp.all_vars in
(* calculate disjunction variables *)
(* actually, don't calculate them, rely on user input *)
let d_vars = List.filter (fun k -> k <> "/init" && opt.disjunct k)
(List.map (fun (id, _) -> id) enum_vars) in
let acc_d_vars = List.filter (fun i -> i.[0] = 'L') d_vars in
(* setup abstract data *)
let top = ED.top enum_vars, ND.top num_vars in
let delta = [] in
let widen = Hashtbl.create 12 in
let st =
{ rp; opt; enum_vars; num_vars; all_vars;
cl; guarantees;
cycle; forget; acc_d_vars; d_vars; top;
env = Hashtbl.create 12; delta; widen } in
(* calculate initial state and environment *)
List.iter (fun ed -> add_case st (pass_cycle st ed))
(apply_cl_get_all_cases top init_cl);
Hashtbl.iter (fun case _ -> st.delta <- case::st.delta) st.env;
st
(*
cycle : st -> cl -> dd_v -> dd_v
*)
let cycle st cl env =
let env_f = apply_cl env cl in
if st.opt.vverbose_ci then
Format.printf "{{ %a ->@.%a }}@." print_dd env print_dd env_f;
dd_pass_cycle st env_f
(*
set_target_case : st -> dd_v -> case -> dd_v
*)
let set_target_case st env case =
List.fold_left2
(fun env id v ->
if List.exists (fun (id2, _) -> id2 = "L"^id) st.enum_vars then
dd_apply_econs env (E_EQ, id, EItem v)
else env)
env st.acc_d_vars case
(*
do_prog : rooted_prog -> unit
*)
let do_prog opt rp =
let st = init_state opt rp in
Format.printf "Init: %a@." print_st st;
let n_ch_iter = ref 0 in
(* chaotic iteration loop *)
while st.delta <> [] do
let case = List.hd st.delta in
incr n_ch_iter;
Format.printf "Chaotic iteration %d: @[<hov>[%a]@]@."
!n_ch_iter
(print_list Formula_printer.print_id ", ") case;
let start = try Hashtbl.find st.env case with Not_found -> assert false in
let start_dd = dd_singleton st.acc_d_vars start in
let f i =
let i = dd_singleton st.d_vars i in
let i' = set_target_case st i case in
let q = dd_join start_dd (cycle st st.cl i') in
try Hashtbl.find q.data case with Not_found -> assert false
in
let rec iter n (i_enum, i_num) =
let fi_enum, fi_num = f (i_enum, i_num) in
let j_enum, j_num =
if n < st.opt.widen_delay then
ED.join i_enum fi_enum, ND.join i_num fi_num
else
ED.join i_enum fi_enum, ND.widen i_num fi_num
in
if ED.eq j_enum i_enum && ND.eq j_num i_num
then (i_enum, i_num)
else iter (n+1) (j_enum, j_num)
in
let y = iter 0 start in
let z = fix (fun (a, b) (c, d) -> ED.eq a c && ND.eq b d) f y in
let i = dd_singleton st.d_vars z in
let j = cycle st st.cl i in
let cases = ref [] in
Hashtbl.iter (fun case _ -> cases := case::(!cases)) j.data;
List.iter
(fun case ->
let i' = set_target_case st i case in
let j = cycle st st.cl i' in
Hashtbl.iter (fun _ q -> add_case st q) j.data)
!cases;
if st.opt.verbose_ci then
Format.printf "-> @[<hov>%a@]@." print_st st;
st.delta <- List.filter ((<>) case) st.delta;
done;
let final = apply_cl { d_vars = st.d_vars; data = st.env } st.cl in
Format.printf "Accessible: %a@." print_dd final;
let check_guarantee (id, f) =
let cl = Formula.conslist_of_f f in
Format.printf "@[<hv 4>%s:@ %a ⇒ ⊥ @ "
id Formula_printer.print_conslist cl;
let z = apply_cl final cl in
if Hashtbl.length z.data = 0 then
Format.printf "OK@]@ "
else
Format.printf "FAIL@]@ "
in
if st.guarantees <> [] then begin
Format.printf "Guarantee @[<v 0>";
List.iter check_guarantee st.guarantees;
Format.printf "@]@."
end;
if List.exists (fun (p, _, _) -> p) st.rp.all_vars then begin
Format.printf "Probes: @[<v 0>";
List.iter (fun (p, id, ty) ->
if p then begin
Format.printf "%a ∊ @[<v>" Formula_printer.print_id id;
Hashtbl.iter (fun case (enum, num) ->
begin match ty with
| TInt | TReal ->
Format.printf "%a" ND.print_itv (ND.project num id)
| TEnum _ ->
Format.printf "@[<hov>{ %a }@]"
(print_list Formula_printer.print_id ", ")
(ED.project enum id)
end;
Format.printf " when @[<hov>[%a]@]@ "
(print_list Formula_printer.print_id ", ") case)
final.data;
Format.printf "@]@ "
end)
st.rp.all_vars;
Format.printf "@]@."
end
end