------------------------------------------------------------------------------ 
-- | 
-- Maintainer	: Ralf Laemmel, Joost Visser
-- Stability	: experimental
-- Portability	: portable
--
-- This module is part of 'Sdf2Haskell', a tool for generating a set of
-- Haskell data types from an SDF grammar. This module provides functionality
-- for calling the external parser SGLR from within Haskell and marshalling
-- the resulting abstract syntax tree to a strongly-typed Haskell term.

------------------------------------------------------------------------------

module SGLR where

import System.Exit 
import System.Environment (getProgName)
import System.Cmd (system)
import Data.Unique (newUnique, hashUnique)
import System.IO (readFile,hPutStrLn,stderr)
import System.Directory (removeFile)
import Data.ATerm.Lib (fromATerm, readATerm, ATermConvertible, 
                 dehyphenAST, afunCap, ATerm(..)
                )
import ImplodePT (compensateImplodePT)

------------------------------------------------------------------------------
-- * Interaction with the system.

-- | Call the external sglr parser.
sglr :: ATermConvertible a
     => FilePath	-- ^ table
     -> FilePath	-- ^ term
     -> String          -- ^ top sort
     -> IO a
sglr tableName termName sortName
  = do unique <- newUnique
       let newName = termName++(show . hashUnique $ unique)
       let asfixName = newName++".asfix"
       let afName = newName++".af"
       let sglrCmd = "sglr -p "++tableName++
                         " -i "++termName++
			 " -o "++asfixName++
			 " -s "++sortName
       let implodeCmd = "implodePT -t -ALclpqX"++ 
                                 " -i "++asfixName++
				 " -o "++afName
       progName <- getProgName
       errLn $ "["++progName++"] "++sglrCmd			 
       exitOK $ system sglrCmd
       errLn $ "["++progName++"] "++implodeCmd
       exitOK $ system implodeCmd
       af <- readFile afName
       --return . fromATerm . afunCap . implodeLit . readATerm . dehyphen $ af
       --let t1 = readATerm . dehyphen $ af
       let t1 = dehyphenAST . readATerm $ af
       --putStrLn $ "After dehyphen: \n"++show t1
       let t2 = compensateImplodePT t1
       --putStrLn $ "After implodeOpt: \n"++show t2
       let t3 = afunCap t2
       return . fromATerm $ t3


-- | Call the external sglr parser but only sends msgs on error and cleans
--   temporary files.
sglrSilent :: ATermConvertible a
      => FilePath
      -> FilePath
      -> String
      -> IO a
sglrSilent tableName termName sortName
  = do progName <- getProgName
       unique <- newUnique
       let newName   = termName ++ (show . hashUnique $ unique)
       let asfixName = newName ++ ".asfix"
    
       let sglrCmd    = concat [ "sglr -p ", tableName, " -i ", termName
                               , " -o ", asfixName, " -s ", sortName]
       exitCode <- system sglrCmd
    
       case exitCode of
          (ExitFailure code) -> do errLn $ "["++progName++"] "++sglrCmd
                                   removeFile asfixName
                                   fail $ "Exit code: " ++ show code
          (ExitSuccess)      -> return ()
    
       let afName    = newName ++ ".af"
       let implodeCmd = concat [ "implodePT -t -ALclpqX -i ", asfixName
                               , " -o ", afName]
       exitCode <- system implodeCmd    
    
       case exitCode of
          (ExitFailure code) -> do errLn $ "["++progName++"] "++implodeCmd
                                   removeFile afName
                                   fail $ "Exit code: " ++ show code
          (ExitSuccess)      -> return ()
    
       af <- readFile afName
       _  <- removeFile asfixName
       _  <- removeFile afName

       let t1 = dehyphenAST . readATerm $ af
       let t2 = compensateImplodePT t1
       let t3 = afunCap t2
       return . fromATerm $ t3
    

-- | Helper function for reporting errors and progress to stderr
errLn :: String -> IO ()
errLn str = hPutStrLn stderr str

-- | Ensure that the given computation exits with an error code
--   that indicates successful execution.       
exitOK :: IO ExitCode -> IO ()
exitOK io
  = do exitCode <- io
       case exitCode of
         (ExitSuccess)   -> return ()
	 (ExitFailure code) -> fail $ "Exit code: " ++ show code

	 
