summaryrefslogblamecommitdiff
path: root/abstract/_constant_domain.ml
blob: 544d2e952fd49fd2239e59a1d3ea54c4f8220511 (plain) (tree)
























































































































































                                                                  
open Util
open Abstract_syntax_tree

module CD : Domain.S =
  struct

    exception DivideByZero
    exception TypeError
    exception Bot
    exception NotImplemented

    type tv =   (* type for a variable *)
      | Top
      | I of Z.t

    type ts =   (* type for an environment *)
      | Bot
      | Nb of tv VarMap.t

    let top_ts = Nb VarMap.empty

    let ts_union a b =
      begin match a, b with
      | Bot, Bot -> Bot
      | Nb a, Bot -> Nb a
      | Bot, Nb b -> Nb b
      | Nb a, Nb b ->
        Nb
          (VarMap.fold
            (fun var value a ->
              try match VarMap.find var a with
                | Top -> a
                | I x ->
                  match value with
                  | I y when y = x -> a
                  | _ -> VarMap.add var Top a
              with
              | Not_found -> VarMap.add var value a)
          b a)
      end

    (* must not raise exception *)
    let ts_add_bool_constraint expr (s : ts) =
      s (* TODO, not necessary... *)


    (* only evaluates numerical statements, raises
      TypeError when result is bool *)
    let rec eval_abs expr (s : tv VarMap.t) =
      match expr with
      | AST_unary(op, (i, _)) ->
        begin match eval_abs i s with
        | Top -> Top
        | I x ->
          match op with
          | AST_UNARY_PLUS -> I x
          | AST_UNARY_MINUS -> I (Z.neg x)
          | _ -> raise TypeError
        end
      | AST_binary(op, (a, _), (b, _)) ->
        begin match eval_abs a s, eval_abs b s with
        | I x, I  y ->
          begin match op with
          | AST_PLUS -> I (Z.add x y)
          | AST_MINUS -> I (Z.sub x y)
          | AST_MULTIPLY -> I (Z.mul x y)
          | AST_DIVIDE ->
            if y = Z.zero then raise DivideByZero;
            I (Z.div x y)
          | AST_MODULO ->
            if y = Z.zero then raise DivideByZero;
            I (Z.rem x y)
          | _ -> raise TypeError
          end
        | _ -> Top
        end
      | AST_identifier(id, _) ->
        begin
          try VarMap.find id s
          with _ -> Top
        end 
      | AST_int_const(s, _) ->
        I (Z.of_string s)
      | AST_bool_const(_) -> raise TypeError
      | AST_int_rand _ -> Top
      | _ -> raise NotImplemented (* extensions *)

    (* returns true if the expression can evaluate to true
      returns false if the expression always evaluates to false *)
    let rec eval_bool_abs expr (s : tv VarMap.t) =
      true (* TODO *)

    (* must not raise exception *)
    let rec interp_abs stat s =
      begin match s with
      | Bot -> Bot
      | Nb vars ->
        begin match stat with
        | AST_block b ->
          List.fold_left
            (fun ss (st, _) -> interp_abs st ss)
            s b
        | AST_assign ((id, _), (exp, _)) ->
          begin
            try
              let value = eval_abs exp vars in
              Nb (VarMap.add id value vars)
            with _ -> Bot
          end
        | AST_if ((cond, _), (tb, _), None) ->
          ts_union (Nb vars)
            (interp_abs tb
              (ts_add_bool_constraint cond (Nb vars)))
        | AST_if ((cond, l), (tb, _), Some (eb, _)) ->
          ts_union
            (interp_abs tb
              (ts_add_bool_constraint cond (Nb vars)))
            (interp_abs eb
              (ts_add_bool_constraint 
                  (AST_unary (AST_NOT, (cond, l)))
                (Nb vars)))
        | AST_while ((cond, _), (body, _)) ->
          let f s =
            ts_union
              s
              (ts_add_bool_constraint cond s)
          in
            fix f (Nb vars)
        | AST_HALT -> Bot
        | AST_assert (cond, _) ->
          if eval_bool_abs cond vars
            then Nb vars
            else begin
              print_string "Assert fail\n";
              Bot
            end
        | AST_print items ->
          List.iter
            (fun (var, _) ->
              print_string (var ^ " = ");
              try
                match VarMap.find var vars with
                | Top -> print_string "T\n"
                | I x -> print_string (Z.to_string x ^ "\n")
              with _ -> print_string "T\n")
            items;
          Nb vars
        | _ -> raise NotImplemented (* extensions *)
        end
      end

  end