{-
-- EPITECH PROJECT, 2025
-- Glados
-- File description:
-- Expression.hs
-}

{-|
Module      : Parser.Expression
Description : Expression parser handling math, logic, and types.
Stability   : stable

This module handles the parsing of values and expressions using 'makeExprParser'.
It supports:
- Integers, Booleans, Strings, Chars, Lists
- Variables and Function calls
- Operator precedence (PEMDAS, etc.)
-}
module Parser.Expression (pExpr) where

import Text.Megaparsec
import Text.Megaparsec.Char
import Control.Monad.Combinators.Expr
import qualified Data.Text as DT
import Data.Char (ord)

import AST.Ast (Ast(..))
import Common.Type.Integer (fitInteger, IntValue(..))
import qualified Text.Megaparsec.Char.Lexer as L
import Parser.Lexer

-- | Helper to create a prefix unary operator AST node.
--
-- Wraps the operand in a function call (e.g., !x -> Call "!" [x]).
prefix :: DT.Text -> (Ast -> Ast)
prefix :: Text -> Ast -> Ast
prefix Text
name = \Ast
a -> Ast -> [Ast] -> Ast
ACall (Text -> Ast
ASymbol Text
name) [Ast
a]

-- | Parse a structure member access suffix.
--
-- Example: .field
-- Returns a AStructAccess node instead of a function call.
-- The field name is converted to a list of characters string for the AST.
pMemberSuffix :: Parser (Ast -> Ast)
pMemberSuffix :: Parser (Ast -> Ast)
pMemberSuffix = do
    _ <- Text -> Parser Text
symbol (String -> Text
DT.pack String
".")
    fieldName <- pIdentifier
    return (\Ast
obj -> Ast -> Text -> Ast
AAccessStruct Ast
obj Text
fieldName)

-- | Parse a signed decimal integer (e.g., 42, -42, +10).
--
-- Uses 'L.signed' to handle the optional sign (+ or -).
-- We pass (return ()) as the space consumer to L.signed to forbid 
-- spaces between the sign and the digits (e.g. "-42" is valid, "- 42" is not).
pInteger :: Parser Ast
pInteger :: Parser Ast
pInteger = (Parser Ast -> Parser Ast
forall a. Parser a -> Parser a
lexeme (Parser Ast -> Parser Ast) -> Parser Ast -> Parser Ast
forall a b. (a -> b) -> a -> b
$ do
    val <- ParsecT Void Text Identity ()
-> ParsecT Void Text Identity Integer
-> ParsecT Void Text Identity Integer
forall e s (m :: * -> *) a.
(MonadParsec e s m, Token s ~ Char, Num a) =>
m () -> m a -> m a
L.signed (() -> ParsecT Void Text Identity ()
forall a. a -> ParsecT Void Text Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return ()) ParsecT Void Text Identity Integer
forall e s (m :: * -> *) a.
(MonadParsec e s m, Token s ~ Char, Num a) =>
m a
L.decimal
    return (AInteger (fitInteger val))) Parser Ast -> String -> Parser Ast
forall e s (m :: * -> *) a.
MonadParsec e s m =>
m a -> String -> m a
<?> String
"integer"

-- | Parse an array index suffix.
--
-- Example: [i]
-- Returns a function that wraps the preceding expression in a 'nth' call.
pIndexSuffix :: Parser (Ast -> Ast)
pIndexSuffix :: Parser (Ast -> Ast)
pIndexSuffix = do
    _ <- Text -> Parser Text
symbol (String -> Text
DT.pack String
"[")
    indexExpr <- pExpr
    _ <- symbol (DT.pack "]")
    return (\Ast
arr -> Ast -> [Ast] -> Ast
ACall (Text -> Ast
ASymbol (String -> Text
DT.pack String
"nth")) [Ast
arr, Ast
indexExpr])

-- | Parse a function call suffix.
--
-- Example: (arg1, arg2)
-- Returns a function that wraps the preceding expression in a 'ACall' node.
pCallSuffix :: Parser (Ast -> Ast)
pCallSuffix :: Parser (Ast -> Ast)
pCallSuffix = do
    args <- Parser [Ast] -> Parser [Ast]
forall a. Parser a -> Parser a
parens (Parser Ast
pExpr Parser Ast -> Parser Text -> Parser [Ast]
forall (m :: * -> *) a sep. MonadPlus m => m a -> m sep -> m [a]
`sepBy` Parser Text
comma)
    return (\Ast
func -> Ast -> [Ast] -> Ast
ACall Ast
func [Ast]
args)

