{-# OPTIONS --safe #-}
module CtxTyp.Denotational where
open import Data.Bool using (Bool; true; false; if_then_else_)
open import Data.Nat using (ℕ; suc; zero; _≥‴_; ≤‴-refl; ≤‴-step)
open import Data.Product as P using (_×_; proj₁; proj₂)
open import Data.Unit using (⊤; tt)
open import Function using (id; _∘_; case_of_)
open import Relation.Binary.PropositionalEquality using (_≡_; refl; cong; cong₂)
open import CtxTyp.Context
open import CtxTyp.Term
open import CtxTyp.Substitution
⟦_⟧ : Type n → Context (suc n) → Set
⟦ [ Δ ⊢ A ]⇒ B ⟧ Γ = ∀ {Γ′} → Subst Γ Γ′ → ⟦ A ⟧ (Γ′ ++ Δ) → ⟦ B ⟧ Γ′
⟦ Δ ▷ A ⟧ Γ = ⟦ A ⟧ (Γ ++ Δ)
⟦ `Bool ⟧ Γ = Bool
⟦ ◯ A ⟧ Γ = Term (suc _) Γ A
subst⟦_⟧ : ∀ A → Subst Γ₁ Γ₂ → ⟦ A ⟧ Γ₁ → ⟦ A ⟧ Γ₂
subst⟦ [ Δ ⊢ A ]⇒ B ⟧ σ f = λ σ′ d → f (substSubst σ′ σ) d
subst⟦ Δ ▷ A ⟧ σ d = subst⟦ A ⟧ (exts++ Δ σ) d
subst⟦ `Bool ⟧ σ b = b
subst⟦ ◯ A ⟧ σ e = subst σ e
G⟦_⟧ : Context n → Context (suc n) → Set
Item⟦_⊢_^_⟧ : Context m → Type m → m ≥‴ n → Context (suc n) → Set
G⟦ ∅ ⟧ Γ′ = ⊤
G⟦ Γ ,[ Δ ⊢ A ^ m≥n ] ⟧ Γ′ = G⟦ Γ ⟧ Γ′ × Item⟦ Δ ⊢ A ^ m≥n ⟧ Γ′
G⟦ Γ₁ ++ Γ₂ ⟧ Γ′ = G⟦ Γ₁ ⟧ Γ′ × G⟦ Γ₂ ⟧ Γ′
Item⟦ Δ ⊢ A ^ ≤‴-refl ⟧ Γ′ = ∀ {Γ″} → Subst Γ′ Γ″ → G⟦ Δ ⟧ Γ″ → ⟦ A ⟧ Γ″
Item⟦ Δ ⊢ A ^ ≤‴-step m≥n ⟧ Γ′ = Γ′ ⊩[ Δ ⊢ A ^ m≥n ]
lookupG : G⟦ Γ ⟧ Γ′ → ∀ {m} {Δ} {A} {m≥n : m ≥‴ _} → Γ ∋[ Δ ⊢ A ^ m≥n ] → Item⟦ Δ ⊢ A ^ m≥n ⟧ Γ′
lookupG {Γ = Γ ,[ _ ^ _ ]} γ Z = γ .proj₂
lookupG {Γ = Γ ,[ _ ^ _ ]} γ (S x) = lookupG (γ .proj₁) x
lookupG {Γ = Γ₁ ++ Γ₂} (γ₁ P., γ₂) (L x) = lookupG γ₁ x
lookupG {Γ = Γ₁ ++ Γ₂} (γ₁ P., γ₂) (R x) = lookupG γ₂ x
_↾ᴳ : G⟦ Γ ⟧ Γ′ → Subst (Γ ↾) Γ′
_↾ᴳ {Γ = ∅} tt = ∅
_↾ᴳ {Γ = Γ ,[ Δ ⊢ A ^ ≤‴-refl ]} γ = γ .proj₁ ↾ᴳ
_↾ᴳ {Γ = Γ ,[ Δ ⊢ A ^ ≤‴-step m≥n ]} γ = (γ .proj₁ ↾ᴳ) , γ .proj₂
_↾ᴳ {Γ = Γ₁ ++ Γ₂} (γ₁ P., γ₂) = γ₁ ↾ᴳ ++ˢ γ₂ ↾ᴳ
liftS : Subst Γ Γ′ → G⟦ inject₁ Γ ⟧ Γ′
liftS ∅ = tt
liftS (σ , item) = liftS σ P., item
liftS (σ₁ ++ˢ σ₂) = liftS σ₁ P., liftS σ₂
substG : Subst Γ₁ Γ₂ → G⟦ Γ ⟧ Γ₁ → G⟦ Γ ⟧ Γ₂
substG {Γ = ∅} σ γ = tt
substG {Γ = Γ ,[ _ ^ ≤‴-refl ]} σ (γ P., f) = substG σ γ P., λ σ′ → f (substSubst σ′ σ)
substG {Γ = Γ ,[ Δ ⊢ A ^ ≤‴-step m≥n ]} σ (γ P., item) = substG σ γ P., substSubstItem σ item
substG {Γ = Γ₁ ++ Γ₂} σ (γ₁ P., γ₂) = substG σ γ₁ P., substG σ γ₂
liftG : Rename Γ Γ′ → G⟦ inject₁ Γ ⟧ Γ′
liftG {Γ = ∅} ρ = tt
liftG {Γ = Γ ,[ _ ^ _ ]} ρ = liftG (ρ ∘ S) P., var (ρ Z)
liftG {Γ = Γ₁ ++ Γ₂} ρ = liftG (ρ ∘ L) P., liftG (ρ ∘ R)
liftG-resp-≗ : ∀ {ρ ρ′ : Rename Γ Γ′} → ρ ≗ ρ′ → liftG ρ ≡ liftG ρ′
liftG-resp-≗ {Γ = ∅} ρ≗ρ′ = refl
liftG-resp-≗ {Γ = Γ ,[ _ ^ _ ]} ρ≗ρ′ = cong₂ P._,_ (liftG-resp-≗ (ρ≗ρ′ ∘ S)) (cong var (ρ≗ρ′ Z))
liftG-resp-≗ {Γ = Γ₁ ++ Γ₂} ρ≗ρ′ = cong₂ P._,_ (liftG-resp-≗ (ρ≗ρ′ ∘ L)) (liftG-resp-≗ (ρ≗ρ′ ∘ R))
extG : G⟦ Γ ⟧ Γ′ → G⟦ Γ ,[ Δ ⊢ A ^ ≤‴-step m≥n ] ⟧ (Γ′ ,[ Δ ⊢ A ^ m≥n ])
extG γ = substG (lift S) γ P., var Z
extG++ : ∀ Δ → G⟦ Γ ⟧ Γ′ → G⟦ Γ ++ inject₁ Δ ⟧ (Γ′ ++ Δ)
extG++ Δ γ = substG (lift L) γ P., liftG R
single⟦_⟧ : (A : Type n) → ⟦ A ⟧ (Γ′ ++ Δ) → ∀ {Γ″} → Subst Γ′ Γ″ → G⟦ inject₁ Δ ⟧ Γ″ → ⟦ A ⟧ Γ″
single⟦ A ⟧ d σ γ = subst⟦ A ⟧ (σ ++ˢ renameSubstʳ (∋-↾⁺ ∘ ∋-inject₁⁺) (γ ↾ᴳ)) d
_∪_ : G⟦ Γ ⟧ Γ′ → G⟦ Δ ⟧ Γ′ → G⟦ Γ ++ Δ ⟧ Γ′
_∪_ γ₁ γ₂ = γ₁ P., γ₂
E⟦_⟧ : Term n Γ A → G⟦ Γ ⟧ Γ′ → ⟦ A ⟧ Γ′
S⟦_⟧ : Subst Δ Γ → G⟦ Γ ⟧ Γ′ → G⟦ Δ ⟧ Γ′
E⟦ ` x `with σ ⟧ γ = (lookupG γ x) (lift id) (S⟦ σ ⟧ γ)
E⟦ `true ⟧ γ = true
E⟦ `false ⟧ γ = false
E⟦ `if e₁ `then e₂ `else e₃ ⟧ γ = if E⟦ e₁ ⟧ γ then E⟦ e₂ ⟧ γ else E⟦ e₃ ⟧ γ
E⟦ `λ⟨_⟩_ {A = A} Δ e₁ ⟧ γ = λ σ d → E⟦ e₁ ⟧ (substG σ γ P., single⟦ A ⟧ d)
E⟦ e₁ · e₂ ⟧ γ = E⟦ e₁ ⟧ γ (lift id) (E⟦ e₂ ⟧ (extG++ _ γ))
E⟦ ⟨ e ⟩ ⟧ γ = subst (γ ↾ᴳ) e
E⟦ `let⟨ Δ ⟩ e₁ e₂ ⟧ γ = let e = E⟦ e₁ ⟧ (extG++ Δ γ) in E⟦ e₂ ⟧ (γ P., term e)
E⟦ `wrap Δ e ⟧ γ = E⟦ e ⟧ (extG++ Δ γ)
E⟦ `letwrap {A = A} Δ e₁ e₂ ⟧ γ = let d = E⟦ e₁ ⟧ γ in E⟦ e₂ ⟧ (γ P., single⟦ A ⟧ d)
S⟦ ∅ ⟧ γ = tt
S⟦ σ , var x ⟧ γ = S⟦ σ ⟧ γ P., lookupG γ x
S⟦ _,_ {m≥n = ≤‴-refl} σ (term e) ⟧ γ = S⟦ σ ⟧ γ P., λ σ′ γ′ → E⟦ e ⟧ ((substG σ′ γ) ∪ γ′)
S⟦ _,_ {m≥n = ≤‴-step m≥sn} σ (term e) ⟧ γ = S⟦ σ ⟧ γ P., term (subst (exts++ _ (exts↾≥ m≥sn (γ ↾ᴳ))) e)
S⟦ σ₁ ++ˢ σ₂ ⟧ γ = S⟦ σ₁ ⟧ γ P., S⟦ σ₂ ⟧ γ
eval : Term n (inject₁ Γ) A → ⟦ A ⟧ Γ
eval e = E⟦ e ⟧ (liftG id)
test₁ : eval {n = 0} {Γ = ∅} (`if `true `then `true `else `false) ≡ true
test₁ = refl
test₂ : eval {n = 0} {Γ = ∅} (`let⟨ ∅ ⟩ ⟨ `true ⟩ ⟨ `if (` # 0 `with ∅) `then `true `else `false ⟩) ≡ (`if `true `then `true `else `false)
test₂ = refl
test₃ : eval {n = 0} {Γ = ∅} (`let⟨ ∅ ,[ (∅ , `Bool ^ 1) ⊢ `Bool ^ ≤‴-refl ] ⟩ (`let⟨ ∅ ⟩ ⟨ `true ⟩ ⟨ ` # 1 `with (∅ , var Z) ⟩) ⟨ `true ⟩) ≡ (`true)
test₃ = refl
test₄ : eval {n = 0} {Γ = ∅} (`let⟨ ∅ ,[ (∅ , `Bool ^ 1) ⊢ `Bool ^ ≤‴-refl ] ⟩ (`let⟨ ∅ ⟩ ⟨ `true ⟩ ⟨ ` # 1 `with (∅ , var Z) ⟩) (`let⟨ ∅ , `Bool ^ 1 ⟩ ⟨ `if ` # 0 `with ∅ `then `true `else `false ⟩ (`let⟨ ∅ ⟩ ⟨ `false ⟩ ⟨ ` # 2 `with (∅ , var (# 1))⟩))) ≡ (`if `true `then `true `else `false)
test₄ = refl
test₅ : eval {n = 0} {Γ = ∅} (`let⟨ ∅ , `Bool ^ 1 ⟩ ⟨ `if (` # 0 `with ∅) `then `true `else `false ⟩ (`let⟨ ∅ ⟩ ⟨ `true ⟩ ⟨ ` # 1 `with (∅ , var (# 0)) ⟩)) ≡ ( `if `true `then `true `else `false )
test₅ = refl