------------------------------------------------------------------------
-- The code derived by Hutton/Wright in "Calculating an exceptional
-- machine" is not structurally recursive. However, it is terminating.
------------------------------------------------------------------------

module NotStructurallyRecursive where

open import Data.Nat
open import NotStructurallyRecursive.Lemmas
open import Relation.Binary.PropositionalEquality

data Expr : Set where
  val :  -> Expr
  _⊕_ : Expr -> Expr -> Expr

data Cont : Set where
  STOP :                 Cont
  ADD  :     -> Cont -> Cont
  EVAL : Expr -> Cont -> Cont

-- Sizes of expressions and continuations, used for the termination
-- proof.

sizeE : Expr -> 
sizeE (val n)   = 1
sizeE (e₁  e₂) = 3 + sizeE e₁ + sizeE e₂

sizeC : Cont -> 
sizeC STOP       = 0
sizeC (ADD  n c) = 1 + sizeC c
sizeC (EVAL e c) = 2 + sizeE e + sizeC c

-- The following mutually recursive functions originally were not
-- structurally recursive, but after adding suitable measure functions
-- they are. The equality proofs are used to make it possible to
-- phrase the arguments to recursive applications in the right way.

mutual

  eval : forall (e : Expr) (c : Cont) s -> s  sizeE e + sizeC c -> 
  eval (val n)   c zero    ()
  eval (val n)   c (suc s) eq = exec c n s (cong pred eq)
  eval (e₁  e₂) c zero    ()
  eval (e₁  e₂) c (suc s) eq = eval e₁ (EVAL e₂ c) s lem
    where lem = lemma₁ (sizeE e₁) (sizeE e₂) (sizeC c) eq

  exec : forall (c : Cont) (n : ) s -> s  sizeC c -> 
  exec STOP         n _       _  = n
  exec (ADD m c₁)   n zero    ()
  exec (ADD m c₁)   n (suc s) eq = exec c₁ (m + n) s (cong pred eq)
  exec (EVAL e₁ c₂) n zero    ()
  exec (EVAL e₁ c₂) n (suc s) eq = eval e₁ (ADD n c₂) s lem
    where lem = lemma₂ (sizeE e₁) (sizeC c₂) eq

run : Expr -> 
run e = eval e STOP _ refl