-- | Parse a boolean literal (True or False).
pBool :: Parser Ast
pBool :: Parser Ast
pBool = Parser Ast -> Parser Ast
forall a. Parser a -> Parser a
lexeme ([Parser Ast] -> Parser Ast
forall (f :: * -> *) (m :: * -> *) a.
(Foldable f, Alternative m) =>
f (m a) -> m a
choice
    [ Bool -> Ast
ABool Bool
True Ast -> ParsecT Void Text Identity (Tokens 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
<$ Tokens Text -> ParsecT Void Text Identity (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Tokens s -> m (Tokens s)
string (String -> Text
DT.pack String
"True")
    , Bool -> Ast
ABool Bool
False Ast -> ParsecT Void Text Identity (Tokens 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
<$ Tokens Text -> ParsecT Void Text Identity (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Tokens s -> m (Tokens s)
string (String -> Text
DT.pack String
"False")
    ]) Parser Ast -> String -> Parser Ast
forall e s (m :: * -> *) a.
MonadParsec e s m =>
m a -> String -> m a
<?> String
"boolean"

-- | Parse a string literal with escape sequences support.
--
-- Converts the string into a list of characters AST ([IChar]).
pString :: Parser Ast
pString :: Parser Ast
pString = (Parser Ast -> Parser Ast
forall a. Parser a -> Parser a
lexeme (Parser Ast -> Parser Ast) -> Parser Ast -> Parser Ast
forall a b. (a -> b) -> a -> b
$ do
    content <- ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
-> ParsecT Void Text Identity String
-> ParsecT Void Text Identity String
forall (m :: * -> *) open close a.
Applicative m =>
m open -> m close -> m a -> m a
between (Token Text -> ParsecT Void Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
char Char
Token Text
'"') (Token Text -> ParsecT Void Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
char Char
Token Text
'"') (ParsecT Void Text Identity Char
-> ParsecT Void Text Identity String
forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
many ParsecT Void Text Identity Char
pStringChar)
    return $ AList (map (AInteger . IChar . fromIntegral . ord) content)
    ) Parser Ast -> String -> Parser Ast
forall e s (m :: * -> *) a.
MonadParsec e s m =>
m a -> String -> m a
<?> String
"string"

-- | Parse a character literal enclosed in single quotes.
--
-- Example: 'c'
pChar :: Parser Ast
pChar :: Parser Ast
pChar = (Parser Ast -> Parser Ast
forall a. Parser a -> Parser a
lexeme (Parser Ast -> Parser Ast) -> Parser Ast -> Parser Ast
forall a b. (a -> b) -> a -> b
$ do
    _ <- Token Text -> ParsecT Void Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
char Char
Token Text
'\''
    c <- L.charLiteral
    _ <- char '\''
    return (AInteger (IChar (fromIntegral (ord c))))) Parser Ast -> String -> Parser Ast
forall e s (m :: * -> *) a.
MonadParsec e s m =>
m a -> String -> m a
<?> String
"character"

-- | Parse a list literal enclosed in brackets.
--
-- Example: [1, 2, True]
pListLiteral :: Parser Ast
pListLiteral :: Parser Ast
pListLiteral = (do
    _ <- Text -> Parser Text
symbol (String -> Text
DT.pack String
"[")
    exprs <- pExpr `sepBy` comma
    _ <- symbol (DT.pack "]")
    return (AList exprs)) Parser Ast -> String -> Parser Ast
forall e s (m :: * -> *) a.
MonadParsec e s m =>
m a -> String -> m a
<?> String
"list"

-- | Parse a variable or a function call.
--
-- Distinguishes between the two by checking for parentheses after the identifier:
--
-- * myVar parses as ASymbol "myVar"
-- * myFunc(1, 2) parses as Call "myFunc" [1, 2]
pVarOrCall :: Parser Ast
pVarOrCall :: Parser Ast
pVarOrCall = do
    name <- Parser Text
pIdentifier
    choice
        [ do
            args <- parens (pExpr `sepBy` comma)
            return (ACall (ASymbol name) args)
        , return (ASymbol name)
        ]

-- | Parse a field initialization within a 'new' expression.
--
-- Syntax: fieldName: value
pFieldInit :: Parser (DT.Text, Ast)
pFieldInit :: Parser (Text, Ast)
pFieldInit = do
    name <- Parser Text
pIdentifier
    _ <- symbol (DT.pack ":")
    val <- pExpr
    return (name, val)

-- | Parse a structure instantiation.
--
-- Syntax: new ClassName { field1: val1, ... }
pNew :: Parser Ast
pNew :: Parser Ast
pNew = do
    _ <- Text -> Parser Text
pKeyword (String -> Text
DT.pack String
"new")
    className <- pIdentifier
    fields <- braces (pFieldInit `sepBy` comma)
    return (ASetStruct className fields)

-- | Parse lambda
--
-- Syntax: lambda (arg1, arg2)
pLambda :: Parser Ast
pLambda :: Parser Ast
pLambda = do
    _ <- Text -> Parser Text
pKeyword (String -> Text
DT.pack String
"lambda")
    args <- parens (pIdentifier `sepBy` comma)
    _ <- optional (symbol (DT.pack "->")) 
    body <- pExpr
    return (ADefineLambda args body)

-- | Parse an IF expression (e.g., x = if (c) { 1 } else { 0 }).
-- Returns an AIf node. Note that 'else' is mandatory or defaults to Void.
pIfExpr :: Parser Ast
pIfExpr :: Parser Ast
pIfExpr = do
    _ <- Text -> Parser Text
pKeyword (String -> Text
DT.pack String
"if")
    cond <- parens pExpr
    thenExpr <- braces pExpr
    elseExpr <- option AVoid $ do
        _ <- pKeyword (DT.pack "else")
        braces pExpr
    return (AIf cond thenExpr elseExpr)

-- | Parse an escaped character code following a backslash.
--
-- Supported sequences:
-- * \\n : Newline
-- * \\r : Carriage return
-- * \\t : Tabulation
-- * \\0 : Null byte
-- * \\\\ : Literal backslash
-- * \\" : Literal double quote
pEscapeCode :: Parser Char
pEscapeCode :: ParsecT Void Text Identity Char
pEscapeCode = [ParsecT Void Text Identity Char]
-> ParsecT Void Text Identity Char
forall (f :: * -> *) (m :: * -> *) a.
(Foldable f, Alternative m) =>
f (m a) -> m a
choice
    [ Token Text -> ParsecT Void Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
char Char
Token Text
'n' ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
forall a b.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity b -> ParsecT Void Text Identity b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Char -> ParsecT Void Text Identity Char
forall a. a -> ParsecT Void Text Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return Char
'\n'
    , Token Text -> ParsecT Void Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
char Char
Token Text
'r' ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
forall a b.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity b -> ParsecT Void Text Identity b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Char -> ParsecT Void Text Identity Char
forall a. a -> ParsecT Void Text Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return Char
'\r'
    , Token Text -> ParsecT Void Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
char Char
Token Text
't' ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
forall a b.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity b -> ParsecT Void Text Identity b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Char -> ParsecT Void Text Identity Char
forall a. a -> ParsecT Void Text Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return Char
'\t'
    , Token Text -> ParsecT Void Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
char Char
Token Text
'0' ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
forall a b.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity b -> ParsecT Void Text Identity b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Char -> ParsecT Void Text Identity Char
forall a. a -> ParsecT Void Text Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return Char
'\0'
    , Token Text -> ParsecT Void Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
char Char
Token Text
'\\' ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
forall a b.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity b -> ParsecT Void Text Identity b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Char -> ParsecT Void Text Identity Char
forall a. a -> ParsecT Void Text Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return Char
'\\'
    , Token Text -> ParsecT Void Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
char Char
Token Text
'"' ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
forall a b.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity b -> ParsecT Void Text Identity b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Char -> ParsecT Void Text Identity Char
forall a. a -> ParsecT Void Text Identity a
forall (m :: * -> *) a. Monad m => a -> m a
return Char
'"'
    ]

-- | Parse a single character inside a string literal.
--
-- It attempts to parse an escape sequence first (starting with '\').
-- If it is not an escape sequence, it consumes any character except 
-- a double quote (which marks the end of the string).
pStringChar :: Parser Char
pStringChar :: ParsecT Void Text Identity Char
pStringChar = 
        ParsecT Void Text Identity Char -> ParsecT Void Text Identity Char
forall a. Parser a -> Parser a
forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
try (Token Text -> ParsecT Void Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
Token s -> m (Token s)
char Char
Token Text
'\\' ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
forall a b.
ParsecT Void Text Identity a
-> ParsecT Void Text Identity b -> ParsecT Void Text Identity b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> ParsecT Void Text Identity Char
pEscapeCode)
    ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
-> ParsecT Void Text Identity Char
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
<|> [Token Text] -> ParsecT Void Text Identity (Token Text)
forall (f :: * -> *) e s (m :: * -> *).
(Foldable f, MonadParsec e s m) =>
f (Token s) -> m (Token s)
noneOf String
[Token Text]
"\""

-- | Parse a term in an expression.
--
-- A term is the basic unit of an expression, such as literals,
-- variables, function calls, or parenthesized sub-expressions.
pTermBase :: Parser Ast
pTermBase :: Parser Ast
pTermBase = Parser Ast -> Parser Ast
withPos (Parser Ast -> Parser Ast) -> Parser Ast -> Parser Ast
forall a b. (a -> b) -> a -> b
$ [Parser Ast] -> Parser Ast
forall (f :: * -> *) (m :: * -> *) a.
(Foldable f, Alternative m) =>
f (m a) -> m a
choice
    [ 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
pNew, 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
pLambda
    , Parser Ast -> Parser Ast
forall a. Parser a -> Parser a
parens Parser Ast
pExpr , 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
pIfExpr
    , Parser Ast
pInteger
    , Parser Ast
pBool
    , Parser Ast
pChar
    , Parser Ast
pString
    , Parser Ast
pListLiteral
    , Parser Ast
pVarOrCall
    ]

-- | Parse a term followed by optional suffixes.
--
-- Handles chaining of array indexing and member access.
-- Example: arr[0].x parses 'arr', then applies '[0]', then applies '.x'.
pTerm :: Parser Ast
pTerm :: Parser Ast
pTerm = do
    base <- Parser Ast
pTermBase
    suffixes <- many (choice [try pCallSuffix, pIndexSuffix, pMemberSuffix])
    return (foldl (\Ast
acc Ast -> Ast
f -> Ast -> Ast
f Ast
acc) base suffixes)

-- | Helper to create a binary operator AST node.
--
-- Transforms an infix operator string (e.g., "+") into a 'Call' AST node.
binary :: DT.Text -> (Ast -> Ast -> Ast)
binary :: Text -> Ast -> Ast -> Ast
binary Text
name = \Ast
a Ast
b -> Ast -> [Ast] -> Ast
ACall (Text -> Ast
ASymbol Text
name) [Ast
a, Ast
b]

-- | Table of syntactic sugar prefix operators.
--
-- Includes logical NOT (!), increment (++), and decrement (--).
sugarSyntOps :: [Operator Parser Ast]
sugarSyntOps :: [Operator Parser Ast]
sugarSyntOps =
    [ Parser (Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a) -> Operator m a
Prefix (Text -> Ast -> Ast
prefix (String -> Text
DT.pack String
"!") (Ast -> Ast) -> Parser Text -> Parser (Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"!"))
    , Parser (Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a) -> Operator m a
Prefix (Ast -> Ast
incrementOps (Ast -> Ast) -> Parser Text -> Parser (Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"++"))
    , Parser (Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a) -> Operator m a
Prefix (Ast -> Ast
decrementOps (Ast -> Ast) -> Parser Text -> Parser (Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"--"))
    , Parser (Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a) -> Operator m a
Postfix (Ast -> Ast
incrementOps (Ast -> Ast) -> Parser Text -> Parser (Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"++"))
    , Parser (Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a) -> Operator m a
Postfix (Ast -> Ast
decrementOps (Ast -> Ast) -> Parser Text -> Parser (Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"--"))
    ]

-- | Table of multiplicative operators (*, /, %).
multiplicativeOps :: [Operator Parser Ast]
multiplicativeOps :: [Operator Parser Ast]
multiplicativeOps =
    [ Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
"*") (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"*"))
    , Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
"div") (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"/"))
    , Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
"mod") (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"%"))
    ]

