------------------------------------------------------------------------
-- A derivation of a compiler for a simple language
------------------------------------------------------------------------

module CompositionBased where

open import Data.Nat
open import Data.Product
open import Data.Vec using (Vec; []; _∷_)
open import Function
open import Relation.Binary.PropositionalEquality as P using (_≡_)
import Relation.Binary.HeterogeneousEquality as H

import Derivation
open import ReverseComposition as RC
  hiding (_↑_⇨_; module Derivation; module Stack)

------------------------------------------------------------------------
-- Language

-- A simple expression language.

data Expr : Set where
  val : (n : )  Expr
  _⊖_ : (e₁ e₂ : Expr)  Expr

-- The semantics of the language.

⟦_⟧ : Expr  
 val n    = n
 e₁  e₂  =  e₁    e₂ 

------------------------------------------------------------------------
-- Rewrite the evaluator in "compositional" style

-- An abbreviation.

infixl 5 _↑_

_↑_ : Set    Set
A  n = RC._↑_⇨_ A n A

module Compositional where

  open RC.Derivation
  open MonoidLaws

  ⟦_⟧↑ : Expr    0
   e ⟧↑ = return  e 

  sub :   2
  sub = Λ λ m  Λ λ n  return (m  n)

  mutual

    ⟦_⟧C : Expr    0
     e ⟧C = witness (eval e)

    eval : (e : Expr)  EqualTo  e ⟧↑
    eval (val n)   =  return n 
    eval (e₁  e₂) = 
      return ( e₁    e₂ )  ≡⟨ P.refl 
       e₂ ⟧↑   e₁ ⟧↑  sub   ≈⟨ proof (eval e₂) ⊙-cong
                                   proof (eval e₁) ⊙-cong refl sub 
       e₂ ⟧C   e₁ ⟧C  sub   

------------------------------------------------------------------------
-- Rewrite the evaluator in accumulator style

module Accumulator where

  open RC.Derivation
  open MonoidLaws
  open Compositional using (⟦_⟧↑; sub; ⟦_⟧C)

  mutual

    ⟦_⟧A : Expr   {i}    suc i    i
     e ⟧A f = witness (eval e f)

    eval :  (e : Expr) {i} (f :   suc i) 
           EqualTo ( e ⟧C  f)
    eval (val n)   f =  return n  f 
    eval (e₁  e₂) f = 
      ( e₂ ⟧C   e₁ ⟧C  sub)  f  ≈⟨ assoc  e₂ ⟧C ( e₁ ⟧C  sub) f 
       e₂ ⟧C  ( e₁ ⟧C  sub)  f  ≈⟨ refl  e₂ ⟧C ⊙-cong
                                        assoc  e₁ ⟧C sub f 
       e₂ ⟧C   e₁ ⟧C  sub  f    ≈⟨ refl  e₂ ⟧C ⊙-cong
                                        proof (eval e₁ (sub  f)) 
       e₂ ⟧C   e₁ ⟧A (sub  f)    ≈⟨ proof (eval e₂ ( e₁ ⟧A (sub  f))) 
       e₂ ⟧A ( e₁ ⟧A (sub  f))    

  halt :   1
  halt = identity

  correct :  e   λ f   e ⟧↑   e ⟧A f
  correct e = halt , (
     e ⟧↑         ≈⟨ proof $ Compositional.eval e 
     e ⟧C         ≈⟨ sym $ right-identity  e ⟧C 
     e ⟧C  halt  ≈⟨ proof $ eval e halt 
     e ⟧A halt    )

  -- ⟦_⟧A preserves equality of the accumulators.

  ⟦_⟧A-cong : (e : Expr)   {i} {f g :   suc i} 
              f  g   e ⟧A f   e ⟧A g
  ⟦_⟧A-cong (val n)   f≈g = f≈g ·-cong H.refl
  ⟦_⟧A-cong (e₁  e₂) f≈g =
     e₂ ⟧A-cong ( e₁ ⟧A-cong (refl sub ⊙-cong f≈g))

------------------------------------------------------------------------
-- Defunctionalise

