
module Typechecker where

data Nat : Set where
  zero : Nat
  suc : Nat -> Nat

{-# BUILTIN NATURAL Nat #-}

_+_ : Nat -> Nat -> Nat
zero   + m = m
suc n  + m = suc (n + m)

data Fin : Nat -> Set where
  f0 : {n : Nat} -> Fin (suc n)
  fs : {n : Nat} -> Fin n -> Fin (suc n)

infixl 30 _::_ _++_

data Vect (A : Set) : Nat -> Set where
  ε    : Vect A zero
  _::_ : {n : Nat} -> Vect A n -> A -> Vect A (suc n)

fs^ : {n : Nat}(m : Nat) -> Fin n -> Fin (m + n)
fs^ zero    i = i
fs^ (suc m) i = fs (fs^ m i)

_++_ : {A : Set}{n m : Nat} -> Vect A n -> Vect A m -> Vect A (m + n)
xs ++ ε         = xs
xs ++ (ys :: y) = (xs ++ ys) :: y

data Chop {A : Set} : {n : Nat} -> Vect A n -> Fin n -> Set where
  chopGlue : {m n : Nat}(xs : Vect A n)(x : A)(ys : Vect A m) ->
             Chop (xs :: x ++ ys) (fs^ m f0)

chop : {A : Set}{n : Nat}(xs : Vect A n)(i : Fin n) -> Chop xs i
chop ε         ()
chop (xs :: x) f0 = chopGlue xs x ε
chop (xs :: y) (fs i) with chop xs i
chop (.(xs :: x ++ ys) :: y) (fs .(fs^ m f0))
  | chopGlue {m} xs x ys = chopGlue xs x (ys :: y)

infixr 40 _⊃_

data Simp : Set where
  o   : Simp
  _⊃_ : Simp -> Simp -> Simp

infix 20 Simp-_

data Simp-_ : Simp -> Set where
  neqo : Simp -> Simp -> Simp- o
  neq⊃ : {S T : Simp} -> Simp- (S ⊃ T)
  neqT : {S T : Simp}(T' : Simp- T) -> Simp- (S ⊃ T)
  neqS : {S : Simp}{T₁ : Simp}(S' : Simp- S)(T₂ : Simp) -> Simp- (S ⊃ T₁)

infixl 60 _∖_

_∖_ : (S : Simp) -> Simp- S -> Simp
.o       ∖ neqo S T        = S ⊃ T
.(_ ⊃ _) ∖ neq⊃            = o
.(S ⊃ T) ∖ neqT {S}{T}  T' = S ⊃ T ∖ T'
.(S ⊃ _) ∖ neqS {S} S' T₂  = S ∖ S' ⊃ T₂

data SimpEq? (S : Simp) : Simp -> Set where
  simpSame : SimpEq? S S
  simpDiff : (T : Simp- S) -> SimpEq? S (S ∖ T)

simpEq? : (S T : Simp) -> SimpEq? S T
simpEq? o o = simpSame
simpEq? o (S ⊃ T) = simpDiff (neqo S T)
simpEq? (S ⊃ T) o = simpDiff neq⊃
simpEq? (S₁ ⊃ T₁) (S₂ ⊃ T₂) with simpEq? S₁ S₂
simpEq? (S ⊃ T₁)  (.S ⊃ T₂)        | simpSame with simpEq? T₁ T₂
simpEq? (S ⊃ T)   (.S ⊃ .T)        | simpSame | simpSame    = simpSame
simpEq? (S ⊃ T)   (.S ⊃ .(T ∖ T')) | simpSame | simpDiff T' = simpDiff (neqT T')
simpEq? (S ⊃ T₁)  (.(S ∖ S') ⊃ T₂) | simpDiff S' = simpDiff (neqS S' T₂)

data Term : Nat -> Set where
  var : {n : Nat} -> Fin n -> Term n
  app : {n : Nat} -> Term n -> Term n -> Term n
  lam : {n : Nat} -> Simp -> Term (suc n) -> Term n

data Good : {n : Nat} -> Vect Simp n -> Simp -> Set where
  gVar : {n m : Nat}(Γ : Vect Simp n)(T : Simp)(Δ : Vect Simp m) ->
         Good (Γ :: T ++ Δ) T
  gApp : {n : Nat}{Γ : Vect Simp n}{S T : Simp} ->
         Good Γ (S ⊃ T) -> Good Γ S -> Good Γ T
  gLam : {n : Nat}{Γ : Vect Simp n}(S : Simp){T : Simp} ->
         Good (Γ :: S) T -> Good Γ (S ⊃ T)

g : {n : Nat}{Γ : Vect Simp n}(T : Simp) -> Good Γ T -> Term n
g T        (gVar{n}{m} Γ .T Δ) = var (fs^ m f0)
g T        (gApp f s)          = app (g _ f) (g _ s)
g .(S ⊃ _) (gLam S t)          = lam S (g _ t)

data Bad {n : Nat}(Γ : Vect Simp n) : Set where
  bNonFun   : Good Γ o -> Term n -> Bad Γ
  bMismatch : {S T : Simp}(S' : Simp- S) ->
              Good Γ (S ⊃ T) -> Good Γ (S ∖ S') -> Bad Γ
  bArg      : {S T : Simp} -> Good Γ (S ⊃ T) -> Bad Γ -> Bad Γ
  bFun      : Bad Γ -> Term n -> Bad Γ
  bLam      : (S : Simp) -> Bad (Γ :: S) -> Bad Γ

b : {n : Nat}{Γ : Vect Simp n} -> Bad Γ -> Term n
b (bNonFun f s)     = app (g o f) s
b (bMismatch _ f s) = app (g _ f) (g _ s)
b (bArg f s)        = app (g _ f) (b s)
b (bFun f s)        = app (b f) s
b (bLam S t)        = lam S (b t)

data TypeCheck? {n : Nat}(Γ : Vect Simp n) : Term n -> Set where
  good : (T : Simp)(t : Good Γ T) -> TypeCheck? Γ (g T t)
  bad  : (t : Bad Γ) -> TypeCheck? Γ (b t)

typeCheck? : {n : Nat}(Γ : Vect Simp n)(t : Term n) -> TypeCheck? Γ t
typeCheck? Γ (var i)   with chop Γ i
typeCheck? .(Γ :: T ++ Δ) (var ._) | chopGlue Γ T Δ = good T (gVar Γ T Δ)

typeCheck? Γ (app f s) with typeCheck? Γ f
typeCheck? Γ (app .(g (S ⊃ T) f) s) | good (S ⊃ T) f with typeCheck? Γ s
typeCheck? Γ (app .(g (S ⊃ T) f) .(g S' s))
             | good (S ⊃ T) f | good S' s with simpEq? S S'
typeCheck? Γ (app .(g (S ⊃ T) f) .(g S s))
             | good (S ⊃ T) f | good .S s        | simpSame    = good T (gApp f s)
typeCheck? Γ (app .(g (S ⊃ T) f) .(g (S ∖ S') s))
             | good (S ⊃ T) f | good .(S ∖ S') s | simpDiff S' = bad (bMismatch S' f s)
typeCheck? Γ (app .(g (S ⊃ T) f) .(b s))
             | good (S ⊃ T) f | bad s     = bad (bArg f s)
typeCheck? Γ (app .(g o f) s)       | good o f       = bad (bNonFun f s)
typeCheck? Γ (app .(b f) s)         | bad f          = bad (bFun f s)

typeCheck? Γ (lam S t) with typeCheck? (Γ :: S) t
typeCheck? Γ (lam S .(g T t)) | good T t = good (S ⊃ T) (gLam S t)
typeCheck? Γ (lam S .(b t))   | bad t    = bad (bLam S t)