-- | Table of additive operators (+, -).
additiveOps :: [Operator Parser Ast]
additiveOps :: [Operator Parser Ast]
additiveOps =
    [ Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
"+") (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"+"))
    , Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
"-") (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"-"))
    ]

-- | Table of comparison operators (==, <, >, etc).
comparisonOps :: [Operator Parser Ast]
comparisonOps :: [Operator Parser Ast]
comparisonOps =
    [ Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
"teq?")  (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Parser Text -> Parser Text
forall a. Parser a -> Parser a
forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
try (Text -> Parser Text
symbol (String -> Text
DT.pack String
"===")))
    , Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
"tneq?") (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Parser Text -> Parser Text
forall a. Parser a -> Parser a
forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
try (Text -> Parser Text
symbol (String -> Text
DT.pack String
"!==")))
    , Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
"eq?") (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"=="))
    , Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
"neq?") (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"!="))
    , Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
"<=")  (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Parser Text -> Parser Text
forall a. Parser a -> Parser a
forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
try (Text -> Parser Text
symbol (String -> Text
DT.pack String
"<=")))
    , Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
">=")  (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Parser Text -> Parser Text
forall a. Parser a -> Parser a
forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
try (Text -> Parser Text
symbol (String -> Text
DT.pack String
">=")))
    , Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
