From f64bf8be2b160c912a4559bb2db60803369e945f Mon Sep 17 00:00:00 2001
From: jackjohn7 <70782491+jackjohn7@users.noreply.github.com>
Date: Fri, 15 May 2026 02:02:57 -0500
Subject: [PATCH] parsing, update readme
---
README.md | 3 ++-
src/Parser.hs | 35 +++++++++++++++++++++++------------
test/ParserSpec.hs | 38 ++++++++++++++++++--------------------
3 files changed, 43 insertions(+), 33 deletions(-)
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")
)
)
)