lambda-calculator 1.0.0 → 1.1.0
raw patch · 24 files changed
+864/−367 lines, 24 filesdep +hlintdep +optparse-applicativedep ~base
Dependencies added: hlint, optparse-applicative
Dependency ranges changed: base
Files
- app/Main.hs +81/−17
- lambda-calculator.cabal +23/−3
- src/Language/Lambda.hs +5/−6
- src/Language/Lambda/Expression.hs +2/−2
- src/Language/Lambda/Parser.hs +1/−1
- src/Language/Lambda/PrettyPrint.hs +0/−50
- src/Language/Lambda/Util/PrettyPrint.hs +53/−0
- src/Language/SystemF.hs +16/−0
- src/Language/SystemF/Expression.hs +136/−0
- src/Language/SystemF/Parser.hs +86/−0
- test/HLint.hs +16/−0
- test/Language/Lambda/EvalSpec.hs +5/−5
- test/Language/Lambda/Examples/BoolSpec.hs +86/−87
- test/Language/Lambda/Examples/NatSpec.hs +81/−82
- test/Language/Lambda/Examples/PairSpec.hs +27/−28
- test/Language/Lambda/ExpressionSpec.hs +8/−9
- test/Language/Lambda/HspecUtils.hs +4/−1
- test/Language/Lambda/ParserSpec.hs +34/−35
- test/Language/Lambda/PrettyPrintSpec.hs +0/−37
- test/Language/Lambda/Util/PrettyPrintSpec.hs +36/−0
- test/Language/LambdaSpec.hs +4/−4
- test/Language/SystemF/ExpressionSpec.hs +67/−0
- test/Language/SystemF/ParserSpec.hs +84/−0
- test/Language/SystemFSpec.hs +9/−0
app/Main.hs view
@@ -2,41 +2,105 @@ import Data.Version +import Options.Applicative hiding (ParseError) import System.Console.Shell import System.Console.Shell.ShellMonad import System.Console.Shell.Backend.Readline (readlineBackend) +import qualified Paths_lambda_calculator as P+ import Language.Lambda-import Paths_lambda_calculator+import Language.Lambda.Util.PrettyPrint+import Language.SystemF main :: IO ()-main = runShell mkShellDesc readlineBackend ()+main = execParser opts >>= runShell'+ where opts = info (helper <*> cliParser)+ (briefDesc <> progDesc "A Lambda Calculus Interpreter") -mkShellDesc :: ShellDescription ()-mkShellDesc = shellDesc' $ mkShellDescription commands eval+-- Option Parsing+data CliOptions = CliOptions {+ language :: Eval Language,+ version :: Bool+ }++-- Supported Languages:+-- +-- * Untyped Lambda Calculus+-- * System F+data Language + = Untyped+ | SystemF++-- The result of an evaluation+type Result a = Either a -- An error+ a -- The result++-- Represent a language together with its evaluation function+data Eval a = Eval a (String -> Result String)++untyped :: Eval Language+untyped = Eval Untyped eval+ where eval = fromEvalString Language.Lambda.evalString++systemf :: Eval Language+systemf = Eval SystemF eval+ where eval = fromEvalString Language.SystemF.evalString++-- Take a typed evaluation function and return a function that returns a result+-- +-- For example:+-- (String -> Either ParseError (LambdaExpr String)) -> (String -> Result String)+-- (String -> Either ParseError (SystemFExpr String String)) -> (String -> Result String)+fromEvalString :: (Show s, PrettyPrint p)+ => (String -> Either s p)+ -> (String -> Result String)+fromEvalString f = either (Left . show) (Right . prettyPrint) . f++cliParser :: Parser CliOptions+cliParser = CliOptions + <$> flag untyped systemf (long "system-f" <> + short 'f' <> + internal <> -- this is a secret feature+ help "Use the System F interpreter")++ <*> switch (long "version" <> + short 'v' <> + help "Print the version")++-- Interactive Shell+runShell' :: CliOptions -> IO ()+runShell' CliOptions{version=True} = putStrLn version'+runShell' CliOptions{language=Eval lang eval} + = runShell (mkShellDesc lang eval) readlineBackend ()++mkShellDesc :: Language + -> (String -> Result String)+ -> ShellDescription ()+mkShellDesc language f = shellDesc' $ mkShellDescription commands (eval f) where shellDesc' d = d { greetingText = Just shellGreeting,- prompt = shellPrompt+ prompt = shellPrompt language } shellGreeting :: String shellGreeting = "Lambda Calculator (" ++ version' ++ ")\nType :h for help\n" -shellPrompt :: s -> IO String-shellPrompt _ = return "λ > "+shellPrompt :: Language -> s -> IO String+shellPrompt language _ = return $ prefix language : " > "+ where prefix Untyped = lambda+ prefix SystemF = upperLambda commands :: [ShellCommand s]-commands = [exitCommand "q",- helpCommand "h"]--eval :: String -> Sh s ()-eval = either shellPutErrLn' shellPutStrLn' . evalString- where shellPutErrLn' :: Show s => s -> Sh s' ()- shellPutErrLn' = shellPutErrLn . show+commands = [+ exitCommand "q",+ helpCommand "h"+ ] - shellPutStrLn' :: PrettyPrint s => s -> Sh s' ()- shellPutStrLn' = shellPutStrLn . prettyPrint+eval :: (String -> Result String) -> String -> Sh s' ()+eval f = either shellPutErrLn shellPutStrLn . f +-- Get the current version version' :: String-version' = showVersion version+version' = showVersion P.version
lambda-calculator.cabal view
@@ -1,5 +1,5 @@ name: lambda-calculator-version: 1.0.0+version: 1.1.0 synopsis: A lambda calculus interpreter description: Please see README.md homepage: https://github.com/sgillespie/lambda-calculus#readme@@ -19,7 +19,12 @@ Language.Lambda.Expression, Language.Lambda.Eval, Language.Lambda.Parser,- Language.Lambda.PrettyPrint++ Language.Lambda.Util.PrettyPrint,++ Language.SystemF,+ Language.SystemF.Expression,+ Language.SystemF.Parser build-depends: base <= 5, parsec default-language: Haskell2010@@ -31,6 +36,7 @@ ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: base, lambda-calculator,+ optparse-applicative, Shellac, Shellac-readline default-language: Haskell2010@@ -47,11 +53,25 @@ Language.Lambda.EvalSpec, Language.Lambda.HspecUtils, Language.Lambda.ParserSpec,- Language.Lambda.PrettyPrintSpec++ Language.Lambda.Util.PrettyPrintSpec,++ Language.SystemFSpec,+ Language.SystemF.ExpressionSpec,+ Language.SystemF.ParserSpec build-depends: base <= 5, lambda-calculator, hspec, HUnit+ ghc-options: -threaded -rtsopts -with-rtsopts=-N+ default-language: Haskell2010++test-suite lambda-calculus-lint+ type: exitcode-stdio-1.0+ hs-source-dirs: test+ main-is: HLint.hs+ build-depends: base <= 5,+ hlint ghc-options: -threaded -rtsopts -with-rtsopts=-N default-language: Haskell2010
src/Language/Lambda.hs view
@@ -1,5 +1,7 @@+{-# LANGUAGE FlexibleInstances #-} module Language.Lambda ( LambdaExpr(..),+ ParseError(..), PrettyPrint(..), evalExpr, evalString,@@ -13,15 +15,12 @@ import Language.Lambda.Eval import Language.Lambda.Expression import Language.Lambda.Parser-import Language.Lambda.PrettyPrint+import Language.Lambda.Util.PrettyPrint evalString :: String -> Either ParseError (LambdaExpr String)-evalString = liftM (evalExpr uniques) . parseExpr+evalString = fmap (evalExpr uniques) . parseExpr --- TODO[sgillespie]: Uniques should be [a..z, a0..z0, a1..z1] etc--- concatMap (\x -> map (\y -> y:x) ['a'..'z']) ([""] ++ map show [0..])- uniques :: [String] uniques = concatMap (\p -> map (:p) . reverse $ ['a'..'z']) suffix- where suffix = [""] ++ map show [(0::Int)..]+ where suffix = "" : map show [(0::Int)..]
src/Language/Lambda/Expression.hs view
@@ -3,7 +3,7 @@ import Prelude hiding (abs, uncurry) -import Language.Lambda.PrettyPrint+import Language.Lambda.Util.PrettyPrint data LambdaExpr name = Var name@@ -46,6 +46,6 @@ = pprExpr pdoc e1 `mappend` addSpace (pprExpr pdoc e2) uncurry :: n -> LambdaExpr n -> ([n], LambdaExpr n)-uncurry n body = uncurry' [n] body+uncurry n = uncurry' [n] where uncurry' ns (Abs n' body') = uncurry' (n':ns) body' uncurry' ns body' = (reverse ns, body')
src/Language/Lambda/Parser.hs view
@@ -22,7 +22,7 @@ abs :: Parser (LambdaExpr String) abs = curry <$> idents <*> expr- where idents = (symbol '\\') *> many1 identifier <* (symbol '.')+ where idents = symbol '\\' *> many1 identifier <* symbol '.' curry = flip (foldr Abs) app :: Parser (LambdaExpr String)
− src/Language/Lambda/PrettyPrint.hs
@@ -1,50 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-module Language.Lambda.PrettyPrint where--import qualified Data.List as L--class PrettyPrint a where- prettyPrint :: a -> String--instance PrettyPrint String where- prettyPrint = id- -newtype PDoc s = PDoc [s]- deriving (Eq, Show)--instance PrettyPrint s => PrettyPrint (PDoc s) where- prettyPrint (PDoc ls) = concat . map prettyPrint $ ls--instance Monoid (PDoc s) where- mempty = empty- (PDoc p1) `mappend` (PDoc p2) = PDoc $ p1 ++ p2--instance Functor PDoc where- fmap f (PDoc ls) = PDoc (fmap f ls)--empty :: PDoc s-empty = PDoc []--add :: s -> PDoc s -> PDoc s-add s (PDoc ps) = PDoc (s:ps)--append :: [s] -> PDoc s -> PDoc s-append = mappend . PDoc--between :: PDoc s -> s -> s -> PDoc s -> PDoc s-between (PDoc str) start end pdoc = PDoc ((start:str) ++ [end]) `mappend` pdoc--betweenParens :: PDoc String -> PDoc String -> PDoc String-betweenParens doc = between doc "(" ")"--intercalate :: [[s]] -> [s] -> PDoc [s] -> PDoc [s]-intercalate ss sep = add $ L.intercalate sep ss--addSpace :: PDoc String -> PDoc String-addSpace = add [space]- -space :: Char-space = ' '--lambda :: Char-lambda = 'λ'
+ src/Language/Lambda/Util/PrettyPrint.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE FlexibleInstances #-}+module Language.Lambda.Util.PrettyPrint where++import qualified Data.List as L++class PrettyPrint a where+ prettyPrint :: a -> String++instance PrettyPrint String where+ prettyPrint = id+ +newtype PDoc s = PDoc [s]+ deriving (Eq, Show)++instance PrettyPrint s => PrettyPrint (PDoc s) where+ prettyPrint (PDoc ls) = concatMap prettyPrint ls++instance Monoid (PDoc s) where+ mempty = empty+ (PDoc p1) `mappend` (PDoc p2) = PDoc $ p1 ++ p2++instance Functor PDoc where+ fmap f (PDoc ls) = PDoc (fmap f ls)++empty :: PDoc s+empty = PDoc []++add :: s -> PDoc s -> PDoc s+add s (PDoc ps) = PDoc (s:ps)++append :: [s] -> PDoc s -> PDoc s+append = mappend . PDoc++between :: PDoc s -> s -> s -> PDoc s -> PDoc s+between (PDoc str) start end pdoc = PDoc ((start:str) ++ [end]) `mappend` pdoc++betweenParens :: PDoc String -> PDoc String -> PDoc String+betweenParens doc = between doc "(" ")"++intercalate :: [[s]] -> [s] -> PDoc [s] -> PDoc [s]+intercalate ss sep = add $ L.intercalate sep ss++addSpace :: PDoc String -> PDoc String+addSpace = add [space]+ +space :: Char+space = ' '++lambda :: Char+lambda = 'λ'++upperLambda :: Char+upperLambda = 'Λ'
+ src/Language/SystemF.hs view
@@ -0,0 +1,16 @@+module Language.SystemF (+ PrettyPrint(..),+ SystemFExpr(..),+ evalString,+ parseExpr+ ) where++import Text.Parsec++import Language.Lambda.Util.PrettyPrint+import Language.SystemF.Expression+import Language.SystemF.Parser++evalString :: String -> Either ParseError (SystemFExpr String String)+evalString = parseExpr+
+ src/Language/SystemF/Expression.hs view
@@ -0,0 +1,136 @@+{-# LANGUAGE FlexibleInstances #-}+module Language.SystemF.Expression where++import Data.Monoid++import Language.Lambda.Util.PrettyPrint++data SystemFExpr name ty+ = Var name -- Variable+ | App (SystemFExpr name ty) (SystemFExpr name ty) -- Application+ | Abs name (Ty ty) (SystemFExpr name ty) -- Abstraction+ | TyAbs ty (SystemFExpr name ty) -- Type Abstraction+ -- \X. body++ | TyApp (SystemFExpr name ty) (Ty ty) -- Type Application+ -- x [X]+ deriving (Eq, Show)++data Ty name+ = TyVar name -- Type variable (T)+ | TyArrow (Ty name) (Ty name) -- Type arrow (T -> U)+ deriving (Eq, Show)++-- Pretty printing+instance (PrettyPrint n, PrettyPrint t) => PrettyPrint (SystemFExpr n t) where+ prettyPrint = prettyPrint . pprExpr empty++instance PrettyPrint n => PrettyPrint (Ty n) where+ prettyPrint = prettyPrint . pprTy empty True++-- Same as prettyPrint, but we assume the same type for names and types. Useful+-- for testing.+prettyPrint' :: PrettyPrint n => SystemFExpr n n -> String+prettyPrint' = prettyPrint++-- Pretty print a system f expression+pprExpr :: (PrettyPrint n, PrettyPrint t) + => PDoc String + -> SystemFExpr n t+ -> PDoc String+pprExpr pdoc (Var n) = prettyPrint n `add` pdoc+pprExpr pdoc (App e1 e2) = pprApp pdoc e1 e2+pprExpr pdoc (Abs n t body) = pprAbs pdoc n t body+pprExpr pdoc (TyAbs t body) = pprTAbs pdoc t body+pprExpr pdoc (TyApp e ty) = pprTApp pdoc e ty++-- Pretty print an application+pprApp :: (PrettyPrint n, PrettyPrint t)+ => PDoc String+ -> SystemFExpr n t+ -> SystemFExpr n t+ -> PDoc String+pprApp pdoc e1@Abs{} e2@Abs{} = betweenParens (pprExpr pdoc e1) pdoc+ `mappend` addSpace (betweenParens (pprExpr pdoc e2) pdoc)+pprApp pdoc e1 e2@App{} = pprExpr pdoc e1+ `mappend` addSpace (betweenParens (pprExpr pdoc e2) pdoc)+pprApp pdoc e1 e2@Abs{} = pprExpr pdoc e1+ `mappend` addSpace (betweenParens (pprExpr pdoc e2) pdoc)+pprApp pdoc e1@Abs{} e2 = betweenParens (pprExpr pdoc e1) pdoc+ `mappend` addSpace (pprExpr pdoc e2)+pprApp pdoc e1 e2+ = pprExpr pdoc e1 `mappend` addSpace (pprExpr pdoc e2)++pprTApp :: (PrettyPrint n, PrettyPrint t)+ => PDoc String+ -> SystemFExpr n t+ -> Ty t+ -> PDoc String+pprTApp pdoc expr ty = expr' `mappend` addSpace (between ty' "[" "]" empty)+ where expr' = pprExpr pdoc expr+ ty' = add (prettyPrint ty) empty++-- Pretty print an abstraction+pprAbs :: (PrettyPrint n, PrettyPrint t)+ => PDoc String+ -> n+ -> Ty t+ -> SystemFExpr n t+ -> PDoc String+pprAbs pdoc name ty body = between vars' lambda' ". " (pprExpr pdoc body')+ where (vars, body') = uncurryAbs name ty body+ vars' = intercalate (map (uncurry pprArg) vars) " " empty+ lambda' = [lambda, space]++ pprArg n t = prettyPrint n ++ (':':pprArg' t)+ pprArg' t@(TyVar _) = prettyPrint t+ pprArg' t@(TyArrow _ _) = prettyPrint $ betweenParens (pprTy empty False t) empty++-- Pretty print types+pprTy :: PrettyPrint n+ => PDoc String+ -> Bool -- Add a space between arrows?+ -> Ty n+ -> PDoc String+pprTy pdoc space (TyVar n) = prettyPrint n `add` pdoc+pprTy pdoc space (TyArrow a b) = pprTyArrow pdoc space a b++pprTyArrow :: PrettyPrint n+ => PDoc String+ -> Bool -- Add a space between arrows?+ -> Ty n+ -> Ty n+ -> PDoc String+pprTyArrow pdoc space a@(TyVar _) b = pprTyArrow' space (pprTy pdoc space a) + (pprTy pdoc space b)+pprTyArrow pdoc space (TyArrow a1 a2) b = pprTyArrow' space a' (pprTy pdoc space b)+ where a' = betweenParens (pprTyArrow pdoc space a1 a2) empty++pprTyArrow' :: Bool -- Add a space between arrows?+ -> PDoc String+ -> PDoc String+ -> PDoc String+pprTyArrow' space a b = a <> arrow <> b+ where arrow | space = " -> " `add` empty+ | otherwise = "->" `add` empty++-- Pretty print a type abstraction+pprTAbs :: (PrettyPrint n, PrettyPrint t)+ => PDoc String+ -> t+ -> SystemFExpr n t+ -> PDoc String+pprTAbs pdoc ty body = between vars' lambda' ". " (pprExpr pdoc body')+ where (vars, body') = uncurryTAbs ty body+ vars' = intercalate (map prettyPrint vars) " " empty+ lambda' = [upperLambda, space]++uncurryAbs :: n -> Ty t -> SystemFExpr n t -> ([(n, Ty t)], SystemFExpr n t)+uncurryAbs name ty = uncurry' [(name, ty)] + where uncurry' ns (Abs n' t' body') = uncurry' ((n', t'):ns) body'+ uncurry' ns body' = (reverse ns, body')++uncurryTAbs :: t -> SystemFExpr n t -> ([t], SystemFExpr n t)+uncurryTAbs ty = uncurry' [ty]+ where uncurry' ts (TyAbs t' body') = uncurry' (t':ts) body'+ uncurry' ts body' = (reverse ts, body')
+ src/Language/SystemF/Parser.hs view
@@ -0,0 +1,86 @@+module Language.SystemF.Parser (+ parseExpr,+ parseType+ ) where++import Control.Monad+import Prelude hiding (abs)++import Text.Parsec+import Text.Parsec.String++import Language.SystemF.Expression++parseExpr :: String -> Either ParseError (SystemFExpr String String)+parseExpr = parse (whitespace *> expr <* eof) ""++parseType :: String -> Either ParseError (Ty String)+parseType = parse (whitespace *> ty <* eof) ""++-- Parse expressions+expr :: Parser (SystemFExpr String String)+expr = try tyapp <|> try app <|> term++app :: Parser (SystemFExpr String String)+app = chainl1 term (return App)++tyapp :: Parser (SystemFExpr String String)+tyapp = TyApp+ <$> term+ <*> ty'+ where ty' = symbol '[' *> ty <* symbol ']'++term :: Parser (SystemFExpr String String)+term = try abs <|> tyabs <|> var <|> parens expr++var :: Parser (SystemFExpr String String)+var = Var <$> exprId++abs :: Parser (SystemFExpr String String)+abs = curry + <$> (symbol '\\' *> many1 args <* symbol '.') + <*> expr+ where args = (,) <$> (exprId <* symbol ':') <*> ty+ curry = flip . foldr . uncurry $ Abs++tyabs :: Parser (SystemFExpr String String)+tyabs = curry <$> args <*> expr+ where args = symbol '\\' *> many1 typeId <* symbol '.'+ curry = flip (foldr TyAbs)++-- Parse type expressions+ty :: Parser (Ty String)+ty = try arrow++arrow :: Parser (Ty String)+arrow = chainr1 tyterm (symbol' "->" *> return TyArrow)++tyterm :: Parser (Ty String)+tyterm = tyvar <|> parens ty++tyvar :: Parser (Ty String)+tyvar = TyVar <$> typeId++parens :: Parser a -> Parser a+parens p = symbol '(' *> p <* symbol ')'++identifier :: Parser Char -> Parser String+identifier firstChar = lexeme ((:) <$> first <*> many rest)+ where first = firstChar <|> char '_'+ rest = first <|> digit++typeId, exprId :: Parser String+typeId = identifier upper+exprId = identifier lower++whitespace :: Parser ()+whitespace = void . many . oneOf $ " \t"++symbol :: Char -> Parser ()+symbol = void . lexeme . char++symbol' :: String -> Parser ()+symbol' = void . lexeme . string++lexeme :: Parser a -> Parser a+lexeme p = p <* whitespace
+ test/HLint.hs view
@@ -0,0 +1,16 @@+module Main (main) where++import Language.Haskell.HLint (hlint)+import System.Exit (exitFailure, exitSuccess)++arguments :: [String]+arguments = [ + "app",+ "src",+ "test"+ ]++main :: IO ()+main = hlint arguments >>= main'+ where main' [] = exitSuccess+ main' _ = exitFailure
test/Language/Lambda/EvalSpec.hs view
@@ -33,7 +33,7 @@ it "reduces simple applications" $ do let e1 = Abs "x" (Var "x")- e2 = (Var "y")+ e2 = Var "y" betaReduce' e1 e2 `shouldBe` Var "y" it "reduces nested abstractions" $ do@@ -97,14 +97,14 @@ etaConvert expr `shouldBe` expr describe "freeVarsOf" $ do- it "Returns simple vars" $ do+ it "Returns simple vars" $ freeVarsOf (Var "x") `shouldBe` ["x"] - it "Does not return bound vars" $ do+ it "Does not return bound vars" $ freeVarsOf (Abs "x" (Var "x")) `shouldBe` [] - it "Returns nested simple vars" $ do+ it "Returns nested simple vars" $ freeVarsOf (Abs "x" (Var "y")) `shouldBe` ["y"] - it "Returns applied simple vars" $ do+ it "Returns applied simple vars" $ freeVarsOf (App (Var "x") (Var "y")) `shouldBe` ["x", "y"]
test/Language/Lambda/Examples/BoolSpec.hs view
@@ -5,105 +5,104 @@ import Language.Lambda.HspecUtils spec :: Spec-spec = do- describe "Bool" $ do- -- Bool is the definition of Booleans. We represent bools- -- using Church Encodings:+spec = describe "Bool" $ do+ -- Bool is the definition of Booleans. We represent bools+ -- using Church Encodings:+ --+ -- true: \t f. t+ -- false: \t f. f+ describe "and" $ do+ -- The function and takes two Bools and returns true+ -- iff both arguments are true+ -- + -- and(true, true) = true+ -- and(false, true) = false+ -- and(true, false) = false+ -- and(false, false) = false --- -- true: \t f. t- -- false: \t f. f- describe "and" $ do- -- The function and takes two Bools and returns true- -- iff both arguments are true- -- - -- and(true, true) = true- -- and(false, true) = false- -- and(true, false) = false- -- and(false, false) = false- --- -- and is defined by- -- and = \x y. x y x- it "true and true = true" $ do- "(\\x y. x y x) (\\t f. t) (\\t f. t)" `shouldEvalTo` "\\t f. t"+ -- and is defined by+ -- and = \x y. x y x+ it "true and true = true" $+ "(\\x y. x y x) (\\t f. t) (\\t f. t)" `shouldEvalTo` "\\t f. t" - it "true and false = false" $ do- "(\\x y. x y x) (\\t f. t) (\\t f. f)" `shouldEvalTo` "\\t f. f"- - it "false and true = false" $ do- "(\\x y. x y x) (\\t f. f) (\\t f. t)" `shouldEvalTo` "\\t f. f"+ it "true and false = false" $+ "(\\x y. x y x) (\\t f. t) (\\t f. f)" `shouldEvalTo` "\\t f. f"+ + it "false and true = false" $+ "(\\x y. x y x) (\\t f. f) (\\t f. t)" `shouldEvalTo` "\\t f. f" - it "false and false = false" $ do- "(\\x y. x y x) (\\t f. f) (\\t f. f)" `shouldEvalTo` "\\t f. f"+ it "false and false = false" $+ "(\\x y. x y x) (\\t f. f) (\\t f. f)" `shouldEvalTo` "\\t f. f" - it "false and p = false" $ do- "(\\x y. x y x) (\\t f. f) p" `shouldEvalTo` "\\t f. f"+ it "false and p = false" $+ "(\\x y. x y x) (\\t f. f) p" `shouldEvalTo` "\\t f. f" - it "true and p = false" $ do- "(\\x y. x y x) (\\t f. t) p" `shouldEvalTo` "p"+ it "true and p = false" $+ "(\\x y. x y x) (\\t f. t) p" `shouldEvalTo` "p" - describe "or" $ do- -- or takes two Bools and returns true iff either argument is true- -- - -- or(true, true) = true- -- or(true, false) = true- -- or(false, true) = true- -- or(false, false) = false- --- -- or is defined by- -- or = \x y. x x y- it "true or true = true" $ do- "(\\x y. x x y) (\\t f. t) (\\t f. t)" `shouldEvalTo` "\\t f. t"+ describe "or" $ do+ -- or takes two Bools and returns true iff either argument is true+ -- + -- or(true, true) = true+ -- or(true, false) = true+ -- or(false, true) = true+ -- or(false, false) = false+ --+ -- or is defined by+ -- or = \x y. x x y+ it "true or true = true" $+ "(\\x y. x x y) (\\t f. t) (\\t f. t)" `shouldEvalTo` "\\t f. t"+ + it "true or false = true" $+ "(\\x y. x x y) (\\t f. t) (\\t f. f)" `shouldEvalTo` "\\t f. t" - it "true or false = true" $ do- "(\\x y. x x y) (\\t f. t) (\\t f. f)" `shouldEvalTo` "\\t f. t"- - it "false or true = true" $ do- "(\\x y. x x y) (\\t f. f) (\\t f. t)" `shouldEvalTo` "\\t f. t"+ it "false or true = true" $+ "(\\x y. x x y) (\\t f. f) (\\t f. t)" `shouldEvalTo` "\\t f. t" - it "false or false = false" $ do- "(\\x y. x x y) (\\t f. f) (\\t f. f)" `shouldEvalTo` "\\t f. f"+ it "false or false = false" $+ "(\\x y. x x y) (\\t f. f) (\\t f. f)" `shouldEvalTo` "\\t f. f" - it "true or p = true" $ do- "(\\x y. x x y) (\\t f. t) p" `shouldEvalTo` "\\t f. t"+ it "true or p = true" $+ "(\\x y. x x y) (\\t f. t) p" `shouldEvalTo` "\\t f. t" - it "false or p = p" $ do- "(\\x y. x x y) (\\t f. f) p" `shouldEvalTo` "p"- + it "false or p = p" $+ "(\\x y. x x y) (\\t f. f) p" `shouldEvalTo` "p"+ - describe "not" $ do- -- not takes a Bool and returns its opposite value- --- -- not(true) = false- -- not(false) = true- --- -- not is defined by- -- not = \x. x (\t f. f) (\t f. t)- it "not true = false" $ do- "(\\x. x (\\t f. f) (\\t f. t)) \\t f. t" `shouldEvalTo` "\\t f. f"+ describe "not" $ do+ -- not takes a Bool and returns its opposite value+ --+ -- not(true) = false+ -- not(false) = true+ --+ -- not is defined by+ -- not = \x. x (\t f. f) (\t f. t)+ it "not true = false" $+ "(\\x. x (\\t f. f) (\\t f. t)) \\t f. t" `shouldEvalTo` "\\t f. f" - it "not false = true"$ do- "(\\x. x (\\t f. f) (\\t f. t)) \\t f. f" `shouldEvalTo` "\\t f. t"- - describe "if" $ do- -- if takes a Bool and two values. If returns the first value- -- if the Bool is true, and the second otherwise. In other words,- -- if p x y = if p then x else y- --- -- if(true, x, y) = x- -- if(false, x, y) = y- -- - -- if is defined by- -- if = \p x y. p x y- it "if true 0 1 = 0" $ do- "(\\p x y. p x y) (\\t f. t) (\\f x. x) (\\f x. f x)"- `shouldEvalTo` "\\f x. x"+ it "not false = true" $+ "(\\x. x (\\t f. f) (\\t f. t)) \\t f. f" `shouldEvalTo` "\\t f. t"+ + describe "if" $ do+ -- if takes a Bool and two values. If returns the first value+ -- if the Bool is true, and the second otherwise. In other words,+ -- if p x y = if p then x else y+ --+ -- if(true, x, y) = x+ -- if(false, x, y) = y+ -- + -- if is defined by+ -- if = \p x y. p x y+ it "if true 0 1 = 0" $+ "(\\p x y. p x y) (\\t f. t) (\\f x. x) (\\f x. f x)"+ `shouldEvalTo` "\\f x. x" - it "if false 0 1 = 1" $ do- "(\\p x y. p x y) (\\t f. f) (\\f x. x) (\\f x. f x)"- `shouldEvalTo` "\\f x. f x"+ it "if false 0 1 = 1" $+ "(\\p x y. p x y) (\\t f. f) (\\f x. x) (\\f x. f x)"+ `shouldEvalTo` "\\f x. f x" - it "it true p q = p" $ do- "(\\p x y. p x y) (\\t f. t) p q" `shouldEvalTo` "p"+ it "it true p q = p" $+ "(\\p x y. p x y) (\\t f. t) p q" `shouldEvalTo` "p" - it "it false p q = q" $ do- "(\\p x y. p x y) (\\t f. f) p q" `shouldEvalTo` "q"+ it "it false p q = q" $+ "(\\p x y. p x y) (\\t f. f) p q" `shouldEvalTo` "q"
test/Language/Lambda/Examples/NatSpec.hs view
@@ -5,99 +5,98 @@ import Language.Lambda.HspecUtils spec :: Spec-spec = do- describe "Nat" $ do- -- Nat is the definition of natural numbers. More precisely, Nat- -- is the set of nonnegative integers. We represent nats using- -- Church Encodings:- --- -- 0: \f x. x- -- 1: \f x. f x- -- 2: \f x. f (f x)- -- ...and so on+spec = describe "Nat" $ do+ -- Nat is the definition of natural numbers. More precisely, Nat+ -- is the set of nonnegative integers. We represent nats using+ -- Church Encodings:+ --+ -- 0: \f x. x+ -- 1: \f x. f x+ -- 2: \f x. f (f x)+ -- ...and so on - describe "successor" $ do- -- successor is a function that adds 1- -- succ(0) = 1- -- succ(1) = 2- -- ... and so forth- --- -- successor is defined by- -- succ = \n f x. f (n f x)- it "succ 0 = 1" $ do- "(\\n f x. f (n f x)) (\\f x. x)" `shouldEvalTo` "\\f x. f x"+ describe "successor" $ do+ -- successor is a function that adds 1+ -- succ(0) = 1+ -- succ(1) = 2+ -- ... and so forth+ --+ -- successor is defined by+ -- succ = \n f x. f (n f x)+ it "succ 0 = 1" $+ "(\\n f x. f (n f x)) (\\f x. x)" `shouldEvalTo` "\\f x. f x" - it "succ 1 = 2" $ do- "(\\n f x. f (n f x)) (\\f x. f x)" `shouldEvalTo` "\\f x. f (f x)"+ it "succ 1 = 2" $+ "(\\n f x. f (n f x)) (\\f x. f x)" `shouldEvalTo` "\\f x. f (f x)" - describe "add" $ do- -- add(m, n) = m + n- --- -- It is defined by applying successor m times on n:- -- add = \m n f x. m f (n f x)- it "add 0 2 = 2" $ do- "(\\m n f x. m f (n f x)) (\\f x. x) (\\f x. f (f x))"- `shouldEvalTo` "\\f x. f (f x)"+ describe "add" $ do+ -- add(m, n) = m + n+ --+ -- It is defined by applying successor m times on n:+ -- add = \m n f x. m f (n f x)+ it "add 0 2 = 2" $+ "(\\m n f x. m f (n f x)) (\\f x. x) (\\f x. f (f x))"+ `shouldEvalTo` "\\f x. f (f x)" - it "add 3 2 = 5" $ do- "(\\m n f x. m f (n f x)) (\\f x. f (f (f x))) (\\f x. f (f x))"- `shouldEvalTo` "\\f x. f (f (f (f (f x))))"+ it "add 3 2 = 5" $+ "(\\m n f x. m f (n f x)) (\\f x. f (f (f x))) (\\f x. f (f x))"+ `shouldEvalTo` "\\f x. f (f (f (f (f x))))" - -- Here, we use `\f x. n f x` instead of `n`. This is because- -- I haven't implemented eta conversion- it "add 0 n = n" $ do- "(\\m n f x. m f (n f x)) (\\f x. x) n"- `shouldEvalTo` "\\f x. n f x"+ -- Here, we use `\f x. n f x` instead of `n`. This is because+ -- I haven't implemented eta conversion+ it "add 0 n = n" $+ "(\\m n f x. m f (n f x)) (\\f x. x) n"+ `shouldEvalTo` "\\f x. n f x" - describe "multiply" $ do- -- multiply(m, n) = m * n- --- -- multiply is defined by applying add m times- -- multiply = \m n f x. m (n f x) x)- --- -- Using eta conversion, we can omit the parameter x- -- multiply = \m n f. m (n f)- it "multiply 0 2 = 0" $ do- "(\\m n f. m (n f)) (\\f x. x) (\\f x. f (f x))"- `shouldEvalTo` "\\f x. x"+ describe "multiply" $ do+ -- multiply(m, n) = m * n+ --+ -- multiply is defined by applying add m times+ -- multiply = \m n f x. m (n f x) x)+ --+ -- Using eta conversion, we can omit the parameter x+ -- multiply = \m n f. m (n f)+ it "multiply 0 2 = 0" $+ "(\\m n f. m (n f)) (\\f x. x) (\\f x. f (f x))"+ `shouldEvalTo` "\\f x. x" - it "multiply 2 3 = 6" $ do- "(\\m n f. m (n f)) (\\f x. f (f x)) (\\f x. f (f (f x)))"- `shouldEvalTo` "\\f x. f (f (f (f (f (f x)))))"+ it "multiply 2 3 = 6" $+ "(\\m n f. m (n f)) (\\f x. f (f x)) (\\f x. f (f (f x)))"+ `shouldEvalTo` "\\f x. f (f (f (f (f (f x)))))" - it "multiply 0 n = 0" $ do- "(\\m n f. m (n f)) (\\f x. x) n"- `shouldEvalTo` "\\f x. x"+ it "multiply 0 n = 0" $+ "(\\m n f. m (n f)) (\\f x. x) n"+ `shouldEvalTo` "\\f x. x" - it "multiply 1 n = n" $ do- "(\\m n f. m (n f)) (\\f x. f x) n"- `shouldEvalTo` "\\f x. n f x"+ it "multiply 1 n = n" $+ "(\\m n f. m (n f)) (\\f x. f x) n"+ `shouldEvalTo` "\\f x. n f x" - describe "power" $ do- -- The function power raises m to the power of n.- -- power(m, n) = m^n- --- -- power is defined by applying multiply n times- -- power = \m n f x. (n m) f x- --- -- Using eta conversion again, we can omit the parameter f- -- power = \m n = n m+ describe "power" $ do+ -- The function power raises m to the power of n.+ -- power(m, n) = m^n+ --+ -- power is defined by applying multiply n times+ -- power = \m n f x. (n m) f x+ --+ -- Using eta conversion again, we can omit the parameter f+ -- power = \m n = n m - -- NOTE: Here we use the first form to get more predictable- -- variable names. Otherwise, alpha conversion will choose a random- -- unique variable.- it "power 0 1 = 0" $ do- "(\\m n f x. (n m) f x) (\\f x. x) (\\f x. f x)"- `shouldEvalTo` "\\f x. x"+ -- NOTE: Here we use the first form to get more predictable+ -- variable names. Otherwise, alpha conversion will choose a random+ -- unique variable.+ it "power 0 1 = 0" $+ "(\\m n f x. (n m) f x) (\\f x. x) (\\f x. f x)"+ `shouldEvalTo` "\\f x. x" - it "power 2 3 = 8" $ do- "(\\m n f x. (n m) f x) (\\f x. f (f x)) (\\f x. f (f (f x)))"- `shouldEvalTo` "\\f x. f (f (f (f (f (f (f (f x)))))))"+ it "power 2 3 = 8" $+ "(\\m n f x. (n m) f x) (\\f x. f (f x)) (\\f x. f (f (f x)))"+ `shouldEvalTo` "\\f x. f (f (f (f (f (f (f (f x)))))))" - it "power n 0 = 1" $ do- "(\\m n f x. (n m) f x) n (\\f x. x)"- `shouldEvalTo` "\\f x. f x"+ it "power n 0 = 1" $+ "(\\m n f x. (n m) f x) n (\\f x. x)"+ `shouldEvalTo` "\\f x. f x" - it "power n 1 = n" $ do- "(\\m n f x. (n m) f x) n (\\f x. f x)"- `shouldEvalTo` "\\f x. n f x"+ it "power n 1 = n" $+ "(\\m n f x. (n m) f x) n (\\f x. f x)"+ `shouldEvalTo` "\\f x. n f x"
test/Language/Lambda/Examples/PairSpec.hs view
@@ -5,35 +5,34 @@ import Test.Hspec spec :: Spec-spec = do- describe "Pair" $ do- -- Pair is the definition of tuples with two items. Pairs,- -- again are represented using Church Encodings:+spec = describe "Pair" $ do+ -- Pair is the definition of tuples with two items. Pairs,+ -- again are represented using Church Encodings:+ --+ -- pair = \x y f. f x y+ describe "first" $ do+ -- The function first returns the first item in a pair+ -- first(x, y) = x --- -- pair = \x y f. f x y- describe "first" $ do- -- The function first returns the first item in a pair- -- first(x, y) = x- --- -- first is defined by- -- first = \p. p (\t f. t)- it "first 0 1 = 0" $ do- "(\\p. p (\\t f. t)) ((\\x y f. f x y) (\\f x. x) (\\f x. f x))"- `shouldEvalTo` "\\f x. x"+ -- first is defined by+ -- first = \p. p (\t f. t)+ it "first 0 1 = 0" $+ "(\\p. p (\\t f. t)) ((\\x y f. f x y) (\\f x. x) (\\f x. f x))"+ `shouldEvalTo` "\\f x. x" - it "first x y = x" $ do- "(\\p. p (\\t f. t)) ((\\x y f. f x y) x y)" `shouldEvalTo` "x"+ it "first x y = x" $+ "(\\p. p (\\t f. t)) ((\\x y f. f x y) x y)" `shouldEvalTo` "x" - describe "second" $ do- -- The function second returns the second item in a pair- -- second(x, y) = y- --- -- second is defined by- -- second = \p. p (\t f. f)- it "second 0 1 = 1" $ do- "(\\p. p (\\t f. f)) ((\\x y f. f x y) (\\f x. x) (\\f x. f x))"- `shouldEvalTo` "\\f x. f x"+ describe "second" $ do+ -- The function second returns the second item in a pair+ -- second(x, y) = y+ --+ -- second is defined by+ -- second = \p. p (\t f. f)+ it "second 0 1 = 1" $+ "(\\p. p (\\t f. f)) ((\\x y f. f x y) (\\f x. x) (\\f x. f x))"+ `shouldEvalTo` "\\f x. f x" - it "second x y = y" $ do- "(\\p. p (\\t f. f)) ((\\x y f. f x y) x y)" `shouldEvalTo` "y"- "(\\p. p (\\x y z. x)) ((\\x y z f. f x y z) x y z)" `shouldEvalTo` "x"+ it "second x y = y" $ do+ "(\\p. p (\\t f. f)) ((\\x y f. f x y) x y)" `shouldEvalTo` "y"+ "(\\p. p (\\x y z. x)) ((\\x y z f. f x y z) x y z)" `shouldEvalTo` "x"
test/Language/Lambda/ExpressionSpec.hs view
@@ -3,26 +3,25 @@ import Test.Hspec import Language.Lambda.Expression-import Language.Lambda.PrettyPrint+import Language.Lambda.Util.PrettyPrint spec :: Spec-spec = do- describe "prettyPrint" $ do- it "prints simple variables" $ do+spec = describe "prettyPrint" $ do+ it "prints simple variables" $ prettyPrint (Var "x") `shouldBe` "x" - it "prints simple abstractions" $ do+ it "prints simple abstractions" $ prettyPrint (Abs "x" (Var "x")) `shouldBe` "λx. x" - it "prints simple applications" $ do+ it "prints simple applications" $ prettyPrint (App (Var "a") (Var "b")) `shouldBe` "a b" - it "prints nested applications" $ do- prettyPrint (Abs "f" (Abs "x" (Var"x")))+ it "prints nested abstractions" $+ prettyPrint (Abs "f" (Abs "x" (Var "x"))) `shouldBe` "λf x. x" - it "prints nested applications" $ do+ it "prints nested applications" $ prettyPrint (App (App (Var "f") (Var "x")) (Var "y")) `shouldBe` "f x y"
test/Language/Lambda/HspecUtils.hs view
@@ -5,4 +5,7 @@ import Language.Lambda shouldEvalTo :: String -> String -> Expectation-shouldEvalTo s1 = shouldBe (evalString s1) . evalString+shouldEvalTo s1 = shouldBe (eval s1) . eval++eval :: String -> Either ParseError (LambdaExpr String)+eval = evalString
test/Language/Lambda/ParserSpec.hs view
@@ -8,47 +8,46 @@ import Language.Lambda.Parser spec :: Spec-spec = do- describe "parseExpr" $ do- it "parses simple variables" $ do- parseExpr "x" `shouldBe` Right (Var "x")+spec = describe "parseExpr" $ do+ it "parses simple variables" $+ parseExpr "x" `shouldBe` Right (Var "x") - it "parses parenthesized variables" $ do- parseExpr "(x)" `shouldBe` Right (Var "x")+ it "parses parenthesized variables" $+ parseExpr "(x)" `shouldBe` Right (Var "x") - it "parses simple abstractions" $ do- parseExpr "\\x. x" `shouldBe` Right (Abs "x" (Var "x"))+ it "parses simple abstractions" $+ parseExpr "\\x. x" `shouldBe` Right (Abs "x" (Var "x")) - it "parses nested abstractions" $ do- parseExpr "\\f a. a" `shouldBe` Right (Abs "f" (Abs "a" (Var "a")))+ it "parses nested abstractions" $+ parseExpr "\\f a. a" `shouldBe` Right (Abs "f" (Abs "a" (Var "a"))) - it "parses simple applications" $ do- parseExpr "f x" `shouldBe` Right (App (Var "f") (Var "x"))+ it "parses simple applications" $+ parseExpr "f x" `shouldBe` Right (App (Var "f") (Var "x")) - it "parses chained applications" $ do- parseExpr "f x y" `shouldBe` Right (App (App (Var "f") (Var "x")) (Var "y"))+ it "parses chained applications" $+ parseExpr "f x y" `shouldBe` Right (App (App (Var "f") (Var "x")) (Var "y")) - it "parses complex expressions" $ do- let exprs = [- "\\f x. f x",- "(\\p x y. y) (\\p x y. x)",- "f (\\x. x)",- "(\\x . f x) g y",- "(\\f . (\\ x y. f x y) f x y) w x y"- ]- - mapM_ ((flip shouldSatisfy) isRight . parseExpr) exprs+ it "parses complex expressions" $ do+ let exprs = [+ "\\f x. f x",+ "(\\p x y. y) (\\p x y. x)",+ "f (\\x. x)",+ "(\\x . f x) g y",+ "(\\f . (\\ x y. f x y) f x y) w x y"+ ]+ + mapM_ (flip shouldSatisfy isRight . parseExpr) exprs - it "does not parse trailing errors" $ do- parseExpr "x +" `shouldSatisfy` isLeft- - it "ignores whitespace" $ do- let exprs = [- " x ",- " \\ x . x ",- " ( x ) "- ]- - 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 ) "+ ]+ + mapM_ (flip shouldSatisfy isRight . parseExpr) exprs
− test/Language/Lambda/PrettyPrintSpec.hs
@@ -1,37 +0,0 @@-module Language.Lambda.PrettyPrintSpec where--import Test.Hspec--import Language.Lambda.PrettyPrint- -spec :: Spec-spec = do- describe "PDoc" $ do- it "pretty prints empty" $ do- prettyPrint' empty `shouldBe` ""-- it "pretty prints added components" $ do- let pdoc = add "f" (add "x" empty)- prettyPrint' pdoc `shouldBe` "fx"-- it "pretty prints appended components" $ do- let pdoc = append ["f", "x", "y"] empty- prettyPrint' pdoc `shouldBe` "fxy"-- it "pretty prints between parens" $ do- let pdoc = between (PDoc ["f"]) "(" ")" empty- prettyPrint' pdoc `shouldBe` "(f)"-- let pdoc' = betweenParens (PDoc ["f"]) empty- prettyPrint' pdoc' `shouldBe` "(f)"-- it "pretty prints intercalated spaces" $ do- let pdoc = intercalate ["f", "x", "y"] [space] empty- prettyPrint' pdoc `shouldBe` "f x y"-- it "pretty prints lambda" $ do- let pdoc = between (PDoc ["x"]) "\\" ". " (add "x" empty)- prettyPrint' pdoc `shouldBe` "\\x. x"--prettyPrint' :: PDoc String -> String-prettyPrint' = prettyPrint
+ test/Language/Lambda/Util/PrettyPrintSpec.hs view
@@ -0,0 +1,36 @@+module Language.Lambda.Util.PrettyPrintSpec where++import Test.Hspec++import Language.Lambda.Util.PrettyPrint+ +spec :: Spec+spec = describe "PDoc" $ do+ it "pretty prints empty" $+ prettyPrint' empty `shouldBe` ""++ it "pretty prints added components" $ do+ let pdoc = add "f" (add "x" empty)+ prettyPrint' pdoc `shouldBe` "fx"++ it "pretty prints appended components" $ do+ let pdoc = append ["f", "x", "y"] empty+ prettyPrint' pdoc `shouldBe` "fxy"++ it "pretty prints between parens" $ do+ let pdoc = between (PDoc ["f"]) "(" ")" empty+ prettyPrint' pdoc `shouldBe` "(f)"++ let pdoc' = betweenParens (PDoc ["f"]) empty+ prettyPrint' pdoc' `shouldBe` "(f)"++ it "pretty prints intercalated spaces" $ do+ let pdoc = intercalate ["f", "x", "y"] [space] empty+ prettyPrint' pdoc `shouldBe` "f x y"++ it "pretty prints lambda" $ do+ let pdoc = between (PDoc ["x"]) "\\" ". " (add "x" empty)+ prettyPrint' pdoc `shouldBe` "\\x. x"++prettyPrint' :: PDoc String -> String+prettyPrint' = prettyPrint
test/Language/LambdaSpec.hs view
@@ -12,19 +12,19 @@ evalString "\\x. x" `shouldBe` Right (Abs "x" (Var "x")) evalString "f y" `shouldBe` Right (App (Var "f") (Var "y")) - it "reduces simple applications" $ do+ it "reduces simple applications" $ evalString "(\\x .x) y" `shouldBe` Right (Var "y") - it "reduces applications with nested redexes" $ do+ it "reduces applications with nested redexes" $ evalString "(\\f x. f x) (\\y. y)" `shouldBe` Right (Abs "x" (Var "x")) describe "uniques" $ do let alphabet = reverse ['a'..'z'] len = length alphabet - it "starts with plain alphabet" $ do+ it "starts with plain alphabet" $ take len uniques `shouldBe` map (:[]) alphabet - it "adds index afterwards" $ do+ it "adds index afterwards" $ take len (drop len uniques) `shouldBe` map (:['0']) alphabet
+ test/Language/SystemF/ExpressionSpec.hs view
@@ -0,0 +1,67 @@+{-# LANGUAGE FlexibleInstances #-}+module Language.SystemF.ExpressionSpec where++import Test.Hspec++import Language.Lambda.Util.PrettyPrint+import Language.SystemF.Expression++spec :: Spec+spec = describe "prettyPrint" $ do+ it "prints simple variables" $+ prettyPrint' (Var "x") `shouldBe` "x"++ it "prints simple applications" $+ prettyPrint' (App (Var "a") (Var "b")) `shouldBe` "a b"++ it "prints simple abstractions" $ + prettyPrint (Abs "x" (TyVar "T") (Var "x")) `shouldBe` "λ x:T. x"++ it "prints simple type abstractions" $+ prettyPrint (TyAbs (TyVar "X") (Var "x")) `shouldBe` "Λ X. x"++ it "prints simple type applications" $ + prettyPrint' (TyApp (Var "t") (TyVar "T")) `shouldBe` "t [T]"++ it "prints nested abstractions" $+ prettyPrint (Abs "f" (TyVar "F") (Abs "x" (TyVar "X") (Var "x")))+ `shouldBe` "λ f:F x:X. x"++ it "prints abstractions with composite types" $ do+ prettyPrint (Abs "f" (TyArrow (TyVar "X") (TyVar "Y")) (Var "f"))+ `shouldBe ` "λ f:(X->Y). f"++ prettyPrint (Abs "f" (TyArrow (TyVar "X") (TyArrow (TyVar "Y") (TyVar "Z"))) (Var "f"))+ `shouldBe ` "λ f:(X->Y->Z). f"++ it "prints nested type abstractions" $+ prettyPrint (TyAbs (TyVar "A") (TyAbs (TyVar "B") (Var "x")))+ `shouldBe` "Λ A B. x"++ it "prints nested applications" $+ prettyPrint' (App (App (Var "f") (Var "x")) (Var "y"))+ `shouldBe` "f x y"++ it "prints parenthesized applications" $ do+ prettyPrint' (App (Var "w") (App (Var "x") (Var "y")))+ `shouldBe` "w (x y)"++ prettyPrint (App (Abs "t" (TyVar "T") (Var "t")) (Var "x"))+ `shouldBe` "(λ t:T. t) x"++ prettyPrint (App (Abs "f" (TyVar "F") (Var "f")) (Abs "g" (TyVar "G") (Var "g")))+ `shouldBe` "(λ f:F. f) (λ g:G. g)"++ it "prints simple types" $+ prettyPrint (TyVar "X") `shouldBe` "X"++ it "print simple arrow types" $+ prettyPrint (TyArrow (TyVar "A") (TyVar "B")) `shouldBe` "A -> B"++ it "prints chained arrow types" $+ prettyPrint (TyArrow (TyVar "X") (TyArrow (TyVar "Y") (TyVar "Z")))+ `shouldBe` "X -> Y -> Z"++ it "prints nested arrow types" $+ prettyPrint (TyArrow (TyArrow (TyVar "T") (TyVar "U")) (TyVar "V"))+ `shouldBe` "(T -> U) -> V"
+ test/Language/SystemF/ParserSpec.hs view
@@ -0,0 +1,84 @@+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"))
+ test/Language/SystemFSpec.hs view
@@ -0,0 +1,9 @@+module Language.SystemFSpec where++import Test.Hspec++import Language.SystemF++spec :: Spec+spec = describe "evalString" $ + return ()