diff options
Diffstat (limited to 'lambda-calcul/haskell/test')
5 files changed, 157 insertions, 47 deletions
diff --git a/lambda-calcul/haskell/test/Minilang/IOSpec.hs b/lambda-calcul/haskell/test/Minilang/IOSpec.hs index 80cda6c..f000f0a 100644 --- a/lambda-calcul/haskell/test/Minilang/IOSpec.hs +++ b/lambda-calcul/haskell/test/Minilang/IOSpec.hs @@ -27,7 +27,7 @@ spec = parallel $ out <- readFile outputFileName - out `shouldBe` "V \"z\"\n" + out `shouldBe` "z" withTempFile :: (String -> IO ()) -> IO () withTempFile = diff --git a/lambda-calcul/haskell/test/Minilang/Lambda/DeBruijnSpec.hs b/lambda-calcul/haskell/test/Minilang/Lambda/DeBruijnSpec.hs index ef8903c..cff047d 100644 --- a/lambda-calcul/haskell/test/Minilang/Lambda/DeBruijnSpec.hs +++ b/lambda-calcul/haskell/test/Minilang/Lambda/DeBruijnSpec.hs @@ -1,8 +1,10 @@ +{-# LANGUAGE TypeApplications #-} module Minilang.Lambda.DeBruijnSpec where import Minilang.Lambda.DeBruijn (DeBruijn (..), eval, toDeBruijn) import qualified Minilang.Lambda.Eval as Eval import Test.Hspec (Spec, it, shouldBe) +import Data.Text (Text) spec :: Spec spec = do @@ -10,9 +12,9 @@ spec = do let term = Eval.Lam "x" (Eval.Lam "y" (Eval.Var "x")) deBruijn = toDeBruijn term - deBruijn `shouldBe` Lam (Lam (Var 1)) + deBruijn `shouldBe` Lam "x" (Lam "y" (Var "x" 1)) it "evaluates deBruijn term" $ do - let term = App (App (Lam (Lam (Var 1))) (Var 12)) (Var 34) + let term = App (App (Lam "x" (Lam "y" (Var "x" 1))) (FV "z")) (FV "t") - eval term `shouldBe` Var 12 + eval @Text term `shouldBe` FV "z" diff --git a/lambda-calcul/haskell/test/Minilang/Lambda/EvalSpec.hs b/lambda-calcul/haskell/test/Minilang/Lambda/EvalSpec.hs index 718b46d..89e86b9 100644 --- a/lambda-calcul/haskell/test/Minilang/Lambda/EvalSpec.hs +++ b/lambda-calcul/haskell/test/Minilang/Lambda/EvalSpec.hs @@ -2,51 +2,52 @@ module Minilang.Lambda.EvalSpec (spec) where import Control.Concurrent (threadDelay) import Control.Concurrent.Async (race_) -import Minilang.Lambda.Eval (Term (..), Value (..), eval, evalNeed) -import Test.Hspec (Spec, describe, it, shouldBe) +import Minilang.Lambda.Eval (Term (..), eval, evalNeed) +import Test.Hspec (Spec, describe, it, pending, shouldBe) spec :: Spec -spec = do - let fix = - Lam - "f" - ( App - (Lam "x" (App (Var "f") (App (Var "x") (Var "x")))) - (Lam "x" (App (Var "f") (App (Var "x") (Var "x")))) - ) - let omega = App fix fix - let term = App (App (Lam "x" (Lam "y" (Var "y"))) omega) (Var "z") - let fst' = Lam "x" (Lam "y" (Var "x")) +spec = it "evaluates Term" pending - describe "call-by-value eval" $ do - it "evaluates 'x' as itself" $ do - let term = Var "x" - eval term [] `shouldBe` V "x" - it "evaluates '((λ x. x) y)' as 'y'" $ do - let term = App (Lam "x" (Var "x")) (Var "y") - eval term [] `shouldBe` V "y" - it "evaluates '((λ x y. y) z t y)' as '(t y)'" $ do - let snd' = Lam "x" (Lam "y" (Var "y")) - let term = App (App (App snd' (Var "z")) (Var "t")) (Var "y") - eval term [] `shouldBe` Ap (V "t") (V "y") - it "evaluates '((λ x y. x) z t)' as 'z'" $ do - let term = App (App fst' (Var "z")) (Var "t") - eval term [] `shouldBe` V "z" - it "diverges on evaluating argument bound to ω" $ - shouldDiverge $ - eval term [] +-- let fix = +-- Lam +-- "f" +-- ( App +-- (Lam "x" (App (Var "f") (App (Var "x") (Var "x")))) +-- (Lam "x" (App (Var "f") (App (Var "x") (Var "x")))) +-- ) +-- let omega = App fix fix +-- let term = App (App (Lam "x" (Lam "y" (Var "y"))) omega) (Var "z") +-- let fst' = Lam "x" (Lam "y" (Var "x")) - describe "call-by-need eval" $ do - it "evaluates 'x' as itself" $ do - evalNeed (Var "x") [] `shouldBe` V "x" - it "evaluates '(λ x y . y) ⊥ z' as 'z'" $ do - evalNeed term [] `shouldBe` V "z" - it "diverges on evaluating ω" $ - shouldDiverge $ - evalNeed omega [] +-- describe "call-by-value eval" $ do +-- it "evaluates 'x' as itself" $ do +-- let term = Var "x" +-- eval term [] `shouldBe` V "x" +-- it "evaluates '((λ x. x) y)' as 'y'" $ do +-- let term = App (Lam "x" (Var "x")) (Var "y") +-- eval term [] `shouldBe` V "y" +-- it "evaluates '((λ x y. y) z t y)' as '(t y)'" $ do +-- let snd' = Lam "x" (Lam "y" (Var "y")) +-- let term = App (App (App snd' (Var "z")) (Var "t")) (Var "y") +-- eval term [] `shouldBe` Ap (V "t") (V "y") +-- it "evaluates '((λ x y. x) z t)' as 'z'" $ do +-- let term = App (App fst' (Var "z")) (Var "t") +-- eval term [] `shouldBe` V "z" +-- it "diverges on evaluating argument bound to ω" $ +-- shouldDiverge $ +-- eval term [] -shouldDiverge :: Value -> IO () -shouldDiverge t = - race_ - (t `seq` fail "Did not diverge") - (threadDelay 1000000) +-- describe "call-by-need eval" $ do +-- it "evaluates 'x' as itself" $ do +-- evalNeed (Var "x") [] `shouldBe` V "x" +-- it "evaluates '(λ x y . y) ⊥ z' as 'z'" $ do +-- evalNeed term [] `shouldBe` V "z" +-- it "diverges on evaluating ω" $ +-- shouldDiverge $ +-- evalNeed omega [] + +-- shouldDiverge :: Value -> IO () +-- shouldDiverge t = +-- race_ +-- (t `seq` fail "Did not diverge") +-- (threadDelay 1000000) diff --git a/lambda-calcul/haskell/test/Minilang/REPL/HaskelineSpec.hs b/lambda-calcul/haskell/test/Minilang/REPL/HaskelineSpec.hs new file mode 100644 index 0000000..b7ca5c9 --- /dev/null +++ b/lambda-calcul/haskell/test/Minilang/REPL/HaskelineSpec.hs @@ -0,0 +1,33 @@ +module Minilang.REPL.HaskelineSpec where + +import Minilang.REPL.Haskeline +import System.Console.Haskeline.Completion +import Test.Hspec +import Prelude hiding + ( lines, + readFile, + writeFile, + ) + +spec :: Spec +spec = parallel $ describe "MiniLang Haskeline REPL" $ do + describe "Completion" $ do + it "returns all commands when input ':' " $ do + (fmap replacement) . snd <$> completion (":", "") + `shouldReturn` [":quit", ":env", ":clear", ":load", ":set", ":unset", ":help"] + + it "returns sub-commands when input ':set' " $ do + (fmap replacement) . snd <$> completion ("tes:", "") + `shouldReturn` [":set step", ":set debug"] + + it "returns sub-commands when input ':unset' " $ do + (fmap replacement) . snd <$> completion ("tesnu:", "") + `shouldReturn` [":unset step", ":unset debug"] + + it "returns list of files when input ':load' " $ do + (fmap replacement) . snd <$> completion ("daol:", "") + `shouldReturn` [":load bool.mtt", ":load nelist.mtt", ":load vector.mtt", ":load indrec.mtt"] + + it "returns filtered list of files when input ':load xxx' " $ do + (fmap replacement) . snd <$> completion ("v daol:", "") + `shouldReturn` [":load vector.mtt"] diff --git a/lambda-calcul/haskell/test/Minilang/REPLSpec.hs b/lambda-calcul/haskell/test/Minilang/REPLSpec.hs new file mode 100644 index 0000000..e134810 --- /dev/null +++ b/lambda-calcul/haskell/test/Minilang/REPLSpec.hs @@ -0,0 +1,74 @@ +module Minilang.REPLSpec where + +import Control.Exception (bracket) +import Data.Text.IO +import Minilang.Lambda.Eval (Term (Var)) +import Minilang.REPL +import Minilang.REPL.Purer +import Minilang.REPL.Types +import System.Directory (getTemporaryDirectory, removeFile) +import System.FilePath ((<.>), (</>)) +import System.IO (IOMode (..), hClose, withFile) +import System.Posix.Temp (mkstemp) +import Test.Hspec +import Prelude hiding (lines, readFile, writeFile) + +spec :: Spec +spec = parallel $ + describe "MiniLang REPL" $ do + it "evaluates MiniLang terms read from pure Monad" $ do + let out = + withInput + [ "rec Nat : U = Sum (zero | succ Nat);", + "id : Π A : U . Π _ : A . A = λ A . λ x . x;", + "id Nat (succ (succ (succ zero)))" + ] + + out + `shouldBe` [ "Bye!", + "$succ $succ $succ $zero : Sum(zero| succ Nat)", + "defined id : Π A : U . A → A", + "defined Nat : U" + ] + + it "shows error when trying to load a non-existent file" $ do + withInput [":load foo"] + `shouldBe` [ "Bye!", + "REPLError \"cannot load file foo in Pure interpreter\"" + ] + + around withTempFile $ + it "evaluates single MiniLang term read from IO" $ \fileName -> do + let outputFileName = fileName <> ".out" + + writeFile fileName "rec Nat : U = Sum (zero | succ Nat);\nid : Π A : U . Π _ : A . A = λ A . λ x . x;\nid Nat (succ (succ (succ zero)))" + + _ <- withFile fileName ReadMode $ \hin -> + withFile outputFileName AppendMode $ \hout -> + withHandles hin hout + + out <- readFile outputFileName + + out + `shouldBe` "\955\928> defined Nat : U\n\955\928> defined id : \928 A : U . A \8594 A\n\955\928> succ succ succ zero : Sum(zero| succ Nat)\n\955\928> Bye!\n" + + describe "Purer REPL" $ do + it "keeps handling Inputs even when they fail" $ do + let inputs = + [ In "Unit : U", + In "Unit : U = Sum(tt)", + Com DumpEnv + ] + + env (purerREPL (withInputs initialREPL inputs)) + `shouldBe` [("Unit", Var "x")] + + outputs (withInputs initialREPL inputs) + `shouldContain` [Msg "cannot find \"Unit\" in empty context"] + +withTempFile :: (String -> IO ()) -> IO () +withTempFile = + bracket mkTempFile rmTempFile + where + mkTempFile = getTemporaryDirectory >>= \dir -> mkstemp (dir </> "test-repl") >>= \(fp, h) -> hClose h >> pure fp + rmTempFile fn = removeFile fn >> removeFile (fn <.> "out") |
