module DDC.Driver.Command.Parse
        ( cmdParseFromFile
        , cmdParseSourceTetraFromFile
        , cmdParseCoreFromFile)
where
import DDC.Driver.Stage
import DDC.Build.Language
import DDC.Source.Tetra.Pretty
import DDC.Source.Tetra.Lexer           as ST
import DDC.Source.Tetra.Parser          as ST
import DDC.Core.Fragment                as C
import DDC.Core.Parser                  as C
import DDC.Core.Lexer                   as C
import DDC.Base.Parser                  as BP
import DDC.Data.Token                   as Token
import Control.Monad.Trans.Error
import Control.Monad.IO.Class
import System.FilePath
import System.Directory
import Control.Monad


-------------------------------------------------------------------------------
-- | Parse a module.
--   The result AST is printed to @stdout@.
--   Any errors are thrown in the `ErrorT` monad.
--
--   This function handle fragments of Disciple Core, as well as Source Tetra
--   modules. The language to use is determined by inspecting the file name
--   extension.
--
cmdParseFromFile 
        :: Config               -- ^ Driver config.
        -> FilePath             -- ^ Module file name.
        -> ErrorT String IO ()

cmdParseFromFile config filePath
 
 -- Parse a Disciple Source Tetra module.
 | ".dst"        <- takeExtension filePath
 =  cmdParseSourceTetraFromFile config filePath

 -- Parse a module in some fragment of Disciple Core.
 | Just language <- languageOfExtension (takeExtension filePath)
 = cmdParseCoreFromFile config language filePath

 -- Don't know how to parse this file.
 | otherwise
 = let  ext     = takeExtension filePath
   in   throwError $ "Cannot parse '" ++ ext ++ "' files."


-------------------------------------------------------------------------------
-- | Parse a Disciple Source Tetra module from a file.
--   The result AST is printed to @stdout@.
--   Any errors are thrown in the `ErrorT` monad.
cmdParseSourceTetraFromFile
        :: Config               -- ^ Driver config.
        -> FilePath             -- ^ Module file path.
        -> ErrorT String IO ()

cmdParseSourceTetraFromFile config filePath
 = do   
        -- Check that the file exists.
        exists  <- liftIO $ doesFileExist filePath
        when (not exists)
         $ throwError $ "No such file " ++ show filePath

        -- Read in the source file.
        src     <- liftIO $ readFile filePath

        -- Lex the source string.
        let toks    = ST.lexModuleString filePath 1 src

        when (configDump config)
         $ liftIO $ writeFile "dump.tetra-parse.tokens-sp" 
                  $ unlines $ map show toks

        when (configDump config)
         $ liftIO $ writeFile "dump.tetra-parse.tokens" 
                  $ unlines $ map show $ map Token.tokenTok toks

        let context = ST.Context
                    { ST.contextTrackedEffects          = True
                    , ST.contextTrackedClosures         = True
                    , ST.contextFunctionalEffects       = False
                    , ST.contextFunctionalClosures      = False }
                    
        case BP.runTokenParser
                C.describeTok filePath 
                (ST.pModule context) toks of
         Left err 
          ->    throwError (renderIndent $ ppr err)
         
         Right mm
          ->    liftIO $ putStrLn (renderIndent $ ppr mm)


-------------------------------------------------------------------------------
-- | Parse a Disciple Core module from a file.
--   The AST is printed to @stdout@.
--   Any errors are thrown in the `ErrorT` monad.
cmdParseCoreFromFile
        :: Config               -- ^ Driver config
        -> Language             -- ^ Core language definition.
        -> FilePath             -- ^ Module file path.
        -> ErrorT String IO ()

cmdParseCoreFromFile _config language filePath
 | Language bundle      <- language
 , fragment             <- bundleFragment  bundle
 , profile              <- fragmentProfile fragment
 = do   
        -- Check that the file exists.
        exists  <- liftIO $ doesFileExist filePath
        when (not exists)
         $ throwError $ "No such file " ++ show filePath

        -- Read in the source file.
        src     <- liftIO $ readFile filePath

        -- Lex the source string.
        let toks = (C.fragmentLexModule fragment) filePath 1 src

        case BP.runTokenParser
                C.describeTok filePath 
                (C.pModule (C.contextOfProfile profile)) toks of
         Left err
          ->    throwError (renderIndent $ ppr err)

         Right mm
          ->    liftIO $ putStrLn (renderIndent $ ppr mm)