(* -------------------------------------------------------------------------- *)

(* The type of lambda-terms, in de Bruijn's representation. *)

type var = int (* a de Bruijn index *)
type term =
  | Var of var
  | Lam of (* bind: *) term
  | App of term * term
  | Let of (* bind: *) term * term

(* -------------------------------------------------------------------------- *)

(* An environment-based big-step interpreter. This is the same interpreter
   that we programmed in Coq, except here, in OCaml, fuel is not needed. *)

type cvalue =
  | Clo of (* bind: *) term * cenv

and cenv =
  cvalue list

let empty : cenv =
  []

exception RuntimeError

let lookup (e : cenv) (x : var) : cvalue =
  try
    List.nth e x
  with Failure _ ->
    raise RuntimeError

let rec eval (e : cenv) (t : term) : cvalue =
  match t with
  | Var x ->
      lookup e x
  | Lam t ->
      Clo (t, e)
  | App (t1, t2) ->
      let cv1 = eval e t1 in
      let cv2 = eval e t2 in
      let Clo (u1, e') = cv1 in
      eval (cv2 :: e') u1
  | Let (t1, t2) ->
      eval (eval e t1 :: e) t2

(* -------------------------------------------------------------------------- *)

(* The CPS-transformed interpreter. *)

let rec evalk (e : cenv) (t : term) (k : cvalue -> 'a) : 'a =
  assert false

let eval (e : cenv) (t : term) : cvalue =
  evalk e t (fun cv -> cv)

(* -------------------------------------------------------------------------- *)

(* The CPS-transformed, defunctionalized interpreter. *)

type kont

let rec evalkd (e : cenv) (t : term) (k : kont) : cvalue =
  assert false

and apply (k : kont) (cv : cvalue) : cvalue =
  assert false

let eval e t =
  evalkd e t (assert false)