diff --git a/README.md b/README.md index 7a24615..cc3aacd 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ A (very WIP) lambda expression evaluator and debugger. I'd like to build the following: -- [ ] parsing with parsec +- [X] parsing with parsec + - need to harden tests a bit more but existing tests pass - [ ] alpha/beta reduction to beta normal - [ ] substitutions for known abstractions (numbers can be written and are displayed as numbers rather than lambda terms) - [ ] persisted variables diff --git a/src/Parser.hs b/src/Parser.hs index e259bd3..ed5c105 100644 --- a/src/Parser.hs +++ b/src/Parser.hs @@ -1,36 +1,47 @@ module Parser where import Control.Monad (void) +import Data.Char (isLetter) import Text.Parsec import Text.Parsec.String (Parser) -data Expr = Variable String | Abstraction (String, Expr) | Application (Expr, Expr) +data Expr = Variable String | Abstraction String Expr | Application Expr Expr deriving (Eq) instance Show Expr where show (Variable name) = name - show (Abstraction (arg, body)) = "λ" ++ arg ++ ". " ++ show body - show (Application (f, x)) = "(" ++ show f ++ " " ++ show x ++ ")" + show (Abstraction arg body) = "λ" ++ arg ++ ". " ++ show body + show (Application f x) = "(" ++ show f ++ " " ++ show x ++ ")" ws :: Parser () ws = skipMany (void space) +lexeme :: Parser a -> Parser a +lexeme p = p <* ws + variable :: Parser Expr -variable = do - name <- many letter +variable = lexeme $ do + name <- many1 (satisfy (\c -> isLetter c && c /= 'λ')) return $ Variable name abstraction :: Parser Expr -abstraction = do +abstraction = lexeme $ do _ <- void (char 'λ') - variable <- many letter - body <- parseExpr - return $ Abstraction (variable, body) + name <- many letter + _ <- void (char '.' >> ws) + body <- application + return $ Abstraction name body -parseExpr :: Parser Expr -parseExpr = variable <|> abstraction +application :: Parser Expr +application = lexeme $ chainl1 atom (return Application) + +parens :: Parser Expr +parens = lexeme $ between (char '(') (char ')') application + +atom :: Parser Expr +atom = variable <|> abstraction <|> parens parse :: String -> Either String Expr -parse content = case Text.Parsec.parse parseExpr "" content of +parse content = case Text.Parsec.parse application "" content of Left err -> Left (show err) Right val -> Right val diff --git a/test/ParserSpec.hs b/test/ParserSpec.hs index 933f218..f362e8a 100644 --- a/test/ParserSpec.hs +++ b/test/ParserSpec.hs @@ -9,39 +9,37 @@ spec = do it "can parse variable" $ (parse "x") `shouldBe` Right (Variable "x") it "can parse identity abstraction" $ - (parse "λx.x") `shouldBe` Right (Abstraction ("x", Variable "x")) + (parse "λx.x") `shouldBe` Right (Abstraction "x" (Variable "x")) it "can parse mockingbird of identity" $ (parse "(λx.x x) (λx.x)") -- (λx.x) (λx.x) -> (λx.x) `shouldBe` Right ( Application ( Abstraction - ( "x", - Application - ( Variable "x", - Variable "x" - ) - ), - Abstraction ("x", Variable "x") + "x" + ( Application + (Variable "x") + (Variable "x") + ) ) + (Abstraction "x" (Variable "x")) ) it "it can parse successor" $ (parse "λn.λf.λx.f (n f x)") `shouldBe` Right ( Abstraction - ( "n", - Abstraction - ( "f", - Abstraction - ( "x", - Application - ( Variable "f", - Application + "n" + ( Abstraction + "f" + ( Abstraction + "x" + ( Application + (Variable "f") + ( Application ( Application - ( Variable "n", - Variable "f" - ), - Variable "x" + (Variable "n") + (Variable "f") ) + (Variable "x") ) ) )