import .imp_syntax

def string.index : string → nat → option char :=
fun s n, s.to_list.nth n

namespace imp

-- Stores
def store.empty : store := []

@[irreducible] def update (x : string) (v : val) : store → store
| [] := [(x, v)]
| ((x', v') :: rest) :=
  if x = x'
  then (x, v) :: rest
  else (x', v') :: (update rest).

def updates : list string → list val →  store → option store
| [] [] s := some s
| (x :: xs') (v :: vs') s := updates xs' vs' (update x v s)
| _ _ _ := none

def store.lookup (x : string) : store → option val
| [] := none
| ((x', v) :: rest) :=
  if x = x'
  then some v
  else store.lookup rest

-- Common
def heap.size : heap → ℤ
| [] := 0
| (_ :: rest) := 1 + heap.size rest.

def is_nop (p : stmt) : decidable (p = stmt.nop) :=
begin
  refine (if H : p = stmt.nop then _ else _),
  apply decidable.is_true,
  assumption,
  apply decidable.is_false,
  assumption,
end

def heap.emtpy : heap := []

def copy : nat → val → list val
| 0 v := []
| (n + 1) v := v :: copy n v

def heap.alloc (h : heap) (i : ℤ) (v : val) : heap :=
  h ++ (val.int i :: copy (int.to_nat i) v).

def heap.read : heap → int → option val
| [] _ := none
| (v :: vs) 0 := some v
| (v :: vs) i := heap.read vs (i - 1)

def heap.write : heap → ℤ → val → option heap
| []  _ _ := none
| (x :: xs) 0 v := some (v :: xs)
| (x :: xs) i v :=
  match heap.write xs (i - 1) v with
  | some h' := some (x :: h')
  | none := none
  end

-- (** Common *)
def heap.length : heap → ℤ
| [] := 0
| (_ :: rest) := 1 + heap.length rest

open stmt

def stmt.size : stmt → nat
| nop := 0
| (set _ _) := 0
| (alloc _ _ _) := 0
| (write _ _ _) := 0
| (ifelse _ p1 p2) := nat.succ (stmt.size p1 + stmt.size p2)
| (while _ p1) := nat.succ (stmt.size p1)
| (seq p1 p2) := nat.succ (stmt.size p1 + stmt.size p2)

inductive result : Type
| done : heap -> val -> result
| timeout : store -> heap -> stmt -> expr -> result
| stuck : store -> heap -> stmt -> expr -> result.

end imp


