spade-0.1.0.9: src/Compiler/AST/FunctionStatement.hs
module Compiler.AST.FunctionStatement where
import Control.Applicative
import Data.List.NonEmpty as NE
import Data.Text as T
import Data.Maybe (isJust)
import Common
import Compiler.AST.Common
import Compiler.AST.Expression
import Compiler.AST.Parser.Common
import Compiler.Lexer
import Compiler.Lexer.Comments
import Parser.Lib
import Parser.Parser
import Test.Common
data FunctionStatementWithLoc
= FunctionStatementWithLoc FunctionStatement Location
deriving Show
instance Eq FunctionStatementWithLoc where
(FunctionStatementWithLoc fs1 _) == (FunctionStatementWithLoc fs2 _) = fs1 == fs2
instance ToSource FunctionStatementWithLoc where
toSourcePretty i (FunctionStatementWithLoc fs _) = toSourcePretty i fs
instance HasAstParser FunctionStatementWithLoc where
astParser = nameParser "Statement" $ do
loc <- getParserLocation
fsr <- astParser
pure $ FunctionStatementWithLoc fsr loc
instance HasGen FunctionStatementWithLoc where
getGen = FunctionStatementWithLoc <$> getGen <*> (pure emptyLocation)
type IsGlobal = Bool
data FunctionStatement
= Let Subscript IsGlobal ExpressionWithLoc
| Call ExpressionWithLoc
| If ExpressionWithLoc (NonEmpty FunctionStatementWithLoc) (NonEmpty FunctionStatementWithLoc)
| MultiIf ExpressionWithLoc (NonEmpty FunctionStatementWithLoc) (NonEmpty (ExpressionWithLoc, NonEmpty FunctionStatementWithLoc)) (Maybe (NonEmpty FunctionStatementWithLoc))
| IfThen ExpressionWithLoc (NonEmpty FunctionStatementWithLoc)
| For Identifier ExpressionWithLoc ExpressionWithLoc (NonEmpty FunctionStatementWithLoc) (Maybe ExpressionWithLoc)
| ForEach Identifier ExpressionWithLoc (NonEmpty FunctionStatementWithLoc)
| While ExpressionWithLoc (NonEmpty FunctionStatementWithLoc)
| Loop (NonEmpty FunctionStatementWithLoc)
| Return ExpressionWithLoc
| Yield ExpressionWithLoc
| Break
| Pass
| FnComment Comment
deriving (Show, Eq)
instance ToSource FunctionStatement where
toSourcePretty i Break = T.concat [indent i, toSource KwBreak]
toSourcePretty i Pass = T.concat [indent i, toSource KwPass]
toSourcePretty i (FnComment c) = stripEnd (toSourcePretty i c)
toSourcePretty i (Let idf False expr) =
T.concat [indent i, toSource KwLet, wst, toSource idf, wst, toSource KwAssignment, wst, toSource expr]
toSourcePretty i (Let idf True expr) =
T.concat [indent i, toSource KwLet, wst, toSource KwGlobal, wst, toSource idf, wst, toSource KwAssignment, wst, toSource expr]
toSourcePretty i (Call expr) =
T.concat $ [indent i, toSource expr]
toSourcePretty i (Return expr) = T.concat $ [indent i, toSource KwReturn, wst, toSource expr]
toSourcePretty i (Yield expr) = T.concat $ [indent i, toSource KwYield, wst, toSource expr]
toSourcePretty i (ForEach idf expr1 stms) =
T.concat $
[indent i, toSource KwForEach, wst, toSource expr1, wst, toSource KwAs, wst, toSource idf, wst, nlt] <>
[toSourcePretty (i+1) (NE.toList stms)] <>
[nlt, indent i, toSource KwEndForEach]
toSourcePretty i (For idf expr1 expr2 stms Nothing) =
T.concat $
[indent i, toSource KwFor, wst, toSource idf, wst, toSource KwAssignment, wst, toSource expr1, wst, toSource KwTo, wst, toSource expr2, nlt] <>
[toSourcePretty (i+1) (NE.toList stms)] <>
[nlt, indent i, toSource KwEndFor]
toSourcePretty i (For idf expr1 expr2 stms (Just stepExpr)) =
T.concat $
[indent i, toSource KwFor, wst, toSource idf, wst, toSource KwAssignment, wst, toSource expr1, wst, toSource KwTo, wst, toSource expr2, wst, toSource KwStep, wst, toSource stepExpr, nlt] <>
[toSourcePretty (i+1) (NE.toList stms)] <>
[nlt, indent i, toSource KwEndFor]
toSourcePretty i (IfThen expr stms) =
T.concat $
[indent i, toSource KwIf, wst, toSource expr, wst, toSource KwThen, nlt] <>
[toSourcePretty (i+1) (NE.toList stms)] <>
[nlt, indent i, toSourcePretty i KwEndIf]
toSourcePretty i (MultiIf expr stms1 rst mstms2) =
T.concat $
[indent i, toSource KwIf, wst, toSource expr, wst, toSource KwThen, nlt] <>
[toSourcePretty (i+1) (NE.toList stms1)] <>
(toSourceElseIf <$> (NE.toList rst)) <>
[nlt, toSourceElse] <>
[indent i, toSource KwEndIf]
where
toSourceElse :: Text
toSourceElse = case mstms2 of
Just stms -> T.concat $
[indent i, toSource KwElse, nlt] <> [toSourcePretty (i+1) (NE.toList stms)] <> [nlt]
Nothing -> ""
toSourceElseIf :: (ExpressionWithLoc, NonEmpty FunctionStatementWithLoc) -> Text
toSourceElseIf (expr1, stms3) =
T.concat $
[nlt, indent i, toSource KwElseIf, wst, toSource expr1, wst, toSource KwThen, nlt] <>
[toSourcePretty (i+1) (NE.toList stms3)]
toSourcePretty i (If expr stms1 stms2) =
T.concat $
[indent i, toSource KwIf, wst, toSource expr, wst, toSource KwThen, nlt] <>
[toSourcePretty (i+1) (NE.toList stms1)] <>
[nlt, indent i, toSource KwElse, nlt] <>
[toSourcePretty (i+1) (NE.toList stms2)] <>
[nlt, indent i, toSource KwEndIf]
toSourcePretty i (While expr1 stms) =
T.concat $
[indent i, toSource KwWhile, wst, toSource expr1, nlt] <>
[toSourcePretty (i+1) (NE.toList stms)] <>
[nlt, indent i, toSource KwEndWhile]
toSourcePretty i (Loop stms) =
T.concat $
[indent i, toSource KwLoop, nlt] <>
[toSourcePretty (i+1) (NE.toList stms)] <>
[nlt, indent i, toSource KwEndLoop]
instance HasGen FunctionStatement where
getGen = recursive choice
[ Let <$> getGen <*> bool <*> getGen
, Call <$> getGen
, Return <$> getGen
, Yield <$> getGen
, FnComment <$> getGen
, pure Break
, pure Pass
]
[ If <$> getGen <*> (nonEmptyGen getGen) <*> (nonEmptyGen getGen)
, IfThen <$> getGen <*> (nonEmptyGen getGen)
, MultiIf <$> getGen <*> (nonEmptyGen getGen) <*> (nonEmptyGen ((,) <$> getGen <*> (nonEmptyGen getGen))) <*> (Test.Common.maybe (nonEmptyGen getGen))
, For <$> getGen <*> getGen <*> getGen <*> (nonEmptyGen getGen) <*> getGen
, Loop <$> (nonEmptyGen getGen)
, While <$> getGen <*> (nonEmptyGen getGen)
]
instance HasAstParser FunctionStatement where
astParser = nameParser "Function statement (raw)" $ do
(whitespaceNL <|> whitespace)
functionStatementParser
functionStatementParser :: AstParser FunctionStatement
functionStatementParser
= ifParser
<|> letParser
<|> forEachParser
<|> forParser
<|> whileParser
<|> loopParser
<|> returnParser
<|> yieldParser
<|> breakParser
<|> passParser
<|> callStatementParser
<|> (FnComment <$> (surroundWs parseComment))
letParser :: AstParser FunctionStatement
letParser = nameParser "Let statement" $ do
surroundWs_ (parseKeyword KwLet)
(isJust -> isGlobal) <- surroundWs (optional (parseKeyword KwGlobal))
idf <- surroundWs (mandatory parseSubscript)
surroundWs_ (mandatory (parseToken "assignment" isAssignment))
expr <- surroundWs (mandatory (astParser @ExpressionWithLoc))
pure $ Let idf isGlobal expr
where
isAssignment (TkKeyword KwAssignment) = Just ()
isAssignment _ = Nothing
callStatementParser :: AstParser FunctionStatement
callStatementParser = nameParser "Function call" $ do
expr <- surroundWs (astParser @ExpressionWithLoc)
pure $ Call expr
loopParser :: AstParser FunctionStatement
loopParser = nameParser "Loop Statement" $ do
surroundWs_ (parseKeyword KwLoop)
stms <- mandatory (some $ astParser @FunctionStatementWithLoc)
surroundWs_ (mandatory (parseKeyword KwEndLoop))
pure $ Loop (NE.fromList stms)
whileParser :: AstParser FunctionStatement
whileParser = nameParser "While Statement" $ do
surroundWs_ (parseKeyword KwWhile)
guardBool <- surroundWs (mandatory $ astParser @ExpressionWithLoc)
stms <- mandatory (some $ astParser @FunctionStatementWithLoc)
surroundWs_ (mandatory (parseKeyword KwEndWhile))
pure $ While guardBool (NE.fromList stms)
forParser :: AstParser FunctionStatement
forParser = nameParser "For Statement" $ do
surroundWs_ (parseKeyword KwFor)
idf <- surroundWs (mandatory parseIdentifier)
surroundWs_ (mandatory $ parseKeyword KwAssignment)
startExp <- surroundWs (mandatory $ astParser @ExpressionWithLoc)
surroundWs_ (mandatory (parseKeyword KwTo))
endExp <- surroundWs (mandatory $ astParser @ExpressionWithLoc)
mStepExpr <- surroundWs (optional (parseKeyword KwStep)) >>= \case
Just _ -> Just <$> surroundWs (mandatory $ astParser @ExpressionWithLoc)
Nothing -> pure Nothing
stms <- mandatory (some $ astParser @FunctionStatementWithLoc)
surroundWs_ (mandatory (parseKeyword KwEndFor))
pure $ For idf startExp endExp (NE.fromList stms) mStepExpr
forEachParser :: AstParser FunctionStatement
forEachParser = nameParser "ForEach Statement" $ do
surroundWs_ (parseKeyword KwForEach)
expr <- surroundWs (mandatory $ astParser @ExpressionWithLoc)
surroundWs_ (mandatory $ parseKeyword KwAs)
idf <- surroundWs (mandatory parseIdentifier)
stms <- mandatory (some $ astParser @FunctionStatementWithLoc)
surroundWs_ (mandatory (parseKeyword KwEndForEach))
pure $ ForEach idf expr (NE.fromList stms)
ifParser :: AstParser FunctionStatement
ifParser = nameParser "If Statement" $ do
surroundWs_ (parseKeyword KwIf)
expr <- surroundWs (mandatory $ astParser @ExpressionWithLoc)
surroundWs_ (mandatory $ parseKeyword KwThen)
stms1 <- mandatory (some $ astParser @FunctionStatementWithLoc)
surroundWs (mandatory (parseKeyword KwElseIf <|> parseKeyword KwElse <|> parseKeyword KwEndIf)) >>= \case
KwElseIf -> do
expr' <- surroundWs (mandatory $ astParser @ExpressionWithLoc)
surroundWs_ (mandatory $ parseKeyword KwThen)
stms2 <- mandatory (some $ astParser @FunctionStatementWithLoc)
remaining <- many $ do
surroundWs_ $ parseKeyword KwElseIf
expr1 <- surroundWs (mandatory $ astParser @ExpressionWithLoc)
surroundWs_ (mandatory $ parseKeyword KwThen)
stms3 <- mandatory (some $ astParser @FunctionStatementWithLoc)
pure (expr1, NE.fromList stms3)
surroundWs (mandatory (parseKeyword KwElse <|> parseKeyword KwEndIf)) >>= \case
KwElse -> do
elseStms <- mandatory (some $ astParser @FunctionStatementWithLoc)
surroundWs_ (mandatory $ parseKeyword KwEndIf)
pure $ MultiIf expr (NE.fromList stms1) ((expr', NE.fromList stms2) :| remaining) (Just $ NE.fromList elseStms)
KwEndIf ->
pure $ MultiIf expr (NE.fromList stms1) ((expr', NE.fromList stms2) :| remaining) Nothing
_ -> fail "Impossible"
KwElse -> do
stms2 <- mandatory (some $ astParser @FunctionStatementWithLoc)
surroundWs_ (mandatory $ parseKeyword KwEndIf)
pure $ If expr (NE.fromList stms1) (NE.fromList stms2)
KwEndIf ->
pure $ IfThen expr (NE.fromList stms1)
_ -> fail "Impossible"
returnParser :: AstParser FunctionStatement
returnParser = nameParser "return statement" $ do
surroundWs_ (parseKeyword KwReturn)
Return <$> (surroundWs $ astParser @ExpressionWithLoc)
yieldParser :: AstParser FunctionStatement
yieldParser = nameParser "yield statement" $ do
surroundWs_ (parseKeyword KwYield)
setGeneratorFlag True
Yield <$> (surroundWs $ astParser @ExpressionWithLoc)
breakParser :: AstParser FunctionStatement
breakParser = nameParser "break statement" $ do
surroundWs_ (parseKeyword KwBreak)
pure Break
passParser :: AstParser FunctionStatement
passParser = nameParser "pass statement" $ do
surroundWs_ (parseKeyword KwPass)
pure Pass