open Ast open Formula open Util exception Bot module type ENUM_ENVIRONMENT_DOMAIN = sig type t type item = string (* construction *) val top : (id * item list) list -> t (* variable management *) val vars : t -> (id * item list) list val forgetvar : t -> id -> t val project : t -> id -> item list (* set-theoretic operations *) val join : t -> t -> t (* union *) val meet : t -> t -> t (* intersection, may raise Bot *) val subset : t -> t -> bool val eq : t -> t -> bool (* abstract operations *) val apply_cons : t -> enum_cons -> t list (* may disjunct *) val assign : t -> (id * id) list -> t (* pretty-printing *) val print : Format.formatter -> t -> unit end (* Simple domain : one valuation for each variable, or T *) module Valuation : ENUM_ENVIRONMENT_DOMAIN = struct type item = string type t = { vars : (id * item list) list; value : item VarMap.t; } let top vars = { vars; value = VarMap.empty } let vars x = x.vars let forgetvar x id = { x with value = VarMap.remove id x.value } let project x id = try [VarMap.find id x.value] with Not_found -> List.assoc id x.vars let join x1 x2 = let v = VarMap.merge (fun _ a b -> match a, b with | Some a, Some b when a = b -> Some a | _ -> None) x1.value x2.value in { x1 with value = v } let meet x1 x2 = let v = VarMap.merge (fun _ a b -> match a, b with | Some a, None | None, Some a -> Some a | Some a, Some b -> if a = b then Some a else raise Bot | _ -> None) x1.value x2.value in { x1 with value = v } let subset a b = VarMap.for_all (fun id v -> try v = VarMap.find id b.value with Not_found -> true) a.value let eq a b = VarMap.equal (=) a.value b.value let cc op = match op with | E_EQ -> (=) | E_NE -> (<>) let apply_cons x (op, id, e) = match e with | EItem s -> begin try let t = VarMap.find id x.value in if cc op s t then [x] else [] with Not_found -> List.map (fun v -> { x with value = VarMap.add id v x.value }) (List.filter (cc op s) (List.assoc id x.vars)) end | EIdent id2 -> match (try Some (VarMap.find id x.value) with _ -> None), (try Some (VarMap.find id2 x.value) with _ -> None) with | None, None -> [x] | None, Some s -> List.map (fun v -> { x with value = VarMap.add id v x.value}) (List.filter (cc op s) (List.assoc id x.vars)) | Some s, None -> List.map (fun v -> { x with value = VarMap.add id2 v x.value}) (List.filter (cc op s) (List.assoc id2 x.vars)) | Some s, Some t -> if cc op s t then [x] else [] let assign x idl = let v = List.fold_left (fun v (id, id2) -> try VarMap.add id (VarMap.find id2 x.value) v with Not_found -> VarMap.remove id v ) x.value idl in { x with value = v } let print fmt x = let b = List.map (fun (x, y) -> y, x) (VarMap.bindings x.value) in let s = List.sort Pervasives.compare b in let rec bl = function | [] -> [] | (v, id)::q -> begin match bl q with | (vv, ids)::q when vv = v -> (v, id::ids)::q | r -> (v, [id])::r end in let sbl = bl s in Format.fprintf fmt "@[{ "; List.iteri (fun j (v, ids) -> if j > 0 then Format.fprintf fmt "@ "; Format.fprintf fmt "@["; List.iteri (fun i id -> if i > 0 then Format.fprintf fmt ",@ "; Format.fprintf fmt "%a" Formula_printer.print_id id) ids; Format.fprintf fmt " ≡ %s@]" v) sbl; Format.fprintf fmt " }@]" end (* Less simple domain : a set of possible values for each variable, or T *) module MultiValuation : ENUM_ENVIRONMENT_DOMAIN = struct type item = string type t = { vars : (id * item list) list; value : item list VarMap.t; } let sort = List.sort Pervasives.compare let uniq x = uniq_sorted (sort x) let top vars = { vars; value = VarMap.empty } let vars x = x.vars let forgetvar x id = { x with value = VarMap.remove id x.value } let project x id = try VarMap.find id x.value with Not_found -> List.assoc id x.vars let join x1 x2 = let v = VarMap.merge (fun _ a b -> match a, b with | Some a, Some b -> Some (uniq (a@b)) | _ -> None) x1.value x2.value in { x1 with value = v } let meet x1 x2 = let v = VarMap.merge (fun _ a b -> match a, b with | Some a, None | None, Some a -> Some a | Some a, Some b -> begin match List.filter (fun x -> List.mem x b) a with | [] -> raise Bot | l -> Some l end | _ -> None) x1.value x2.value in { x1 with value = v } let subset a b = VarMap.for_all (fun id v -> try let v2 = VarMap.find id b.value in List.for_all (fun x -> List.mem x v2) v with Not_found -> true) a.value let eq a b = subset a b && subset b a let apply_cons x (op, id, e) = let op = match op with | E_EQ -> (=) | E_NE -> (<>) in match e with | EItem s -> let pv = project x id in begin match List.filter (op s) pv with | [] -> [] | vals -> [{ x with value = VarMap.add id vals x.value }] end | EIdent id2 -> let v1 = project x id in let v2 = project x id2 in List.fold_left (fun l v1 -> let v2 = List.filter (op v1) v2 in let x = { x with value = VarMap.add id [v1] x.value } in match v2 with | [] -> l | _ -> { x with value = VarMap.add id2 v2 x.value }::l) [] v1 let assign x idl = let v = List.fold_left (fun v (id, id2) -> try VarMap.add id (VarMap.find id2 x.value) v with Not_found -> VarMap.remove id v ) x.value idl in { x with value = v } let print fmt x = let b = List.map (fun (x, y) -> y, x) (VarMap.bindings x.value) in let s = List.sort Pervasives.compare b in let rec bl = function | [] -> [] | (v, id)::q -> let v = sort v in if v <> sort (List.assoc id x.vars) then match bl q with | (vv, ids)::q when vv = v -> (v, id::ids)::q | r -> (v, [id])::r else bl q in let sbl = bl s in Format.fprintf fmt "@[{ "; List.iteri (fun j (v, ids) -> if j > 0 then Format.fprintf fmt "@ "; Format.fprintf fmt "@["; List.iteri (fun i id -> if i > 0 then Format.fprintf fmt ",@ "; Format.fprintf fmt "%a" Formula_printer.print_id id) ids; match v with | [v0] -> Format.fprintf fmt " ≡ %s@]" v0 | l -> Format.fprintf fmt " ∊ @[{ %a }@]@]" (print_list Format.pp_print_string ", ") l) sbl; Format.fprintf fmt " }@]" end (* Complicated domain : a set of values for each variables, plus some constraints on couples of variables (eg. (x, y) in [ tt, tt ; ff, ff ] WARNING : This domain is not proved to be safe. In particular, it may represent a set of contraints that imply Bot (ie that are impossible) without raising the Bot exception. (there is potentially exponential cost in the checking that all the constraints are coherent, and I have no idea of an algorithm for doing that check.) Therefore, do not use this domain unless you know what you are doing (which is probably not the case anyway). *) module MultiValuationCCons : ENUM_ENVIRONMENT_DOMAIN = struct module VarC = struct type t = id * id let compare = Pervasives.compare end module VarCMap = Mapext.Make(VarC) type item = string type t = { vars : (id * item list) list; value : item list VarMap.t; (* in ccond (x, y) -> l, must have x < y (textual order on identifiers) -> unicity *) ccons : (item * item) list VarCMap.t; } let sort = List.sort Pervasives.compare let uniq x = uniq_sorted (sort x) let list_inter x y = List.filter (fun k -> List.mem k y) x let all_couples l1 l2 = List.flatten (List.map (fun x -> List.map (fun y -> x, y) l2) l1) let top vars = { vars; value = VarMap.empty; ccons = VarCMap.empty } let vars x = x.vars let forgetvar x id = (* TODO : try to find a substitution variable so that some contraints can be propagated. this is important so that cycle passing can be done correctly ! *) { x with value = VarMap.remove id x.value; ccons = VarCMap.filter (fun (a, b) _ -> a <> id && b <> id) x.ccons } let project x id = try VarMap.find id x.value with Not_found -> List.assoc id x.vars let project2 x id1 id2 = try let id1', id2' = ord_couple (id1, id2) in if id1' = id1 then VarCMap.find (id1, id2) x.ccons else List.map (fun (a, b) -> b, a) (VarCMap.find (id1', id2') x.ccons) with _ -> let v1, v2 = project x id1, project x id2 in all_couples v1 v2 let strict x = let rec f x = (* - if (x, y) in [ a, b1 ; a, b2 ; ... ; a, bn ], replace this by x = a and y in { b1, ..., bn } - filter (x, y) in [ ... ] with x in specified itv, y in specified itv *) let usefull, vv, cc = VarCMap.fold (fun (x, y) l (usefull, vv, cc) -> let p1, p2 = uniq (List.map fst l), uniq (List.map snd l) in let p1 = try list_inter p1 (VarMap.find x vv) with _ -> p1 in let p2 = try list_inter p2 (VarMap.find y vv) with _ -> p2 in if p1 = [] || p2 = [] then raise Bot; let vv = VarMap.add x p1 (VarMap.add y p2 vv) in if List.length p1 = 1 || List.length p2 = 1 then true, vv, cc else match List.filter (fun (u, v) -> List.mem u p1 && List.mem v p2) l with | [] -> raise Bot | l2 -> List.length l2 < List.length l, vv, VarCMap.add (x, y) l2 cc) x.ccons (false, x.value, VarCMap.empty) in let x = { x with value = vv; ccons = cc } in if usefull then f x else x in f x let join x1 x2 = let v = VarMap.merge (fun _ a b -> match a, b with | Some a, Some b -> Some (uniq (a@b)) | _ -> None) x1.value x2.value in let x = { x1 with value = v } in let cc = VarCMap.merge (fun (id1, id2) l1 l2 -> let v1, v2 = project x1 id1, project x id2 in let ac = all_couples v1 v2 in let c = List.filter (fun q -> (match l1 with Some l -> List.mem q l | _ -> true) || (match l2 with Some l -> List.mem q l | _ -> true)) ac in if List.length c < List.length ac then Some c else None) x1.ccons x2.ccons in strict { x with ccons = cc } let meet x1 x2 = let v = VarMap.merge (fun _ a b -> match a, b with | Some a, None | None, Some a -> Some a | Some a, Some b -> begin match list_inter a b with | [] -> raise Bot | l -> Some l end | _ -> None) x1.value x2.value in let x = { x1 with value = v } in let cc = VarCMap.merge (fun (id1, id2) l1 l2 -> let v1, v2 = project x id1, project x id2 in let ac = all_couples v1 v2 in let c1 = match l1 with Some l -> list_inter l ac | None -> ac in let c2 = match l2 with Some l -> list_inter l ac | None -> ac in match list_inter c1 c2 with | [] -> raise Bot | l -> if List.length l < List.length ac then Some l else None) x1.ccons x2.ccons in strict { x with ccons = cc } let subset a b = VarMap.for_all (fun id v -> let vs = project a id in List.for_all (fun c -> List.mem c vs) v) b.value && VarCMap.for_all (fun (id1, id2) l -> let l2 = project2 a id1 id2 in List.for_all (fun c -> List.mem c l) l2) b.ccons let eq a b = subset a b && subset b a let apply_cons x (op, id, e) = let op = match op with | E_EQ -> (=) | E_NE -> (<>) in match e with | EItem s -> let pv = project x id in begin match List.filter (op s) pv with | [] -> [] | vals -> try [strict { x with value = VarMap.add id vals x.value }] with Bot -> [] end | EIdent id2 -> let id, id2 = ord_couple (id, id2) in let c = project2 x id id2 in let ok_c = List.filter (fun (a, b) -> op a b) c in try [match uniq (List.map fst ok_c), uniq (List.map snd ok_c) with | [], _ | _, [] -> raise Bot | [a], q -> strict { x with value = VarMap.add id [a] (VarMap.add id2 q x.value) } | q, [b] -> strict { x with value = VarMap.add id q (VarMap.add id2 [b] x.value) } | _ -> strict { x with ccons = VarCMap.add (id, id2) ok_c x.ccons} ] with Bot -> [] let assign x idl = let x2 = List.fold_left (fun x (v, _) -> forgetvar x v) x idl in let v = List.fold_left (fun v (id, id2) -> try VarMap.add id (VarMap.find id2 x.value) v with Not_found -> v) x2.value idl in let c = VarCMap.fold (fun (v1, v2) l c -> let v1' = try List.assoc v1 idl with _ -> v1 in let v2' = try List.assoc v2 idl with _ -> v2 in VarCMap.add (v1', v2') l c) x.ccons x2.ccons in strict { x with value = v; ccons = c } let print fmt x = let b = List.map (fun (x, y) -> y, x) (VarMap.bindings x.value) in let s = List.sort Pervasives.compare b in let rec bl = function | [] -> [] | (v, id)::q -> let v = sort v in if v <> sort (List.assoc id x.vars) then match bl q with | (vv, ids)::q when vv = v -> (v, id::ids)::q | r -> (v, [id])::r else bl q in let sbl = bl s in Format.fprintf fmt "@[{ "; List.iteri (fun j (v, ids) -> if j > 0 then Format.fprintf fmt "@ "; Format.fprintf fmt "@["; List.iteri (fun i id -> if i > 0 then Format.fprintf fmt ",@ "; Format.fprintf fmt "%a" Formula_printer.print_id id) ids; match v with | [v0] -> Format.fprintf fmt " ≡ %s@]" v0 | l -> Format.fprintf fmt " ∊ @[{ %a }@]@]" (print_list Format.pp_print_string ", ") l) sbl; Format.fprintf fmt " }@]" end