module Defunctionalised where

  open RC.Derivation
  open Compositional using (⟦_⟧↑; sub)
  open Accumulator using (⟦_⟧A; ⟦_⟧A-cong)

  data Term :   Set where
    return⊙ :  {i} (n : ) (t : Term (1 + i))  Term i
    sub⊙    :  {i} (t : Term (1 + i))  Term (2 + i)
    halt    : Term 1

  -- The semantics of Term.

  apply :  {i}  Term i    i
  apply (return⊙ n t) = return n  apply t
  apply (sub⊙ t)      = sub  apply t
  apply halt          = Accumulator.halt

  -- The right-hand sides evaluate, so the code above is equivalent to
  -- the following one.

  exec :  {i}  Term i    i
  exec (return⊙ n t) = exec t · n
  exec (sub⊙ t)      = Λ λ m  Λ λ n  exec t · (m  n)
  exec halt          = Λ λ n  return n

  -- Compiler.

  mutual

    comp :  {i}  Expr  Term (suc i)  Term i
    comp e t = proj₁ (comp′ e t)

    comp′ :  (e : Expr) {i} (t : Term (suc i)) 
             λ (t′ : Term i)   e ⟧A (exec t)  exec t′
    comp′ (val n) t = return⊙ n t , (
      exec t · n          ≡⟨ P.refl 
      exec (return⊙ n t)  )
    comp′ (e₁  e₂) t = comp e₂ (comp e₁ (sub⊙ t)) , (
       e₂ ⟧A ( e₁ ⟧A (sub  exec t))   ≡⟨ P.refl 
       e₂ ⟧A ( e₁ ⟧A (exec (sub⊙ t)))  ≈⟨  e₂ ⟧A-cong (proj₂ $ comp′ e₁ (sub⊙ t)) 
       e₂ ⟧A (exec (comp e₁ (sub⊙ t)))  ≈⟨ proj₂ $ comp′ e₂ (comp e₁ (sub⊙ t)) 
      exec (comp e₂ (comp e₁ (sub⊙ t)))  )

  correct :  e   e ⟧↑  exec (comp e halt)
  correct e =
     e ⟧↑                   ≈⟨ proj₂ (Accumulator.correct e) 
     e ⟧A Accumulator.halt  ≡⟨ P.refl 
     e ⟧A (exec halt)       ≈⟨ proj₂ (comp′ e halt) 
    exec (comp e halt)       

------------------------------------------------------------------------
-- Switch to an explicit stack

module Stack where

  open Derivation
  open Compositional using (⟦_⟧↑)
  open Defunctionalised using (Term; return⊙; sub⊙; halt; comp)
  open P.≡-Reasoning

  _^_ : Set    Set
  A ^ n = Vec A n  A

  mutual

    exec :  {i}  Term i   ^ i
    exec t = witness  exec′ t

    exec′ :  {i} (t : Term i) (xs : Vec  i) 
            EqualTo (app (Defunctionalised.exec t) xs)
    exec′ (return⊙ n t) s =  begin
      app (Defunctionalised.exec t) (n  s)  ≡⟨ proof (exec′ t (n  s)) 
      exec t (n  s)                         
    exec′ (sub⊙ t) (m  n  s) =  begin
      app (Defunctionalised.exec t) (m  n  s)  ≡⟨ proof (exec′ t (m  n  s)) 
      exec t (m  n  s)                         
    exec′ halt (n  []) =  begin n 

  correct :  e   e   exec (comp e halt) []
  correct e = begin
     e                                                  ≡⟨ P.refl 
    run  e ⟧↑                                            ≡⟨ run-cong (Defunctionalised.correct e) 
    run (Defunctionalised.exec (comp e halt))             ≡⟨ P.refl 
    app {A = } (Defunctionalised.exec (comp e halt)) []  ≡⟨ proj₂ $ exec′ (comp e halt) [] 
    exec (comp e halt) []                                 

------------------------------------------------------------------------
-- Wrapping up

module Finally where

  Code : Set
  Code = Defunctionalised.Term 0

  comp : Expr  Code
  comp e = Defunctionalised.comp e Defunctionalised.halt

  exec : Code  
  exec = flip Stack.exec []

  correct :  e   e   exec (comp e)
  correct = Stack.correct