{-
-- EPITECH PROJECT, 2025
-- Glados
-- File description:
-- Ast
-}

module AST.Ast (Ast(..), showAst, printAst, cleanAst) where

import Common.Type.Integer (IntValue(..), intValueToInt)
import qualified Data.Text as DT

-- | Abstract Syntax Tree (AST) definition for the GLADOS language.
--
-- @details
--   The AST represents the hierarchical structure of the parsed source code.
--   Each constructor corresponds to a specific syntactic construct in
--   the language, such as literals, definitions, control flow, and
--   function calls.
--
data Ast
    = AInteger IntValue
      -- ^ Represents an integer literal.
      --   @param IntValue The value of the integer.

    | ABool Bool
      -- ^ Represents a boolean literal.
      --   @param Bool True or False.

    | ASymbol DT.Text
      -- ^ Represents a symbol (identifier).
      --   Used for variable names, function names, or reference lookups.
      --   @param Text The name of the symbol.

    | AVoid
      -- ^ Represents a Void or Null value (no return value).

    | AList [Ast]
      -- ^ Represents a generic list of AST nodes.
      --   Can be used for list literals or sequence of expressions.
      --   @param [Ast] The elements of the list.

    | ADefineFunc DT.Text [(DT.Text, DT.Text)] DT.Text Ast
      -- ^ Represents a named function definition.
      --   @param Text The name of the function.
      --   @param [(Text, Text)] The list of arguments (Name, Type).
      --   @param Text The return type of the function.
      --   @param Ast The body of the function.

    | ADefineLambda [DT.Text] Ast
      -- ^ Represents an anonymous function (Lambda).
      --   @param [Text] The list of parameter names.
      --   @param Ast The body of the lambda.

    | ADefineStruct DT.Text [(DT.Text, DT.Text)]
      -- ^ Represents a structure type definition.
      --   @param Text The name of the structure.
      --   @param [(Text, Text)] The list of fields (Name, Type).

    | ASetVar DT.Text DT.Text Ast
      -- ^ Represents a variable definition or assignment.
      --   @param Text The name of the variable.
      --   @param Text The type of the variable.
      --   @param Ast The value assigned to the variable.

    | ASetStruct DT.Text [(DT.Text, Ast)]
      -- ^ Represents a structure definition statement wrapper.
      --   @param ADefineStruct The structure definition node.

    | ACall Ast [Ast]
      -- ^ Represents a function call (Application).
      --   @param Ast The callee (function expression or symbol).
      --   @param [Ast] The list of arguments passed to the function.

    | AAccessStruct Ast DT.Text
      -- ^ Represents a field access (e.g., player.x).
      --   @param Ast The object/structure being accessed.
      --   @param Text The name of the field.

    | AImport DT.Text
      -- ^ Represents an import statement.
      --   @param Text The name of the module or file to import.

    | AIf Ast Ast Ast
      -- ^ Represents a conditional control flow (If-Then-Else).
      --   @param Ast The condition expression.
      --   @param Ast The "Then" branch.
      --   @param Ast The "Else" branch.

    | AWhile Ast Ast
      -- ^ Represents a While loop.
      --   @param Ast The loop condition.
      --   @param Ast The loop body.

    | AFor Ast Ast Ast Ast
      -- ^ Represents a For loop.
      --   @param Ast The initialization step.
      --   @param Ast The loop condition.
      --   @param Ast The update/increment step.
      --   @param Ast The loop body.

    | AReturn Ast
      -- ^ Represents an explicit return statement.
      --   @param Ast The expression to return.

    | AExprStmt Ast
      -- ^ Represents an expression used as a statement.
      --   This wrapper is used when an expression is executed for its side effects
      --   (e.g., a function call or assignment) rather than its return value.
      --   Usually corresponds to an expression followed by a semicolon.
      --   @param Ast The wrapped expression.

    | ABlock [Ast]
      -- ^ Represents a scoped block of code (enclosed in braces `{}`).
      --   Used to group multiple statements into a single logical unit,
      --   typically for function bodies, loop bodies, or conditional branches.
      --   @param [Ast] The list of statements contained in the block.

    | ABreak
      -- ^ Represents a 'break' statement.
      --   Used to immediately terminate the nearest enclosing loop (for, while).
      --   Control flow continues at the next statement following the loop.

    | APos Int Int Ast
      -- ^ Represents a source code position wrapper.
      --   Used for precise error reporting (line, column).
      --   This node wraps another AST node without changing its semantics.
      --   @param Int The line number.
      --   @param Int The column number.
      --   @param Ast The wrapped AST node.

    deriving (Int -> Ast -> ShowS
[Ast] -> ShowS
Ast -> String
(Int -> Ast -> ShowS)
-> (Ast -> String) -> ([Ast] -> ShowS) -> Show Ast
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Ast -> ShowS
showsPrec :: Int -> Ast -> ShowS
$cshow :: Ast -> String
show :: Ast -> String
$cshowList :: [Ast] -> ShowS
showList :: [Ast] -> ShowS
Show, Ast -> Ast -> Bool
(Ast -> Ast -> Bool) -> (Ast -> Ast -> Bool) -> Eq Ast
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Ast -> Ast -> Bool
== :: Ast -> Ast -> Bool
$c/= :: Ast -> Ast -> Bool
/= :: Ast -> Ast -> Bool
Eq)

