{-
-- EPITECH PROJECT, 2025
-- Glados
-- File description:
-- Condition Parser (If, Loops, etc.)
-}

{-|
Module      : Parser.Condition
Description : Parser for control flow structures.
Stability   : stable
This module defines parsers for control flow:
- Conditional statements (if/else)
- Iteration statements (while)
Note: Parsers in this module often require 'pBlock' and 'pVarDef' as arguments
to avoid circular dependencies with Parser.Statement.
-}
module Parser.Conditions (
    pIf,
    pWhile,
    pFor
) where

import Text.Megaparsec
import qualified Data.Text as DT
import AST.Ast (Ast(..))
import Parser.Lexer
import Parser.Expression (pExpr)

-- | Parse the content inside the 'if' parentheses.
pIfCondition :: Parser Ast -> Parser (Maybe Ast, Ast)
pIfCondition :: Parser Ast -> Parser (Maybe Ast, Ast)
pIfCondition Parser Ast
pVarDefParam = Parser (Maybe Ast, Ast) -> Parser (Maybe Ast, Ast)
forall a. Parser a -> Parser a
parens (Parser (Maybe Ast, Ast) -> Parser (Maybe Ast, Ast))
-> Parser (Maybe Ast, Ast) -> Parser (Maybe Ast, Ast)
forall a b. (a -> b) -> a -> b
$ do
    maybeInit <- Parser Ast -> ParsecT Void Text Identity (Maybe Ast)
forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional (Parser Ast -> Parser Ast
forall a. Parser a -> Parser a
forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
try Parser Ast
pVarDefParam Parser Ast -> Parser Ast -> Parser Ast
forall a.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity a -> ParsecT Void Text Identity a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Ast -> Parser Ast
forall a. Parser a -> Parser a
forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
try (Parser Ast
pExpr Parser Ast -> ParsecT Void Text Identity Text -> Parser Ast
forall a b.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity b -> ParsecT Void Text Identity a
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* ParsecT Void Text Identity Text
semicolon))
    condition <- pExpr
    return (maybeInit, condition)

-- | Parse the optional 'else' branch.
-- Returns 'AVoid' if no else block is found.
pElseBranch :: Parser Ast -> Parser Ast -> Parser Ast
pElseBranch :: Parser Ast -> Parser Ast -> Parser Ast
pElseBranch Parser Ast
pVarDefParam Parser Ast
pBlockParam = Ast -> Parser Ast -> Parser Ast
forall (m :: * -> *) a. Alternative m => a -> m a -> m a
option Ast
AVoid (Parser Ast -> Parser Ast) -> Parser Ast -> Parser Ast
forall a b. (a -> b) -> a -> b
$ do
    _ <- Text -> ParsecT Void Text Identity Text
pKeyword (String -> Text
DT.pack String
"else")
    try (pIf pVarDefParam pBlockParam) <|> pBlockParam

-- | Construct the final Condition AST.
-- Handles the optional initialization wrapper.
buildIfAst :: Maybe Ast -> Ast -> Ast -> Ast -> Ast
buildIfAst :: Maybe Ast -> Ast -> Ast -> Ast -> Ast
buildIfAst Maybe Ast
initStmt Ast
cond Ast
thenB Ast
elseB =
    let ifNode :: Ast
ifNode = Ast -> Ast -> Ast -> Ast
AIf Ast
cond Ast
thenB Ast
elseB
    in case Maybe Ast
initStmt of
        Just Ast
initInstr -> [Ast] -> Ast
AList [Ast
initInstr, Ast
ifNode]
        Maybe Ast
Nothing        -> Ast
ifNode

-- | Parse a full conditional statement: if, else if, else.
--
-- This function uses Dependency Injection for 'pVarDef' and 'pBlock'.
pIf :: Parser Ast -> Parser Ast -> Parser Ast
pIf :: Parser Ast -> Parser Ast -> Parser Ast
pIf Parser Ast
pVarDefParam Parser Ast
pBlockParam = do
    _ <- Text -> ParsecT Void Text Identity Text
