module STLC where

open import Level.Bounded using ([_])
open import Level

open import Data.Unit.Base using ()
open import Data.Empty using ()
open import Data.Bool.Base using (true; false; if_then_else_)
open import Data.Nat.Properties using (≤-refl)
open import Data.Char.Base
open import Data.String as String hiding (parens)
open import Data.List.Base as List using (_∷_; [])
open import Data.List.NonEmpty as List⁺ using (List⁺; _∷_)
open import Data.Vec.Base as Vec using (Vec)
open import Data.Maybe.Base using (Maybe; nothing; just)
open import Data.List.Sized.Interface
open import Data.Product using (_×_; _,_; uncurry; ; proj₁)
open import Function.Base

open import Relation.Nullary
open import Relation.Nullary.Decidable using (map′)
import Relation.Binary as B
open import Relation.Binary.PropositionalEquality using (_≡_; refl; cong)

open import Base Level.zero hiding (parens; commit)

data Tok : Set where
  LAM ARR DOT COL : Tok
  LPAR RPAR : Tok
  ID : String  Tok

eqTok : B.Decidable {A = Tok} _≡_
eqTok LAM LAM = yes refl
eqTok LAM ARR = no  ())
eqTok LAM DOT = no  ())
eqTok LAM COL = no  ())
eqTok LAM LPAR = no  ())
eqTok LAM RPAR = no  ())
eqTok LAM (ID x) = no  ())
eqTok ARR LAM = no  ())
eqTok ARR ARR = yes refl
eqTok ARR DOT = no  ())
eqTok ARR COL = no  ())
eqTok ARR LPAR = no  ())
eqTok ARR RPAR = no  ())
eqTok ARR (ID x) = no  ())
eqTok DOT LAM = no  ())
eqTok DOT ARR = no  ())
eqTok DOT DOT = yes refl
eqTok DOT COL = no  ())
eqTok DOT LPAR = no  ())
eqTok DOT RPAR = no  ())
eqTok DOT (ID x) = no  ())
eqTok COL LAM = no  ())
eqTok COL ARR = no  ())
eqTok COL DOT = no  ())
eqTok COL COL = yes refl
eqTok COL LPAR = no  ())
eqTok COL RPAR = no  ())
eqTok COL (ID x) = no  ())
eqTok LPAR LAM = no  ())
eqTok LPAR ARR = no  ())
eqTok LPAR DOT = no  ())
eqTok LPAR COL = no  ())
eqTok LPAR LPAR = yes refl
eqTok LPAR RPAR = no  ())
eqTok LPAR (ID x) = no  ())
eqTok RPAR LAM = no  ())
eqTok RPAR ARR = no  ())
eqTok RPAR DOT = no  ())
eqTok RPAR COL = no  ())
eqTok RPAR LPAR = no  ())
eqTok RPAR RPAR = yes refl
eqTok RPAR (ID x) = no  ())
eqTok (ID x) LAM = no  ())
eqTok (ID x) ARR = no  ())
eqTok (ID x) DOT = no  ())
eqTok (ID x) COL = no  ())
eqTok (ID x) LPAR = no  ())
eqTok (ID x) RPAR = no  ())
eqTok (ID x) (ID y) = map′ (cong ID)  where refl  refl) (x String.≟ y)

Token : Set
Token = Position × Tok

keywords : List⁺ (String × Tok)
keywords = ("→"   , ARR)
          ("λ"   , LAM)
          (":"   , COL)
          []

breaking : Char   λ b  if b then Maybe Tok else Lift _ 
breaking c = case c of λ where
  '('  true , just LPAR
  ')'  true , just RPAR
  '.'  true , just DOT
  c    if isSpace c then true , nothing else false , _

open import Text.Lexer keywords breaking ID using (tokenize)

instance
  _ : Tokenizer [ Position × Tok ]
  _ = record { tokenize = tokenize  fromList }

infixr 15 _`→_
data Type : Set where
     : String  Type
  _`→_ : Type  Type  Type

