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

module Parser.ImportSystem (resolveImports, constructForAst) where

import Control.Exception (try, IOException)
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Text.Megaparsec.Error (errorBundlePretty)
import Data.List (isSuffixOf)
import AST.Ast (Ast(..))
import Parser.Statement (parseALL)

-- | Main entry point used by the Compiler/VM.
-- Resolves all 'AImport' nodes within a list of AST statements.
--
-- @param asts: The initial list of AST nodes (parsed from the main file).
-- @return: Either an error message (String) or the fully resolved AST list.
resolveImports :: [Ast] -> IO (Either String [Ast])
resolveImports :: [Ast] -> IO (Either String [Ast])
resolveImports [Ast]
asts = do
    res <- [String] -> [Ast] -> IO (Either String ([Ast], [String]))
resolveImportsRec [] [Ast]
asts
    case res of
        Left String
err -> Either String [Ast] -> IO (Either String [Ast])
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String [Ast]
forall a b. a -> Either a b
Left String
err)
        Right ([Ast]
newAsts, [String]
_) -> Either String [Ast] -> IO (Either String [Ast])
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ([Ast] -> Either String [Ast]
forall a b. b -> Either a b
Right [Ast]
newAsts)

-- | Recursive worker that processes a list of nodes threading the 'visited' state.
--
-- @param visited: List of file paths already imported (stack trace for cycles).
-- @param nodes: The list of AST nodes to process.
-- @return: Tuple (Resolved ASTs, Updated Visited Stack).
resolveImportsRec :: [String] -> [Ast] -> IO (Either String ([Ast], [String]))
resolveImportsRec :: [String] -> [Ast] -> IO (Either String ([Ast], [String]))
resolveImportsRec [String]
visited [] = Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (([Ast], [String]) -> Either String ([Ast], [String])
forall a b. b -> Either a b
Right ([], [String]
visited))
resolveImportsRec [String]
visited (Ast
node:[Ast]
xs) = do
    processedHead <- [String] -> Ast -> IO (Either String ([Ast], [String]))
processNode [String]
visited Ast
node
    handleRecResult processedHead xs

-- | Helper to process the tail of the list after the head is resolved.
--
-- @param headResult: The result of processing the first node.
-- @param xs: The remaining nodes to process.
handleRecResult :: Either String ([Ast], [String]) -> [Ast] ->
    IO (Either String ([Ast], [String]))
handleRecResult :: Either String ([Ast], [String])
-> [Ast] -> IO (Either String ([Ast], [String]))
handleRecResult (Left String
err) [Ast]
_ = Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err)
handleRecResult (Right ([Ast]
headNodes, [String]
visitedAfterHead)) [Ast]
xs = do
    processedTail <- [String] -> [Ast] -> IO (Either String ([Ast], [String]))
resolveImportsRec [String]
visitedAfterHead [Ast]
xs
    return $ case processedTail of
        Left String
err -> String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err
        Right ([Ast]
tailNodes, [String]
visitedAfterTail) ->
            ([Ast], [String]) -> Either String ([Ast], [String])
forall a b. b -> Either a b
Right ([Ast]
headNodes [Ast] -> [Ast] -> [Ast]
forall a. [a] -> [a] -> [a]
++ [Ast]
tailNodes, [String]
visitedAfterTail)

-- | Safe file reading wrapper.
--
-- @param path: Path to the .npy file.
-- @return: The file content or an IO Error message.
readImportFile :: String -> IO (Either String T.Text)
readImportFile :: String -> IO (Either String Text)
readImportFile String
path = do
    res <- IO Text -> IO (Either IOException Text)
forall e a. Exception e => IO a -> IO (Either e a)
try (String -> IO Text
TIO.readFile String
path) :: IO (Either IOException T.Text)
    return $ case res of
        Left IOException
ex -> String -> Either String Text
forall a b. a -> Either a b
Left (String
"IO Error reading import '"
            String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
path String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"': " String -> String -> String
forall a. [a] -> [a] -> [a]
++ IOException -> String
forall a. Show a => a -> String
show IOException
ex)
        Right Text
txt -> Text -> Either String Text
forall a b. b -> Either a b
Right Text
txt

-- | Parses an imported file and recursively resolves its own imports.
--
-- @param visited: Current stack of imported files.
-- @param path: The filename being processed.
-- @param content: The raw text content of the file.
parseImportContent :: [String] -> String -> T.Text ->
    IO (Either String ([Ast], [String]))