pKeyword (String -> Text
DT.pack String
"if")
    (initStmt, cond) <- pIfCondition pVarDefParam
    thenBlock <- pBlockParam
    elseBlock <- pElseBranch pVarDefParam pBlockParam
    return $ buildIfAst initStmt cond thenBlock elseBlock

-- | Parse a while loop.
--
-- Syntax: while (condition) { body }
-- The body parsing relies on the injected 'pBlockParam'.
pWhile :: Parser Ast -> Parser Ast
pWhile :: Parser Ast -> Parser Ast
pWhile Parser Ast
pBlockParam = do
    _ <- Text -> ParsecT Void Text Identity Text
pKeyword (String -> Text
DT.pack String
"while")
    cond <- parens pExpr
    body <- pBlockParam
    return (AWhile cond body)

-- | Specific parser for updating a for loop (e.g. i = i + 1)
-- Note: Does not consume a trailing semicolon.
pForUpdate :: Parser Ast
pForUpdate :: Parser Ast
pForUpdate = Parser Ast -> Parser Ast
forall a. Parser a -> Parser a
forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
try (do
    name <- ParsecT Void Text Identity Text
pIdentifier
    _ <- symbol (DT.pack "=")
    val <- pExpr
    return (ASetVar name (DT.pack "auto") val))
    Parser Ast -> Parser Ast -> Parser Ast
forall a.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity a -> ParsecT Void Text Identity a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Ast
pExpr

-- | Parse the header of the for loop (init; cond; update).
--
-- Handles optional initialization and update statements.
-- Returns a tuple (Init, Condition, Update).
pForHeader :: Parser Ast -> Parser (Ast, Ast, Ast)
pForHeader :: Parser Ast -> Parser (Ast, Ast, Ast)
pForHeader Parser Ast
pVarDefParam = Parser (Ast, Ast, Ast) -> Parser (Ast, Ast, Ast)
forall a. Parser a -> Parser a
parens (Parser (Ast, Ast, Ast) -> Parser (Ast, Ast, Ast))
-> Parser (Ast, Ast, Ast) -> Parser (Ast, Ast, Ast)
forall a b. (a -> b) -> a -> b
$ do
    initS <- Parser Ast -> Parser Ast
forall a. Parser a -> Parser a
forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
try Parser Ast
pVarDefParam
         Parser Ast -> Parser Ast -> Parser Ast
forall a.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity a -> ParsecT Void Text Identity a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Parser Ast
pExpr Parser Ast -> ParsecT Void Text Identity Text -> Parser Ast
forall a b.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity b -> ParsecT Void Text Identity a
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* ParsecT Void Text Identity Text
semicolon)
         Parser Ast -> Parser Ast -> Parser Ast
forall a.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity a -> ParsecT Void Text Identity a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Ast
AVoid Ast -> ParsecT Void Text Identity Text -> Parser Ast
forall a b.
a -> ParsecT Void Text Identity b -> ParsecT Void Text Identity a
forall (f :: * -> *) a b. Functor f => a -> f b -> f a
<$ ParsecT Void Text Identity Text
semicolon)
    condS <- pExpr <* semicolon
    updateS <- try pForUpdate
           <|> return AVoid
    return (initS, condS, updateS)

-- | Parse a for loop.
--
-- Syntax: for (init; cond; update) { body }
-- The condition is mandatory to prevent infinite loops by accident.
pFor :: Parser Ast -> Parser Ast -> Parser Ast
pFor :: Parser Ast -> Parser Ast -> Parser Ast
pFor Parser Ast
pVarDefParam Parser Ast
pBlockParam = do
    _ <- Text -> ParsecT Void Text Identity Text
pKeyword (String -> Text
DT.pack String
"for")
    (initStmt, cond, updateStmt) <- pForHeader pVarDefParam
    body <- pBlockParam
    return (AFor initStmt cond updateStmt body)