module ParserM = Agdarsec Level.zero [ Position ] [  ] (record { into = proj₁ })
open ParserM

instance
  _ = ParserM.monadZero
  _ = ParserM.monadPlus
  _ = ParserM.monad

P = ParserM.param [ Token ]  n  [ Vec Token n ]) λ where (p , _) _  Value (_ , lift (p , []))

theTok : Tok  ∀[ Parser P [ Token ] ]
theTok t = maybeTok $ λ where
  tok@(p , t')  case eqTok t t' of λ where
    (yes eq)  just tok
    (no ¬eq)  nothing

parens :  {A}  ∀[  Parser P A  Parser P A ]
parens □p = theTok LPAR &> □p <& box (theTok RPAR)

name : ∀[ Parser P [ String ] ]
name = maybeTok λ where (p , ID str)  just str; _  nothing

type : ∀[ Parser P [ Type ] ]
type = fix _ $ λ rec 
  let varlike str = case String.toList str of λ where
        ('`'  nm)  just (String.fromList nm)
        _  nothing
      typevar : Parser P [ String ] _
      typevar = guardM varlike name
  in chainr1 ( <$> typevar <|> parens rec)
             (box (_`→_ <$ theTok ARR))

_ : "`a → (`b → `c) → `b"  type
_ =  "a" `→ ( "b" `→  "c") `→  "b" !

mutual

  data Val : Set where
    Lam : String  Val  Val
    Emb : Neu  Val

  data Neu : Set where
    Var : String  Neu
    Cut : Val  Type  Neu
    App : Neu  Val  Neu

record Language n : Set where
  field pVal : Parser P [ Val ] n
        pNeu : Parser P [ Neu ] n
open Language

language : ∀[ Language ]
language = fix Language $ λ rec 
  let □val = map pVal rec
      □neu = map pNeu rec
      var  = Var <$> guard (List.all isAlpha ∘′ String.toList) name
      cut  =  where ((v , _) , σ)  Cut v σ)
             <$> (theTok LPAR
              &> □val <&> box (theTok COL) <&> box (commit type)
              <& box (theTok RPAR))
      app  = flip App <$> ((Emb <$> var) <|> parens □val)
      neu  = iterate (var <|> cut <|> parens □neu) (box app)
      lam  = uncurry Lam
             <$> ((theTok LAM &> box name)
             <&> box (theTok DOT &> map commit □val))
      val = lam <|> Emb <$> neu

  in record { pVal = val <|> parens □val
            ; pNeu = neu <|> parens □neu
            }

val : ∀[ Parser P [ Val ] ]
val = pVal language

-- tests

_ : "λx.x"  val
_ = Lam "x" (Emb (Var "x")) !

_ : "λx.(λy.x y)"  val
_ = Lam "x" (Lam "y" (Emb (App (Var "x") (Emb (Var "y"))))) !

_ : "(λx.(λx.x : `a → `a) x)"  val
_ = Lam "x"
      (Emb (App (Cut (Lam "x" (Emb (Var "x"))) ( "a" `→  "a"))
                (Emb (Var "x")))) !

_ : "λg.λf.λa.g a (f a)"  val
_ = Lam "g" (Lam "f" (Lam "a"
     (Emb (App (App (Var "g") (Emb (Var "a")))
               (Emb (App (Var "f") (Emb (Var "a")))))))) !

_ : "(λg.λf.λa.(g a) (f a))"  val
_ = Lam "g" (Lam "f" (Lam "a"
     (Emb (App (App (Var "g") (Emb (Var "a")))
               (Emb (App (Var "f") (Emb (Var "a")))))))) !

_ : "(λg.(λf.(λa.(((g) ((a)))) ((f) (a)))))"  val
_ = Lam "g" (Lam "f" (Lam "a"
     (Emb (App (App (Var "g") (Emb (Var "a")))
               (Emb (App (Var "f") (Emb (Var "a")))))))) !