module Compiler.AST.FunctionStatement where import Control.Applicative import Data.List as L import Data.List.NonEmpty as NE import Data.Maybe import Data.Text as T 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 "FunctionStatement" $ do loc <- getParserLocation fsr <- astParser pure $ FunctionStatementWithLoc fsr loc instance HasGen FunctionStatementWithLoc where getGen = FunctionStatementWithLoc <$> getGen <*> (pure emptyLocation) data FunctionStatement = Let Subscript ExpressionWithLoc | Call Identifier [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) | ForEach Identifier ExpressionWithLoc (NonEmpty FunctionStatementWithLoc) | While ExpressionWithLoc (NonEmpty FunctionStatementWithLoc) | Loop (NonEmpty FunctionStatementWithLoc) | Return ExpressionWithLoc | Break | FnComment Comment deriving (Show, Eq) instance ToSource FunctionStatement where toSourcePretty i Break = T.concat [indent i, toSource KwBreak] toSourcePretty i (FnComment c) = stripEnd (toSourcePretty i c) toSourcePretty i (Let idf expr) = T.concat [indent i, toSource KwLet, wst, toSource idf, wst, toSource KwAssignment, wst, toSource expr] toSourcePretty i (Call idf args) = T.concat $ [indent i, toSource idf, toSource DlParenOpen] <> argsSrc <> [toSource DlParenClose] where argsSrc = (L.intersperse (toSource DlComma <> " ") (toSource <$> args)) toSourcePretty i (Return expr) = T.concat $ [indent i, toSource KwReturn, 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) = 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 (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 <*> getGen , Call <$> getGen <*> getGen , Return <$> getGen , FnComment <$> getGen , pure Break ] [ 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) , 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 <|> breakParser <|> callStatementParser <|> (FnComment <$> (surroundWs parseComment)) letParser :: AstParser FunctionStatement letParser = nameParser "Let statement" $ do surroundWs_ (parseKeyword KwLet) idf <- surroundWs (mandatory parseSubscript) surroundWs_ (mandatory (parseToken "assignment" isAssignment)) expr <- surroundWs (mandatory (astParser @ExpressionWithLoc)) pure $ Let idf expr where isAssignment (TkKeyword KwAssignment) = Just () isAssignment _ = Nothing callStatementParser :: AstParser FunctionStatement callStatementParser = nameParser "Function call" $ do idf <- surroundWs parseIdentifier args <- parseItemListInParen (astParser @ExpressionWithLoc) pure $ Call idf $ fromMaybe [] (NE.toList <$> args) 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) stms <- mandatory (some $ astParser @FunctionStatementWithLoc) surroundWs_ (mandatory (parseKeyword KwEndFor)) pure $ For idf startExp endExp (NE.fromList stms) 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) breakParser :: AstParser FunctionStatement breakParser = nameParser "break statement" $ do surroundWs_ (parseKeyword KwBreak) pure Break