"<")   (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"<"))
    , Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
">")   (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
">"))
    ]

-- | Table for logical AND operator (&&).
-- It has higher precedence than OR but lower than comparison operators.
logicalAndOps :: [Operator Parser Ast]
logicalAndOps :: [Operator Parser Ast]
logicalAndOps =
    [ Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
"&&") (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"&&")) ]

-- | Table for logical OR operator (||).
-- It has the lowest precedence among logical operators.
logicalOrOps :: [Operator Parser Ast]
logicalOrOps :: [Operator Parser Ast]
logicalOrOps =
    [ Parser (Ast -> Ast -> Ast) -> Operator Parser Ast
forall (m :: * -> *) a. m (a -> a -> a) -> Operator m a
InfixL (Text -> Ast -> Ast -> Ast
binary (String -> Text
DT.pack String
"||") (Ast -> Ast -> Ast) -> Parser Text -> Parser (Ast -> Ast -> 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
<$ Text -> Parser Text
symbol (String -> Text
DT.pack String
"||")) ]

-- | Handle the increment operator (++).
--
-- If applied to a variable (ASymbol), transforms it into an assignment:
-- x = x + 1 (using 'auto' type inference).
-- Otherwise, treats it as a standard function call to "++".
incrementOps :: Ast -> Ast
incrementOps :: Ast -> Ast
incrementOps (ASymbol Text
name) = 
    Text -> Text -> Ast -> Ast
ASetVar Text
name (String -> Text
DT.pack String
"auto") (Ast -> [Ast] -> Ast
ACall (Text -> Ast
ASymbol (String -> Text
DT.pack String
"+"))
        [Text -> Ast
ASymbol Text
name, IntValue -> Ast
AInteger (Integer -> IntValue
fitInteger Integer
1)])
incrementOps (APos Int
l Int
c Ast
ast) = Int -> Int -> Ast -> Ast
APos Int
l Int
c (Ast -> Ast
incrementOps Ast
ast)
incrementOps Ast
other = Ast -> [Ast] -> Ast
ACall (Text -> Ast
ASymbol (String -> Text
DT.pack String
"++")) [Ast
other]

-- | Handle the decrement operator (--).
--
-- If applied to a variable (ASymbol), transforms it into an assignment:
-- x = x - 1 (using 'auto' type inference).
-- Otherwise, treats it as a standard function call to "--".
decrementOps :: Ast -> Ast
decrementOps :: Ast -> Ast
decrementOps (ASymbol Text
name) = 
    Text -> Text -> Ast -> Ast
ASetVar Text
name (String -> Text
DT.pack String
"auto") (Ast -> [Ast] -> Ast
ACall (Text -> Ast
ASymbol (String -> Text
DT.pack String
"-"))
        [Text -> Ast
ASymbol Text
name, IntValue -> Ast
AInteger (Integer -> IntValue
fitInteger Integer
1)])
decrementOps (APos Int
l Int
c Ast
ast) = Int -> Int -> Ast -> Ast
APos Int
l Int
c (Ast -> Ast
decrementOps Ast
ast)
decrementOps Ast
other = Ast -> [Ast] -> Ast
ACall (Text -> Ast
ASymbol (String -> Text
DT.pack String
"--")) [Ast
other]

-- | Combined operator table for expression parsing.
--
-- Defines the precedence order: Access ([]) > Unaire (!) > Math > Comparaison > AND > OR
opTable :: [[Operator Parser Ast]]
opTable :: [[Operator Parser Ast]]
opTable = [[Operator Parser Ast]
sugarSyntOps, [Operator Parser Ast]
multiplicativeOps, [Operator Parser Ast]
additiveOps,
    [Operator Parser Ast]
comparisonOps, [Operator Parser Ast]
logicalAndOps, [Operator Parser Ast]
logicalOrOps]

-- | Main expression parser.
--
-- Uses 'makeExprParser' to handle operator precedence and associativity automatically.
pExpr :: Parser Ast
pExpr :: Parser Ast
pExpr = Parser Ast -> [[Operator Parser Ast]] -> Parser Ast
forall (m :: * -> *) a.
MonadPlus m =>
m a -> [[Operator m a]] -> m a
makeExprParser Parser Ast
pTerm [[Operator Parser Ast]]
opTable