parseImportContent :: [String] -> String -> Text -> IO (Either String ([Ast], [String]))
parseImportContent [String]
visited String
path Text
content = case Text -> Either (ParseErrorBundle Text Void) [Ast]
parseALL Text
content of
    Left ParseErrorBundle Text Void
err -> Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left (String
"Parse error in '" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
path String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"':\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++
        ParseErrorBundle Text Void -> String
forall s e.
(VisualStream s, TraversableStream s, ShowErrorComponent e) =>
ParseErrorBundle s e -> String
errorBundlePretty ParseErrorBundle Text Void
err))
    Right [Ast]
asts -> [String] -> [Ast] -> IO (Either String ([Ast], [String]))
resolveImportsRec (String
path String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
visited) [Ast]
asts

-- | Validates the import path (extension) before processing.
processImport :: [String] -> T.Text -> IO (Either String ([Ast], [String]))
processImport :: [String] -> Text -> IO (Either String ([Ast], [String]))
processImport [String]
visited Text
path =
    let pathStr :: String
pathStr = Text -> String
T.unpack Text
path
    in if Bool -> Bool
not (String
".npy" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isSuffixOf` String
pathStr)
        then Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left (String -> Either String ([Ast], [String]))
-> String -> Either String ([Ast], [String])
forall a b. (a -> b) -> a -> b
$ String
"Import Error: File '" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
pathStr String -> String -> String
forall a. [a] -> [a] -> [a]
++
            String
"' must have .npy extension")
        else [String] -> String -> IO (Either String ([Ast], [String]))
processImportLogic [String]
visited String
pathStr

-- | Core logic for imports: Cycle detection and File Loading.
--
-- @param visited: The import stack.
-- @param pathStr: The file to import.
processImportLogic :: [String] -> String ->
    IO (Either String ([Ast], [String]))
processImportLogic :: [String] -> String -> IO (Either String ([Ast], [String]))
processImportLogic [String]
visited String
pathStr
    | String
pathStr String -> [String] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String]
visited = Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left (String -> Either String ([Ast], [String]))
-> String -> Either String ([Ast], [String])
forall a b. (a -> b) -> a -> b
$
        String
"Circular or duplicate import detected: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
pathStr String -> String -> String
forall a. [a] -> [a] -> [a]
++
        String
" is already in the import stack " String -> String -> String
forall a. [a] -> [a] -> [a]
++ [String] -> String
forall a. Show a => a -> String
show [String]
visited)
    | Bool
otherwise = do
        content <- String -> IO (Either String Text)
readImportFile String
pathStr
        case content of
            Left String
err -> Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err)
            Right Text
txt -> [String] -> String -> Text -> IO (Either String ([Ast], [String]))
parseImportContent [String]
visited String
pathStr Text
txt

-- | Dispatcher function.
-- Routes AST nodes to their specific handlers to resolve nested imports.
processNode :: [String] -> Ast -> IO (Either String ([Ast], [String]))
processNode :: [String] -> Ast -> IO (Either String ([Ast], [String]))
processNode [String]
visited (AImport Text
path) = [String] -> Text -> IO (Either String ([Ast], [String]))
processImport [String]
visited Text
path
processNode [String]
visited (AList [Ast]
subStmts) = [String] -> [Ast] -> IO (Either String ([Ast], [String]))
processAList [String]
visited [Ast]
subStmts
processNode [String]
visited (ADefineFunc Text
name [(Text, Text)]
args Text
ret Ast
body) =
    [String]
-> Text
-> [(Text, Text)]
-> Text
-> Ast
-> IO (Either String ([Ast], [String]))
processFunc [String]
visited Text
name [(Text, Text)]
args Text
ret Ast
body
processNode [String]
visited (ADefineLambda [Text]
args Ast
body) =
    [String] -> [Text] -> Ast -> IO (Either String ([Ast], [String]))
processLambda [String]
visited [Text]
args Ast
body
processNode [String]
visited (AWhile Ast
cond Ast
body) = [String] -> Ast -> Ast -> IO (Either String ([Ast], [String]))
processWhile [String]
visited Ast
cond Ast
body
processNode [String]
visited (AFor Ast
i Ast
c Ast
u Ast
b) = [String]
-> Ast -> Ast -> Ast -> Ast -> IO (Either String ([Ast], [String]))
processFor [String]
visited Ast
i Ast
c Ast
u Ast
b
processNode [String]
visited (AIf Ast
c Ast
t Ast
e) = [String]
-> Ast -> Ast -> Ast -> IO (Either String ([Ast], [String]))
processIf [String]
visited Ast
c Ast
t Ast
e
processNode [String]
visited (APos Int
l Int
c Ast
ast) = [String]
-> Int -> Int -> Ast -> IO (Either String ([Ast], [String]))
processAPos [String]
visited Int
l Int
c Ast
ast
processNode [String]
visited Ast
other = Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (([Ast], [String]) -> Either String ([Ast], [String])
forall a b. b -> Either a b
Right ([Ast
other], [String]
visited))

-- | Resolves imports inside a block (AList).
processAList :: [String] -> [Ast] -> IO (Either String ([Ast], [String]))
processAList :: [String] -> [Ast] -> IO (Either String ([Ast], [String]))
processAList [String]
visited [Ast]
subStmts = do
    res <- [String] -> [Ast] -> IO (Either String ([Ast], [String]))
resolveImportsRec [String]
visited [Ast]
subStmts
    case res of
        Left String
err -> Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err)
        Right ([Ast]
newStmts, [String]
newVisited) ->
            Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (([Ast], [String]) -> Either String ([Ast], [String])
forall a b. b -> Either a b
Right ([[Ast] -> Ast
AList [Ast]
newStmts], [String]
newVisited))

-- | Resolves imports inside a Function body.
processFunc :: [String] -> T.Text -> [(T.Text, T.Text)] -> T.Text -> Ast ->
    IO (Either String ([Ast], [String]))
processFunc :: [String]
-> Text
-> [(Text, Text)]
-> Text
-> Ast
-> IO (Either String ([Ast], [String]))
processFunc [String]
visited Text
name [(Text, Text)]
args Text
ret Ast
body = do
    res <- [String] -> Ast -> IO (Either String (Ast, [String]))
processSingleNode [String]
visited Ast
body
    case res of
        Left String
err -> Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err)
        Right (Ast
newBody, [String]
newVisited) ->
            Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (([Ast], [String]) -> Either String ([Ast], [String])
forall a b. b -> Either a b
Right ([Text -> [(Text, Text)] -> Text -> Ast -> Ast
ADefineFunc Text
name [(Text, Text)]
args Text
ret Ast
newBody], [String]
newVisited))

-- | Resolves imports inside a Lambda body.
processLambda :: [String] -> [T.Text] -> Ast ->
    IO (Either String ([Ast], [String]))
processLambda :: [String] -> [Text] -> Ast -> IO (Either String ([Ast], [String]))
processLambda [String]
visited [Text]
args Ast
body = do
    res <- [String] -> Ast -> IO (Either String (Ast, [String]))
processSingleNode [String]
visited Ast
body
    case res of
        Left String
err -> Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err)
        Right (Ast
newBody, [String]
newVisited) ->
            Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (([Ast], [String]) -> Either String ([Ast], [String])
forall a b. b -> Either a b
Right ([[Text] -> Ast -> Ast
ADefineLambda [Text]
args Ast
newBody], [String]
newVisited))

-- | Resolves imports inside a While loop (Condition and Body).
processWhile :: [String] -> Ast -> Ast -> IO (Either String ([Ast], [String]))
processWhile :: [String] -> Ast -> Ast -> IO (Either String ([Ast], [String]))
processWhile [String]
visited Ast
cond Ast
body = do
    resCond <- [String] -> Ast -> IO (Either String (Ast, [String]))
processSingleNode [String]
visited Ast
cond
    processWhileBody resCond body

-- | Second step of While processing (The Body).
--
-- @param resCond: Result of processing the condition.
-- @param body: The loop body to process next.
processWhileBody :: Either String (Ast, [String]) -> Ast ->
    IO (Either String ([Ast], [String]))
processWhileBody :: Either String (Ast, [String])
-> Ast -> IO (Either String ([Ast], [String]))
processWhileBody (Left String
err) Ast
_ = Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err)
processWhileBody (Right (Ast
nc, [String]
visitedAfterCond)) Ast
body = do
    resBody <- [String] -> Ast -> IO (Either String (Ast, [String]))
processSingleNode [String]
visitedAfterCond Ast
body
    return $ case resBody of
        Left String
err -> String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err
        Right (Ast
nb, [String]
visitedAfterBody) ->
            ([Ast], [String]) -> Either String ([Ast], [String])
forall a b. b -> Either a b
Right ([Ast -> Ast -> Ast
AWhile Ast
nc Ast
nb], [String]
visitedAfterBody)

-- | Helper used by unit tests to reconstruct AFor nodes.
constructForAst :: [Ast] -> Either String [Ast]
constructForAst :: [Ast] -> Either String [Ast]
constructForAst [Ast
ni, Ast
nc, Ast
nu, Ast
nb] = [Ast] -> Either String [Ast]
forall a b. b -> Either a b
Right [Ast -> Ast -> Ast -> Ast -> Ast
AFor Ast
ni Ast
nc Ast
nu Ast
nb]
constructForAst [Ast]
_ =
    String -> Either String [Ast]
forall a b. a -> Either a b
Left String
"Internal Error: Invalid argument count in For loop processing"

-- | Start of For Loop processing.
--
-- Abbreviations used to fit 80 cols:
-- i = Init (Initialization AST)
-- c = Cond (Condition AST)
-- u = Update (Increment AST)
-- b = Body (Loop body AST)
processFor :: [String] -> Ast -> Ast -> Ast -> Ast ->
    IO (Either String ([Ast], [String]))
processFor :: [String]
-> Ast -> Ast -> Ast -> Ast -> IO (Either String ([Ast], [String]))
processFor [String]
visited Ast
i Ast
c Ast
u Ast
b = do
    resI <- [String] -> Ast -> IO (Either String (Ast, [String]))
processSingleNode [String]
visited Ast
i
    processForCond resI c u b

-- | Second step of For Loop: The Condition.
--
-- ni = New Init (Result of previous step)
-- v1 = Visited list after processing Init
processForCond :: Either String (Ast, [String]) -> Ast -> Ast -> Ast ->
    IO (Either String ([Ast], [String]))
processForCond :: Either String (Ast, [String])
-> Ast -> Ast -> Ast -> IO (Either String ([Ast], [String]))
processForCond (Left String
err) Ast
_ Ast
_ Ast
_ = Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err)
processForCond (Right (Ast
ni, [String]
v1)) Ast
c Ast
u Ast
b = do
    resC <- [String] -> Ast -> IO (Either String (Ast, [String]))
processSingleNode [String]
v1 Ast
c
    processForUpdate resC ni u b

-- | Third step of For Loop: The Update/Increment.
--
-- nc = New Condition
-- v2 = Visited list after processing Condition
processForUpdate :: Either String (Ast, [String]) -> Ast -> Ast -> Ast ->
    IO (Either String ([Ast], [String]))
processForUpdate :: Either String (Ast, [String])
-> Ast -> Ast -> Ast -> IO (Either String ([Ast], [String]))
processForUpdate (Left String
err) Ast
_ Ast
_ Ast
_ = Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err)
processForUpdate (Right (Ast
nc, [String]
v2)) Ast
ni Ast
u Ast
b = do
    resU <- [String] -> Ast -> IO (Either String (Ast, [String]))
processSingleNode [String]
v2 Ast
u
    processForBody resU ni nc b

-- | Final step of For Loop: The Body & Reassembly.
--
-- nu = New Update
-- v3 = Visited list after processing Update
-- nb = New Body
-- v4 = Final Visited list
processForBody :: Either String (Ast, [String]) -> Ast -> Ast -> Ast ->
    IO (Either String ([Ast], [String]))
processForBody :: Either String (Ast, [String])
-> Ast -> Ast -> Ast -> IO (Either String ([Ast], [String]))
processForBody (Left String
err) Ast
_ Ast
_ Ast
_ = Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err)
processForBody (Right (Ast
nu, [String]
v3)) Ast
ni Ast
nc Ast
b = do
    resB <- [String] -> Ast -> IO (Either String (Ast, [String]))
processSingleNode [String]
v3 Ast
b
    return $ case resB of
        Left String
err -> String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err
        Right (Ast
nb, [String]
v4) -> ([Ast], [String]) -> Either String ([Ast], [String])
forall a b. b -> Either a b
Right ([Ast -> Ast -> Ast -> Ast -> Ast
AFor Ast
ni Ast
nc Ast
nu Ast
nb], [String]
v4)

-- | Start of If Statement processing.
--
-- c = Condition
-- t = Then block
-- e = Else block
processIf :: [String] -> Ast -> Ast -> Ast ->
    IO (Either String ([Ast], [String]))
processIf :: [String]
-> Ast -> Ast -> Ast -> IO (Either String ([Ast], [String]))
processIf [String]
visited Ast
c Ast
t Ast
e = do
    resC <- [String] -> Ast -> IO (Either String (Ast, [String]))
processSingleNode [String]
visited Ast
c
    processIfThen resC t e

-- | Second step of If: The Then Branch.
--
-- nc = New Condition
-- v1 = Visited list after processing Condition
processIfThen :: Either String (Ast, [String]) -> Ast -> Ast ->
    IO (Either String ([Ast], [String]))
processIfThen :: Either String (Ast, [String])
-> Ast -> Ast -> IO (Either String ([Ast], [String]))
processIfThen (Left String
err) Ast
_ Ast
_ = Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err)
processIfThen (Right (Ast
nc, [String]
v1)) Ast
t Ast
e = do
    resT <- [String] -> Ast -> IO (Either String (Ast, [String]))
processSingleNode [String]
v1 Ast
t
    processIfElse resT nc e

-- | Final step of If: The Else Branch & Reassembly.
--
-- nt = New Then block
-- v2 = Visited list after processing Then
-- ne = New Else block
processIfElse :: Either String (Ast, [String]) -> Ast -> Ast ->
    IO (Either String ([Ast], [String]))
processIfElse :: Either String (Ast, [String])
-> Ast -> Ast -> IO (Either String ([Ast], [String]))
processIfElse (Left String
err) Ast
_ Ast
_ = Either String ([Ast], [String])
-> IO (Either String ([Ast], [String]))
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err)
processIfElse (Right (Ast
nt, [String]
v2)) Ast
nc Ast
e = do
    resE <- [String] -> Ast -> IO (Either String (Ast, [String]))
processSingleNode [String]
v2 Ast
e
    return $ case resE of
        Left String
err -> String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err
        Right (Ast
ne, [String]
v3) -> ([Ast], [String]) -> Either String ([Ast], [String])
forall a b. b -> Either a b
Right ([Ast -> Ast -> Ast -> Ast
AIf Ast
nc Ast
nt Ast
ne], [String]
v3)

-- | Processes Position nodes (APos).
--
-- l = Line number
-- c = Column number
processAPos :: [String] -> Int -> Int -> Ast ->
    IO (Either String ([Ast], [String]))
processAPos :: [String]
-> Int -> Int -> Ast -> IO (Either String ([Ast], [String]))
processAPos [String]
visited Int
l Int
c Ast
ast = do
    res <- [String] -> Ast -> IO (Either String ([Ast], [String]))
processNode [String]
visited Ast
ast
    return $ case res of
        Left String
err -> String -> Either String ([Ast], [String])
forall a b. a -> Either a b
Left String
err
        Right ([Ast
single], [String]
newVisited) ->
            ([Ast], [String]) -> Either String ([Ast], [String])
forall a b. b -> Either a b
Right ([Int -> Int -> Ast -> Ast
APos Int
l Int
c Ast
single], [String]
newVisited)
        Right ([Ast]
multiple, [String]
newVisited) -> ([Ast], [String]) -> Either String ([Ast], [String])
forall a b. b -> Either a b
Right ([Ast]
multiple, [String]
newVisited)

-- | Process a node that is expected to be a single expression.
--
-- Handles unboxing of lists if necessary (e.g., resolving AVoid).
processSingleNode :: [String] -> Ast -> IO (Either String (Ast, [String]))
processSingleNode :: [String] -> Ast -> IO (Either String (Ast, [String]))
processSingleNode [String]
visited Ast
node = do
    res <- [String] -> Ast -> IO (Either String ([Ast], [String]))
processNode [String]
visited Ast
node
    return $ case res of
        Left String
err -> String -> Either String (Ast, [String])
forall a b. a -> Either a b
Left String
err
        Right ([], [String]
newVisited) -> (Ast, [String]) -> Either String (Ast, [String])
forall a b. b -> Either a b
Right (Ast
AVoid, [String]
newVisited)
        Right ([Ast
x], [String]
newVisited) -> (Ast, [String]) -> Either String (Ast, [String])
forall a b. b -> Either a b
Right (Ast
x, [String]
newVisited)
        Right (Ast
x:[Ast]
_, [String]
newVisited) -> (Ast, [String]) -> Either String (Ast, [String])
forall a b. b -> Either a b
Right (Ast
x, [String]
newVisited)