Fixpoint expr_get (s: locals)(h: heap) (e: expr) : option adr :=
match e with
| Const n => Some n
| Var x => table_get s x
| Offset e n => do1 v <- expr_get s h e; Some (v+n)
| Mem e => do1 v <- expr_get s h e; heap_get h v
end.
Fixpoint get_list s h (xs: list expr) : option (list adr) :=
match xs with
| nil => Some nil
| e :: es => do1 v <- expr_get s h e; do1 vs <- get_list s h es; Some (v::vs)
end.
Definition mk_locals  (xs: list var) (ys: list adr) : locals := combine xs ys.
Definition state := (locals * heap * control)%type.
Definition step (p: program) (sk: state) : option state :=
match sk with
| ((stk,hp), Assign (Var x) y c) =>
do1 v <- expr_get stk hp y;
Some ((table_set x v stk, hp), c)
| ((stk,hp), Assign (Mem x) y c) =>
do1 v <- expr_get stk hp y;
do1 p <- expr_get stk hp x;
Some ((stk, heap_set p v hp), c)
| (_, Assign _ y c) => None
| ((stk,hp), IfThenElse x c1 c2) =>
do1 v <- expr_get stk hp x;
Some (if eq_dec v 0 then ((stk,hp), c2) else ((stk,hp), c1))
| ((stk, hp), Go x ys) =>
do1 v <- expr_get stk hp x;
do1 k <- table_get p v;
do1 vs <- get_list stk hp ys;
Some ((mk_locals (fst k) vs, hp), snd k)
end.
Fixpoint stepN (p: program) (sk: state) (n: nat) : option state :=
match n with O => Some sk
| S n' => match step p sk with Some sk' => stepN p sk' n' | _ => None end
end.
Fixpoint boundary (p: program) : nat :=
match p with (n, _)::p' => Max.max (S n) (boundary p') | nil => 2 end.
Definition initial_locals (p: program) : locals := (0, boundary p)::nil.
Definition initial_heap (p: program) : heap :=
fun i => if lt_dec i (boundary p) then None else Some 0.
Definition run (p: program) (n: nat) : option state :=
stepN p (nil, initial_heap p, Go (Const 0) (Const (boundary p) :: nil)) n.
Notation "'Do' x ':=' v ';' c" := (Assign x v c) (at level 200, x at level 1, v at level 90, c at level 200).
Notation "'If' x 'Then' c1 'Else' c2" := (IfThenElse x c1 c2)  (at level 200, x at level 90, c1 at level 200, c2 at level 200).
Definition safe (p: program) : Prop :=  forall n, exists sk', run p n = Some sk'.
(* LOCAL VARIABLE ENVIRONMENTS *)
Definition env := var -> adr.
Definition env_set (f: env) (x: var) (y: adr) :=
fun i => if eq_dec i x then y else f i.

(* Specifications: *)
Lemma env_gss : forall s x y, env_set s x y x = y.
Lemma env_gso: forall s x y z, x<>z -> env_set s x y z = s z.
Definition locals2env (s: locals) : env :=
fun x => match table_get s x with Some a => a | None => 0 end.