{-# OPTIONS --no-termination-check #-}

module Lambda where

module Prelude where

  data Bool : Set where
    true  : Bool
    false : Bool

  if_then_else_ : {A : Set} -> Bool -> A -> A -> A
  if true  then x else y = x
  if false then x else y = y

  _∧_ : Bool -> Bool -> Bool
  true  ∧ y = y
  false ∧ y = false

  _∨_ : Bool -> Bool -> Bool
  true  ∨ y = true
  false ∨ y = y

  ¬_ : Bool -> Bool
  ¬ true  = false
  ¬ false = true

  data List (A : Set) : Set where
    nil    : List A
    _::_ : A -> List A -> List A

  _++_ : {A : Set} -> List A -> List A -> List A
  nil      ++ ys = ys
  (x :: xs) ++ ys = x :: xs ++ ys

  filter : {A : Set} -> (A -> Bool) -> List A -> List A
  filter p  nil      = nil
  filter p (x :: xs) = if p x then x :: filter p xs else filter p xs

  postulate
    String : Set
    Char   : Set

  {-# BUILTIN BOOL    Bool   #-}
  {-# BUILTIN FALSE   false  #-}
  {-# BUILTIN TRUE    true   #-}
  {-# BUILTIN STRING  String #-}
  {-# BUILTIN CHAR    Char   #-}
  {-# BUILTIN LIST    List   #-}

  primitive
    primStringEquality : String -> String -> Bool

  _==_ = primStringEquality

  infix 10 if_then_else_
  infixr 50 _::_ _++_
  infixl 5 _∨_
  infixl 7 _∧_
  infix 50 ¬_
  infix 15 _==_

open Prelude

Name : Set
Name = String

data Exp : Set where
  var  : Name -> Exp
  ƛ_⟶_ : Name -> Exp -> Exp
  _$_  : Exp -> Exp -> Exp

infixl 50 _$_
infix  20 ƛ_⟶_

infix 80 _[_/_]
infix 15 _∈_

_∈_ : Name -> List Name -> Bool
x ∈ y :: ys = x == y ∨ x ∈ ys
x ∈ nil    = false

-- Free variables
FV : Exp -> List Name
FV (var x)   = x :: nil
FV (s $ t)   = FV s ++ FV t
FV (ƛ x ⟶ t) = filter (\y -> ¬ (x == y)) (FV t)

-- Fresh names
fresh : Name -> Exp -> Name
fresh x e = fresh' (FV e)
  where
    fresh' : List Name -> Name
    fresh' xs = "z" -- TODO

-- Substitution
_[_/_] : Exp -> Exp -> Name -> Exp
var x     [ r / z ] = if x == z then r else var x
(s $ t)   [ r / z ] = s [ r / z ] $ t [ r / z ]
(ƛ x ⟶ t) [ r / z ] =
       if x == z   then ƛ x ⟶ t
  else if x ∈ FV r then ( let y : Name
                              y = fresh x r
                          in  ƛ y ⟶ t [ var y / x ] [ r / z ]
                        )
  else                  ƛ x ⟶ t [ r / z ]