showAst :: Ast -> String
showAst :: Ast -> String
showAst (AInteger IntValue
i) = Int -> String
forall a. Show a => a -> String
Prelude.show (Int -> String) -> Int -> String
forall a b. (a -> b) -> a -> b
$ IntValue -> Int
intValueToInt IntValue
i
showAst (ABool Bool
True) = String
"#t"
showAst (ABool Bool
False) = String
"#f"
showAst (ASymbol Text
s) = Text -> String
DT.unpack Text
s
showAst (AList [Ast]
xs) = String
"(" String -> ShowS
forall a. [a] -> [a] -> [a]
++ [String] -> String
Prelude.unwords ((Ast -> String) -> [Ast] -> [String]
forall a b. (a -> b) -> [a] -> [b]
Prelude.map Ast -> String
showAst [Ast]
xs) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
")"
showAst (ADefineLambda [Text]
_ Ast
_) = String
"#<lambda>"
showAst (APos Int
_ Int
_ Ast
ast) = Ast -> String
showAst Ast
ast
showAst (ABlock [Ast]
xs) = String
"{ " String -> ShowS
forall a. [a] -> [a] -> [a]
++ [String] -> String
unwords ((Ast -> String) -> [Ast] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Ast -> String
showAst [Ast]
xs) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" }"
showAst Ast
ABreak = String
"break"
showAst Ast
other = Ast -> String
forall a. Show a => a -> String
Prelude.show Ast
other
-- TO DO: add new AST lines

printAst :: Ast -> IO ()
printAst :: Ast -> IO ()
printAst Ast
ast = String -> IO ()
putStrLn (Ast -> String
forall a. Show a => a -> String
Prelude.show Ast
ast)
-- TO DO: replace function by an AST tree view

-- | Recursively removes all position wrappers (APos) from the AST.
--
-- This is useful for:
-- Pretty printing (to avoid cluttering the output with position data).
-- Testing (to compare AST structure without worrying about line numbers).
--
-- @param Ast The AST potentially containing APos nodes.
-- @return Ast The cleaned AST with purely structural nodes.
cleanAst :: Ast -> Ast
cleanAst :: Ast -> Ast
cleanAst (APos Int
_ Int
_ Ast
ast) = Ast -> Ast
cleanAst Ast
ast
cleanAst (ABlock [Ast]
xs) = [Ast] -> Ast
ABlock ((Ast -> Ast) -> [Ast] -> [Ast]
forall a b. (a -> b) -> [a] -> [b]
map Ast -> Ast
cleanAst [Ast]
xs)
cleanAst (AList [Ast]
xs) = [Ast] -> Ast
AList ((Ast -> Ast) -> [Ast] -> [Ast]
forall a b. (a -> b) -> [a] -> [b]
map Ast -> Ast
cleanAst [Ast]
xs)
cleanAst (ADefineFunc Text
n [(Text, Text)]
a Text
r Ast
b) = Text -> [(Text, Text)] -> Text -> Ast -> Ast
ADefineFunc Text
n [(Text, Text)]
a Text
r (Ast -> Ast
cleanAst Ast
b)
cleanAst (ADefineLambda [Text]
args Ast
body) = [Text] -> Ast -> Ast
ADefineLambda [Text]
args (Ast -> Ast
cleanAst Ast
body)
cleanAst (ASetVar Text
n Text
t Ast
expr) = Text -> Text -> Ast -> Ast
ASetVar Text
n Text
t (Ast -> Ast
cleanAst Ast
expr)
cleanAst (ASetStruct Text
n [(Text, Ast)]
fields) =
    Text -> [(Text, Ast)] -> Ast
ASetStruct Text
n (((Text, Ast) -> (Text, Ast)) -> [(Text, Ast)] -> [(Text, Ast)]
forall a b. (a -> b) -> [a] -> [b]
map (\(Text
f, Ast
a) -> (Text
f, Ast -> Ast
cleanAst Ast
a)) [(Text, Ast)]
fields)
cleanAst (ACall Ast
func [Ast]
args) = Ast -> [Ast] -> Ast
ACall (Ast -> Ast
cleanAst Ast
func) ((Ast -> Ast) -> [Ast] -> [Ast]
forall a b. (a -> b) -> [a] -> [b]
map Ast -> Ast
cleanAst [Ast]
args)
cleanAst (AIf Ast
c Ast
t Ast
e) = Ast -> Ast -> Ast -> Ast
AIf (Ast -> Ast
cleanAst Ast
c) (Ast -> Ast
cleanAst Ast
t) (Ast -> Ast
cleanAst Ast
e)
cleanAst (AWhile Ast
c Ast
b) = Ast -> Ast -> Ast
AWhile (Ast -> Ast
cleanAst Ast
c) (Ast -> Ast
cleanAst Ast
b)
cleanAst (AFor Ast
i Ast
c Ast
u Ast
b) =
    Ast -> Ast -> Ast -> Ast -> Ast
AFor (Ast -> Ast
cleanAst Ast
i) (Ast -> Ast
cleanAst Ast
c) (Ast -> Ast
cleanAst Ast
u) (Ast -> Ast
cleanAst Ast
b)
cleanAst (AReturn Ast
e) = Ast -> Ast
AReturn (Ast -> Ast
cleanAst Ast
e)
cleanAst (AExprStmt Ast
e) = Ast -> Ast
AExprStmt (Ast -> Ast
cleanAst Ast
e)
cleanAst (AAccessStruct Ast
obj Text
field) = Ast -> Text -> Ast
AAccessStruct (Ast -> Ast
cleanAst Ast
obj) Text
field
cleanAst Ast
ABreak = Ast
ABreak
cleanAst Ast
other = Ast
other