summaryrefslogblamecommitdiff
path: root/abstract/enum_domain.ml
blob: d216276b95434104f592df35d858946d5d357cce (plain) (tree)



































                                                                       


                                                         











































































































                                                                          


























































































































                                                                                                          








                                                                                   
























































































































































































































                                                                                                          
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 "@[<v 2>{ ";
        List.iteri
          (fun j (v, ids) ->
            if j > 0 then Format.fprintf fmt "@ ";
            Format.fprintf fmt "@[<hov 4>";
            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 "@[<v 2>{ ";
        List.iteri
          (fun j (v, ids) ->
            if j > 0 then Format.fprintf fmt "@ ";
            Format.fprintf fmt "@[<hov 4>";
            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 " ∊ @[<hov>{ %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 "@[<v 2>{ ";
        List.iteri
          (fun j (v, ids) ->
            if j > 0 then Format.fprintf fmt "@ ";
            Format.fprintf fmt "@[<hov 4>";
            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 " ∊ @[<hov>{ %a }@]@]" (print_list Format.pp_print_string ", ") l)
          sbl;
        Format.fprintf fmt " }@]"

end