186 lines
3.5 KiB
Haskell
186 lines
3.5 KiB
Haskell
module APL.Parser (parseAPL) where
|
|
|
|
import APL.AST (Exp (..), VName)
|
|
import Control.Monad (void)
|
|
import Data.Char (isAlpha, isAlphaNum, isDigit)
|
|
import Data.Void (Void)
|
|
import Text.Megaparsec
|
|
( Parsec,
|
|
choice,
|
|
chunk,
|
|
eof,
|
|
errorBundlePretty,
|
|
many,
|
|
notFollowedBy,
|
|
parse,
|
|
satisfy,
|
|
some,
|
|
try,
|
|
)
|
|
import Text.Megaparsec.Char (space)
|
|
|
|
type Parser = Parsec Void String
|
|
|
|
lexeme :: Parser a -> Parser a
|
|
lexeme p = p <* space
|
|
|
|
keywords :: [String]
|
|
keywords =
|
|
[ "if",
|
|
"then",
|
|
"else",
|
|
"true",
|
|
"false",
|
|
"let",
|
|
"in",
|
|
"try",
|
|
"catch",
|
|
"print",
|
|
"put",
|
|
"get"
|
|
]
|
|
|
|
lVName :: Parser VName
|
|
lVName = lexeme $ try $ do
|
|
c <- satisfy isAlpha
|
|
cs <- many $ satisfy isAlphaNum
|
|
let v = c : cs
|
|
if v `elem` keywords
|
|
then fail "Unexpected keyword"
|
|
else pure v
|
|
|
|
lStringLit :: Parser String
|
|
lStringLit =
|
|
lexeme $ some (satisfy (/= '"'))
|
|
|
|
lInteger :: Parser Integer
|
|
lInteger =
|
|
lexeme $ read <$> some (satisfy isDigit) <* notFollowedBy (satisfy isAlphaNum)
|
|
|
|
lString :: String -> Parser ()
|
|
lString s = lexeme $ void $ chunk s
|
|
|
|
lKeyword :: String -> Parser ()
|
|
lKeyword s = lexeme $ void $ try $ chunk s <* notFollowedBy (satisfy isAlphaNum)
|
|
|
|
lBool :: Parser Bool
|
|
lBool =
|
|
lexeme . try . choice $
|
|
[ const True <$> lKeyword "true",
|
|
const False <$> lKeyword "false"
|
|
]
|
|
|
|
pStringLit :: Parser String
|
|
pStringLit =
|
|
lString "\"" *> lStringLit <* lString "\""
|
|
|
|
pAtom :: Parser Exp
|
|
pAtom =
|
|
choice
|
|
[ CstInt <$> lInteger,
|
|
CstBool <$> lBool,
|
|
Var <$> lVName,
|
|
lString "(" *> pExp <* lString ")"
|
|
]
|
|
|
|
pFExp :: Parser Exp
|
|
pFExp = pAtom >>= chain
|
|
where
|
|
chain x =
|
|
choice
|
|
[ do
|
|
y <- pAtom
|
|
chain $ Apply x y,
|
|
pure x
|
|
]
|
|
|
|
pLExp :: Parser Exp
|
|
pLExp =
|
|
choice
|
|
[ If
|
|
<$> (lKeyword "if" *> pExp)
|
|
<*> (lKeyword "then" *> pExp)
|
|
<*> (lKeyword "else" *> pExp),
|
|
Lambda
|
|
<$> (lString "\\" *> lVName)
|
|
<*> (lString "->" *> pExp),
|
|
TryCatch
|
|
<$> (lKeyword "try" *> pExp)
|
|
<*> (lKeyword "catch" *> pExp),
|
|
Let
|
|
<$> (lKeyword "let" *> lVName)
|
|
<*> (lString "=" *> pExp)
|
|
<*> (lKeyword "in" *> pExp),
|
|
pFExp
|
|
]
|
|
|
|
pExp3 :: Parser Exp
|
|
pExp3 =
|
|
choice
|
|
[
|
|
Print
|
|
<$> (lKeyword "print" *> pStringLit)
|
|
<*> pAtom,
|
|
KvGet <$> (lKeyword "get" *> pAtom),
|
|
KvPut <$> (lKeyword "put" *> pAtom)
|
|
<*> pAtom,
|
|
pLExp
|
|
]
|
|
|
|
pExp2 :: Parser Exp
|
|
pExp2 = pExp3 >>= chain
|
|
where
|
|
chain x =
|
|
choice
|
|
[ do
|
|
lString "**"
|
|
y <- pExp2
|
|
chain $ Pow x y,
|
|
pure x
|
|
]
|
|
|
|
pExp1 :: Parser Exp
|
|
pExp1 = pExp2 >>= chain
|
|
where
|
|
chain x =
|
|
choice
|
|
[
|
|
do
|
|
lString "*"
|
|
y <- pLExp
|
|
chain $ Mul x y,
|
|
do
|
|
lString "/"
|
|
y <- pLExp
|
|
chain $ Div x y,
|
|
pure x
|
|
]
|
|
|
|
pExp0 :: Parser Exp
|
|
pExp0 = pExp1 >>= chain
|
|
where
|
|
chain x =
|
|
choice
|
|
[ do
|
|
lString "+"
|
|
y <- pExp1
|
|
chain $ Add x y,
|
|
do
|
|
lString "-"
|
|
y <- pExp1
|
|
chain $ Sub x y,
|
|
do
|
|
lString "=="
|
|
y <- pExp1
|
|
chain $ Eql x y,
|
|
pure x
|
|
]
|
|
|
|
pExp :: Parser Exp
|
|
pExp = pExp0
|
|
|
|
parseAPL :: FilePath -> String -> Either String Exp
|
|
parseAPL fname s = case parse (space *> pExp <* eof) fname s of
|
|
Left err -> Left $ errorBundlePretty err
|
|
Right x -> Right x
|