packages feed

lambda-calculator-1.1.0: test/Language/SystemF/ParserSpec.hs

module Language.SystemF.ParserSpec (spec) where

import Data.Either

import Test.Hspec

import Language.SystemF.Expression
import Language.SystemF.Parser

spec :: Spec
spec = do
  describe "parseExpr" $ do
    it "parses simple variables" $
      parseExpr "x" `shouldBe` Right (Var "x")

    it "parses parenthesized variables" $
      parseExpr "(x)" `shouldBe` Right (Var "x")

    it "parses simple abstractions" $
      parseExpr "\\x:T. x" `shouldBe` Right (Abs "x" (TyVar "T") (Var "x"))

    it "parses simple type abstractions" $
      parseExpr "\\X. x" `shouldBe` Right (TyAbs "X" (Var "x"))

    it "parses simple type applications" $ 
      parseExpr "x [T]" `shouldBe` Right (TyApp (Var "x") (TyVar "T"))

    it "parses nested abstractions" $
      parseExpr "\\a:A b:B. b" 
        `shouldBe` Right (Abs "a" (TyVar "A") (Abs "b" (TyVar "B") (Var "b")))

    it "parses abstractions with arrow types" $
      parseExpr "\\f:(T->U). f"
        `shouldBe` Right (Abs "f" (TyArrow (TyVar "T") (TyVar "U")) (Var "f"))

    it "parses simple applications" $
      parseExpr "f x" `shouldBe` Right (App (Var "f") (Var "x"))

    it "parses chained applications" $
      parseExpr "a b c" `shouldBe` Right (App (App (Var "a") (Var "b")) (Var "c"))

    it "parses complex expressions"  $ do
      let exprs = [
            "\\f:(A->B) x:B. f x",
            "(\\p:(X->Y->Z) x:X y:Y. y) (\\p:(A->B->C) x:B y:C. x)",
            "f (\\x:T. x)",
            "(\\ x:X . f x) g y",
            "(\\f:(X->Y) . (\\ x:X y:Y. f x y) f x y) w x y",
            "(\\x:T. x) [U]"
            ]

      mapM_ (flip shouldSatisfy isRight . parseExpr) exprs

    it "does not parse trailing errors" $
      parseExpr "x +" `shouldSatisfy` isLeft

    it "ignores whitespace" $ do
      let exprs = [
            " x ",
            " \\ x : X. x ",
            " ( x ) "
            ]

      mapM_ (flip shouldSatisfy isRight . parseExpr) exprs
  
  describe "parseType" $ do
    it "parses simple variables" $
      parseType "X" `shouldBe` Right (TyVar "X")

    it "parses parenthesized variables" $
      parseType "(T)" `shouldBe` Right (TyVar "T")

    it "parses simple arrow types" $
      parseType "A -> B" `shouldBe` Right (TyArrow (TyVar "A") (TyVar "B")) 

    it "parses parenthesized arrow types" $
      parseType "((X)->(Y))" `shouldBe` Right (TyArrow (TyVar "X") (TyVar "Y"))

    it "parses nested arrow types" $ do
      parseType "T -> U -> V" 
        `shouldBe` Right (TyArrow (TyVar "T") (TyArrow (TyVar "U") (TyVar "V")))

      parseType "(W -> V) -> U"
        `shouldBe` Right (TyArrow (TyArrow (TyVar "W") (TyVar "V")) (TyVar "U"))