aboutsummaryrefslogtreecommitdiff
path: root/src/Language/SimpleShell/Parser
diff options
context:
space:
mode:
Diffstat (limited to 'src/Language/SimpleShell/Parser')
-rw-r--r--src/Language/SimpleShell/Parser/Expr.hs182
1 files changed, 182 insertions, 0 deletions
diff --git a/src/Language/SimpleShell/Parser/Expr.hs b/src/Language/SimpleShell/Parser/Expr.hs
new file mode 100644
index 0000000..d4d0b81
--- /dev/null
+++ b/src/Language/SimpleShell/Parser/Expr.hs
@@ -0,0 +1,182 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TupleSections #-}
+
+module Language.SimpleShell.Parser.Expr
+ ( exprP
+ )
+where
+
+
+import Language.SimpleShell.Parser (Parser, lookupVar, lookupFun)
+import Language.SimpleShell.AST.Expr (Expr(..), TypedExpr, VarName, FunName)
+import Language.SimpleShell.AST.SimpleType (SimpleType(..))
+import Control.Monad.Combinators.FailExpr
+ ( Associativity(..)
+ , makeExprParser
+ , Operator(..)
+ )
+
+import Control.Applicative ((<|>))
+import Control.Monad.Combinators (manyTill)
+import Data.Char (isAlpha)
+import Data.Foldable (asum)
+import Data.Text (Text)
+import Text.Megaparsec (takeWhile1P)
+import Text.Megaparsec.Char (string, char)
+import qualified Text.Megaparsec.Char.Lexer as L (charLiteral, decimal)
+
+
+exprP :: Parser TypedExpr
+exprP
+ = makeExprParser termP binaryOperatorTable
+ <|> builtinUnaryFunP
+ <|> funP
+
+termP :: Parser TypedExpr
+termP
+ = literalP
+ <|> varP
+ <|> unaryOpP
+ <|> char '(' *> exprP <* char ')'
+
+
+-- | Parse expression with fixed type.
+exprP' :: String -> SimpleType -> Parser Expr
+exprP' errMsg t = do
+ (t', e) <- exprP
+ if t == t'
+ then return e
+ else fail errMsg
+
+
+literalP :: Parser TypedExpr
+literalP
+ = (IntType,) . IntLiteral <$> L.decimal
+ <|> (StrType,) . StrLiteral <$> strLitP
+ <|> (BoolType,) . BoolLiteral <$> boolLitP
+ where
+ strLitP = char '"' *> manyTill L.charLiteral (char '"')
+ boolLitP
+ = True <$ string "true"
+ <|> False <$ string "false"
+
+varP :: Parser TypedExpr
+varP = do
+ _ <- char '$'
+ x <- varNameP
+ t <- lookupVar x
+ return (t, Var x)
+
+funP :: Parser TypedExpr
+funP = do
+ fname <- funNameP
+ (t', ts) <- lookupFun fname
+ args <- mapM (exprP' "Type mismatch with function signature.") ts
+ return (t', FunCall fname args)
+
+
+-- TODO
+varNameP :: Parser VarName
+varNameP = takeWhile1P Nothing isAlpha
+
+-- TODO
+funNameP :: Parser FunName
+funNameP = takeWhile1P Nothing isAlpha
+
+
+-- Binary operators.
+
+type BinaryFun = Expr -> Expr -> Expr
+type BinarySig = SimpleType -> SimpleType -> Maybe SimpleType
+
+binaryOperatorTable :: [[Operator Parser TypedExpr]]
+binaryOperatorTable =
+ [ [ binary AssocR "||" Or $ sameSig BoolType
+ ]
+ , [ binary AssocR "&&" And $ sameSig BoolType
+ ]
+ , [ binary AssocN "==" Eq $ anyCmpSig
+ , binary AssocN "!=" Neq $ anyCmpSig
+ , binary AssocN "<=" Le $ intCmpSig
+ , binary AssocN "<" Lt $ intCmpSig
+ , binary AssocN ">=" Ge $ intCmpSig
+ , binary AssocN ">" Gt $ intCmpSig
+ ]
+ , [ Binary AssocL addP
+ , binary AssocL "-" Sub $ sameSig IntType
+ ]
+ , [ binary AssocL "*" Mul $ sameSig IntType
+ , binary AssocL "/" Div $ sameSig IntType
+ ]
+ ]
+ where
+ sameSig :: SimpleType -> BinarySig
+ sameSig t t1 t2 = if t == t1 && t == t2 then Just t else Nothing
+
+ anyCmpSig t t'
+ | t == t' = Just BoolType
+ | otherwise = Nothing
+
+ intCmpSig IntType IntType = Just BoolType
+ intCmpSig _ _ = Nothing
+
+ addP :: Parser (TypedExpr -> TypedExpr -> Maybe TypedExpr)
+ addP = do
+ _ <- char '+'
+ return $ \(t1, x1) (t2, x2) ->
+ if t1 == t2
+ then
+ case t1 of
+ StrType -> Just (StrType, Concat x1 x2)
+ IntType -> Just (IntType, Add x1 x2)
+ _ -> Nothing
+ else Nothing
+
+ binary
+ :: Associativity -> Text -> BinaryFun -> BinarySig
+ -> Operator Parser TypedExpr
+ binary assoc symbol op sig = Binary assoc $ do
+ _ <- string symbol
+ return $ \(t1, e1) (t2, e2) -> fmap (, op e1 e2) $ sig t1 t2
+
+
+-- Unary operators and builtin unary functions.
+
+type UnaryFun = Expr -> Expr
+type UnarySig = SimpleType -> Maybe SimpleType
+type UnaryParser = Parser (TypedExpr -> Maybe TypedExpr)
+
+unaryOperators, builtinUnaryFuns :: [UnaryParser]
+(unaryOperators, builtinUnaryFuns) =
+ ( [ unary "!" Not $ uniqueSig BoolType BoolType
+ , unary "-" UMinus $ uniqueSig IntType IntType
+ ]
+ , [ unary "length" Length $ uniqueSig StrType IntType
+ , unary "int" IntCast $ uniqueSig StrType IntType
+ , unary "str" StrCast $ uniqueSig IntType StrType
+ ]
+ )
+ where
+ uniqueSig :: SimpleType -> SimpleType -> UnarySig
+ uniqueSig t1 t2 t1'
+ | t1 == t1' = Just t2
+ | otherwise = Nothing
+
+ unary :: Text -> UnaryFun -> UnarySig -> UnaryParser
+ unary symbol op sig = do
+ _ <- string symbol
+ return $ \(t, e) -> fmap (, op e) $ sig t
+
+unaryOpP, builtinUnaryFunP :: Parser TypedExpr
+(unaryOpP, builtinUnaryFunP) =
+ ( asum $ map (aux "unary operator" termP) unaryOperators
+ , asum $ map (aux "builtin unary function" exprP) builtinUnaryFuns
+ )
+ where
+ aux :: String -> Parser TypedExpr -> UnaryParser -> Parser TypedExpr
+ aux desc argP p = do
+ f <- p
+ x <- argP
+ case f x of
+ Just x' -> return x'
+ Nothing -> fail $ "Mismatching " ++ desc ++ " signature."