packages feed

lambda-calculator-3.1.0.0: test/Language/Lambda/SystemFSpec.hs

module Language.Lambda.SystemFSpec where

import Language.Lambda.Shared.Errors (LambdaException(..), isLambdaException)
import Language.Lambda.SystemF
import Language.Lambda.SystemF.HspecUtils

import Lens.Micro
import RIO
import RIO.Map (empty, fromList)
import Test.Hspec

spec :: Spec
spec = do
  describe "evalText" $ do
    let eval' :: Text -> Either LambdaException (SystemFExpr Text)
        eval' = over _Right (^. _expr) . eval

    it "evaluates simple text" $ do
      eval' "x" `shouldBeRight` Var "x"
      eval' "\\x:T. x" `shouldBeRight` Abs "x" (TyVar "T") (Var "x")
      eval' "\\X. x" `shouldBeRight` TyAbs "X" (Var "x")

    it "reduces simple applications" $
      eval' "(\\x:T. x) y:T" `shouldBeRight` VarAnn "y" (TyVar "T")

    it "reduces applications with nested redexes" $
      eval' "(\\f:T->T x:T. f x) (\\y:T. y)"
        `shouldBeRight` Abs "x" (TyVar "T") (Var "x")

    it "lets update state" $ do
      let act = evalText "let x = a: A" >> evalText "x"

      unsafeExecTypecheck act (mkTypecheckState [] [])
        `shouldBe` TypedExpr (VarAnn "a" (TyVar "A")) (TyVar "A")

  describe "runEvalText" $ do
    let runEvalText' input = extract $ runEvalText input empty
        extract = _Right %~ (^. _expr) . fst
    
    it "evaluates simple text" $ do
      runEvalText' "x" `shouldBeRight` Var "x"
      runEvalText' "\\x:T. x" `shouldBeRight` Abs "x" (TyVar "T") (Var "x")
      runEvalText' "\\X. x" `shouldBeRight` TyAbs "X" (Var "x")

  describe "execEvalText" $ do
    let execEvalText' input = extract $ execEvalText input empty
        extract = over _Right (^. _expr)
    
    it "evaluates simple text" $ do
      execEvalText' "x" `shouldBeRight` Var "x"
      execEvalText' "\\x:T. x" `shouldBeRight` Abs "x" (TyVar "T") (Var "x")
      execEvalText' "\\X. x" `shouldBeRight` TyAbs "X" (Var "x")

  describe "unsafeExecEvalText" $ do
    let unsafeExecEvalText' input = extract $ unsafeExecEvalText input empty
        extract = (^. _expr)

    it "evaluates simple text" $ do
      unsafeExecEvalText' "x" `shouldBe` Var "x"
      unsafeExecEvalText' "\\x:T. x" `shouldBe` Abs "x" (TyVar "T") (Var "x")
      unsafeExecEvalText' "\\X. x" `shouldBe` TyAbs "X" (Var "x")

    it "throws errors" $ do
      evaluate (unsafeExecEvalText' "\\x. x") `shouldThrow` isLambdaException

  describe "typecheckText" $ do
    let tc :: Text -> Either LambdaException (Ty Text)
        tc input = execTypecheck (typecheckText input) initialState

        initialState = mkTypecheckState defaultUniques defaultTyUniques

    it "typechecks simple text" $ do
      tc "x" `shouldHaveType` "Z"
      tc "\\x:T. x" `shouldHaveType` "T -> T"
      tc "\\X. x" `shouldHaveType` "forall X. Z"
      tc "(\\x:T. x) y:T" `shouldHaveType` "T"
      tc "(\\f:(T->T) x:T. f x) (\\y:T. y)" `shouldHaveType` "T -> T"

  describe "runTypecheckText" $ do
    let tc :: Text -> Either LambdaException (Ty Text)
        tc input = fst <$> runTypecheckText input globals'

        globals' = fromList [("x", TypedExpr (Var "x") (TyVar "A"))]

    it "typechecks simple text" $ do
      tc "x" `shouldHaveType` "A"
      tc "\\x:T. x" `shouldHaveType` "T -> T"
      tc "\\X. x" `shouldHaveType` "forall X. A"
      tc "(\\x:T. x) y:T" `shouldHaveType` "T"
      tc "(\\f:(T->T) x:T. f x) (\\y:T. y)" `shouldHaveType` "T -> T"

  describe "execTypecheckText" $ do
    let tc :: Text -> Either LambdaException (Ty Text)
        tc input = execTypecheckText input globals'

        globals' = fromList [("x", TypedExpr (Var "x") (TyVar "A"))]

    it "typechecks simple text" $ do
      tc "x" `shouldHaveType` "A"
      tc "\\x:T. x" `shouldHaveType` "T -> T"
      tc "\\X. x" `shouldHaveType` "forall X. A"
      tc "(\\x:T. x) y:T" `shouldHaveType` "T"
      tc "(\\f:(T->T) x:T. f x) (\\y:T. y)" `shouldHaveType` "T -> T"

  describe "unsafeExecTypecheckText" $ do
    let tc :: Text -> Ty Text
        tc input = unsafeExecTypecheckText input globals'

        globals' = fromList [("x", TypedExpr (Var "x") (TyVar "A"))]

    it "typechecks simple text" $ do
      Right (tc "x") `shouldHaveType` "A"
      Right (tc "\\x:T. x") `shouldHaveType` "T -> T"
      Right (tc "\\X. x") `shouldHaveType` "forall X. A"
      Right (tc "(\\x:T. x) y:T") `shouldHaveType` "T"
      Right (tc "(\\f:(T->T) x:T. f x) (\\y:T. y)") `shouldHaveType` "T -> T"