I am attempting to develop a lambda calculus interpreter, which supports definitions of terms. For that, I'm using the Megaparsec library in Haskell.
I have the following grammar:
term := var | lambda var . term | term term
def := var = term
instruction := :e t | def
My parser should return a list of instruction
s, where :e t
will evaluate to whatever t
is reduced, and def
will allow me to have named definitions.
So far, I've managed to parse a very similar grammar, except I'm using semicolons to separate each instruction:
...
instruction := :e t; | def;
I'd like to remove the need for those semicolons, but I'm having no luck with that. My code is currently:
sc :: Parser ()
sc = L.space space1 (L.skipLineComment "--") (L.skipBlockComment "{-" "-}")
lexeme :: Parser a -> Parser a
lexeme = L.lexeme sc
symbol :: Text -> Parser Text
symbol = L.symbol sc
semicolon :: Parser Text
semicolon = symbol ";"
lowercaseWord :: Parser [Char]
lowercaseWord = some lowerChar
variable :: Parser Term
variable = lexeme lowercaseWord <&> Var
lamAbs :: Parser Term
lamAbs = do
symbol "lambda"
space
v <- lexeme lowercaseWord
space
symbol "."
space
t <- term
space
return $ Abs v t
term :: Parser Term
term = foldl1 App <$> sepBy1 (parens term <|> try lamAbs <|> variable) sc
where parens = between (symbol "(") (symbol ")")
definition :: Parser (Instruction Term)
definition = do
n <- lexeme lowercaseWord
space
symbol "="
space
t <- term
return $ IDef n t
commandEval :: Parser (Instruction Term)
commandEval = do
symbol ":e"
t <- term
optional newline
return $ IEval t
program :: Parser [Instruction Term]
program = sc *> sepEndBy (definition <|> commandEval) semicolon <* eof
parse :: ProgramParser (Instruction Term)
parse text = case runParser program "" text of
Left err -> Left $ errorBundlePretty err
Right ast -> Right ast
When I attempt to remove the need for semicolons by changing the definition of program
to:
program = sc *> some (definition <|> commandEval) <* eof
the parser will not be able to figure out that it needs to stop parsing a term
once it finds something like var = ...
, to continue parsing the next definition.
For example, the following input:
a = (lambda x . x)
x
:e b a
b = lambda y . y
returns the following error:
6:3:
|
6 | b = lambda y . y
| ^
unexpected '='
expecting ":e", "lambda", '(', end of input, lowercase letter, or newline
I tried to change the definition of term
to make it explicit that it should not be followed by a lowercase word and a =
. But that didn't work either:
term = foldl1 App <$> sepBy1 ((parens term <|> try lamAbs <|> variable) <* notFollowedBy newdef) sc
where parens = between (symbol "(") (symbol ")")
newdef = (lexeme lowercaseWord *> space) *> symbol "="
How can I make it so that I don't have the need for semicolons, and my parser automatically stops parsing a term application once it finds var = term
?
Thank you in advance for your help.