summaryrefslogblamecommitdiff
path: root/abstract/interpret.ml
blob: 5f037754af590b08a0b9bc58d4c4172de341281b (plain) (tree)
1
2
3
4
5
6
7
8
9








                                               






                                                                 


                                        


                                                        


                                                   



                                                                    



                                                                       





                                                                            


























                                                                                             





                                                                                            




                                         





                                                                                                






                                                                     



                                                                               


























                                                                      
                                    
                              
                                                                     
                                  

                                                                                              
                            
                                          
                                    


                                                                                     
                           








                                                                      



                                                         
                                           




                                                                   
                  

                                                                  
   
open Abstract_syntax_tree
open Environment_domain
open Util

module Make (E : ENVIRONMENT_DOMAIN) = struct

	let neg (e, l) =
		(AST_unary(AST_NOT, (e, l))), l

	let binop op (e, l) e2 =
		(AST_binary (op, (e,l), e2)), l
	let m1 (e, l) =
		binop AST_MINUS (e, l) (AST_int_const("1", l), l)
	let p1 (e, l) =
		binop AST_PLUS (e, l) (AST_int_const("1", l), l)

    let bottom_with_vars =
        List.fold_left E.addvar E.bottom

	let rec condition cond env =
		begin match fst cond with
		| AST_binary (AST_LESS_EQUAL, e1, e2) ->
			E.compare_leq env e1 e2
		| AST_binary (AST_EQUAL, e1, e2) ->
            E.compare_eq env e1 e2
		| AST_binary (AST_AND, e1, e2) ->
			E.meet (condition e1 env) (condition e2 env)
		| AST_binary (AST_OR, e1, e2) ->
			E.join (condition e1 env) (condition e2 env)

		(* transformations : remove not *)
		| AST_unary (AST_NOT, (AST_unary(AST_NOT, cond), x)) ->
			condition cond env
		| AST_unary (AST_NOT, (AST_binary(AST_AND, e1, e2), x)) ->
			condition
				(AST_binary(AST_OR, neg e1, neg e2), x) env
		| AST_unary (AST_NOT, (AST_binary(AST_OR, e1, e2), x)) ->
			condition
				(AST_binary(AST_AND, neg e1, neg e2), x) env

		| AST_unary (AST_NOT, (AST_binary(op, e1, e2), x)) ->
			let op2 = match op with
			| AST_LESS_EQUAL -> AST_GREATER
			| AST_LESS -> AST_GREATER_EQUAL
			| AST_GREATER_EQUAL -> AST_LESS
			| AST_GREATER -> AST_LESS_EQUAL
			| AST_EQUAL -> AST_NOT_EQUAL
			| AST_NOT_EQUAL -> AST_EQUAL
			| _ -> assert false
			in
			condition (binop op2 e1 e2) env
		
		(* transformations : encode everything with leq *)
		| AST_binary(AST_LESS, e1, e2) ->
			condition
				(binop AST_AND (binop AST_LESS_EQUAL e1 (m1 e2))
							   (binop AST_LESS_EQUAL (p1 e1) e2))
				env
		| AST_binary (AST_GREATER_EQUAL, e1, e2) ->
			condition
				(binop AST_LESS_EQUAL e2 e1)
				env
		| AST_binary (AST_GREATER, e1, e2) ->
			condition
				(binop AST_LESS e2 e1)
				env
		| AST_binary (AST_NOT_EQUAL, e1, e2) ->
			condition
				(binop AST_OR (binop AST_LESS e1 e2) (binop AST_LESS e2 e1))
				env

		| _ -> env
		end

	let rec interp_stmt env stat =
		begin match fst stat with
		| AST_block b ->
			(* remember to remove vars that have gone out of scope at the end *)
			let prevars = E.vars env in
			let env2 = List.fold_left interp_stmt env b in
			let postvars = E.vars env2 in
			let rmvars = List.filter (fun x -> not (List.mem x prevars)) postvars in
			List.fold_left E.rmvar env2 rmvars
		| AST_assign ((id, _), exp) ->
			E.assign env id exp
		| AST_if (cond, tb, None) ->
			E.join
				(interp_stmt (condition cond env) tb)
				(condition (neg cond) env)
		| AST_if (cond, tb, Some eb) ->
			let e1 = (interp_stmt (condition cond env) tb) in
			let e2 = (interp_stmt (condition (neg cond) env) eb) in
			E.join e1 e2
		| AST_while (cond, body) ->
            (* loop unrolling *)
            let rec unroll u = function
                | 0 -> u, bottom_with_vars (E.vars env)
                | n ->
                     let prev_u, u_prev_u = unroll u (n-1) in
                     interp_stmt (condition cond prev_u) body,
                     E.join u_prev_u (condition (neg cond) prev_u)
            in
            let env, u_u = unroll env 3 in
            (* widening *)
            let widen_delay = 3 in
            let fsharp i =
                let next_step = interp_stmt (condition cond i) body in
                E.join env next_step
            in
            let rec iter n i =
                let i' =
                    (if n < widen_delay then E.join else E.widen)
                        i
                        (fsharp i)
                in
                if i = i' then i else iter (n+1) i'
            in
            let x = iter 0 env in
            let y = fix fsharp x in     (* decreasing iteration *)
            E.join (condition (neg cond) y) u_u
		| AST_HALT -> bottom_with_vars (E.vars env)
		| AST_assert cond ->
			if not
				(E.is_bot (condition (neg cond) env))
			then begin
				Format.printf "%s: ERROR: assertion failure@."
					(Abstract_syntax_printer.string_of_extent (snd stat));
			end;
			condition cond env
		| AST_print items ->
			Format.printf "%s: %s@."
				(Abstract_syntax_printer.string_of_extent (snd stat))
				(E.var_str env (List.map fst items));
			env
		| AST_local ((ty, _), vars) ->
			List.fold_left
				(fun env ((id, _), init) ->
					let env2 = E.addvar env id in
					match init with
					| Some e -> E.assign env2 id e
					| None -> env2)
				env
				vars
		| _ -> assert false (* not implemented *)
		end
	
	let interpret prog =
		let result = List.fold_left
			(fun env x -> match x with
				| AST_stat st -> interp_stmt env st
				| _ -> env)
			E.init
			(fst prog)
		in
			Format.printf "Output: %s@."
				(E.var_str result (E.vars result))
end