ivo/src/LambdaCalculus/Parser.hs

80 lines
2.4 KiB
Haskell

module LambdaCalculus.Parser (parse) where
import Control.Applicative (liftA2)
import Control.Monad (void)
import Text.Parsec hiding (parse)
import qualified Text.Parsec as Parsec
import Text.Parsec.String (Parser)
import LambdaCalculus.Representation.AbstractSyntax
-- | Parse a keyword, unambiguously not a variable name.
keyword :: String -> Parser ()
keyword kwd = try $ do
void $ string kwd
-- TODO: rephrase this in terms of `extension`
notFollowedBy alphaNum
-- | The extension of a variable name.
-- The first letter of a variable name must be a letter,
-- but the rest of the variable name may be more general.
extension :: Parser String
extension = many alphaNum
-- | A variable name, e.g. `x`, `foo`, `f2`, `fooBar27`.
name :: Parser String
name = do
notFollowedBy anyKeyword
liftA2 (:) letter extension
where
anyKeyword = choice $ map keyword keywords
where
-- | Keywords that are forbidden from use as variable names.
keywords = ["let", "in"]
-- | A variable expression.
variable :: Parser Expression
variable = Variable <$> name
-- | A lambda abstraction.
abstraction :: Parser Expression
abstraction = do
char 'λ' <|> char '\\' ; spaces
variables <- variableList ; spaces
char '.' ; spaces
body <- expression
return $ Abstraction variables body
where variableList :: Parser [String]
variableList = name `sepBy` spaces
-- | A function application.
application :: Parser Expression
application = Application <$> applicationTerm `sepEndBy` spaces
where
-- | An application term is any expression which consumes input,
-- i.e. anything other than an ungrouped application.
applicationTerm :: Parser Expression
applicationTerm = let_ <|> variable <|> abstraction <|> grouping
where
-- | An expression grouped by parentheses.
grouping :: Parser Expression
grouping = between (char '(' >> spaces) (spaces >> char ')') expression
-- | A `let` expression.
let_ :: Parser Expression
let_ = do
keyword "let" ; spaces
variable <- name ; spaces
char '=' ; spaces
value <- expression ; spaces
string "in" ; spaces
body <- expression
return $ Let variable value body
-- | Any expression.
expression :: Parser Expression
expression = application
-- | Parse a lambda calculus expression.
parse :: SourceName -> String -> Either ParseError Expression
parse = Parsec.parse (expression <* eof)