module Main where
import System.Environment (getArgs)
import System.Directory (doesDirectoryExist, createDirectory, copyFile, doesFileExist)
import System.Directory.Tools (doesNotExistOrOldThan)
import System.Process (runProcess, waitForProcess)
import Control.Monad.Tools (whenM, unlessM, skipRet)
import Control.Applicative ((<$>))
import Text.RegexPR (gsubRegexPR)
import Text.ParserCombinators.MTLParse
import YJTools.Tribial (ghcMake)
import Data.Char (isSpace)
import Data.Maybe (maybeToList)
ehaskellDir, haskellSffx, ehsHandleStr, putStrStr :: String
ehaskellDir = "_ehs/"
haskellSffx = ".hs"
ehsHandleStr = "_ehs_handle"
putStrStr = "hPutStr " ++ ehsHandleStr
main :: IO ()
main = do
args <- getArgs
let [infile] = dropOptionO args
outfile = takeOptionO args
exeName = gsubRegexPR "\\." "_" infile
exeFile = ehaskellDir ++ exeName
srcFile = ehaskellDir ++ exeName ++ haskellSffx
cont <- readFile infile
unlessM (doesDirectoryExist ehaskellDir) $ createDirectory ehaskellDir
copyRequiredFile cont
whenM (doesNotExistOrOldThan srcFile infile) $
writeFile srcFile $ fst $ head $ runParse parseAll ("", cont)
whenM (doesNotExistOrOldThan exeFile srcFile) $
ghcMake exeName ehaskellDir >> return ()
runProcess exeFile (maybeToList outfile) Nothing Nothing Nothing Nothing Nothing
>>= waitForProcess
return ()
copyRequiredFile :: String -> IO ()
copyRequiredFile src = evalParseT copyRequiredFileParse ("",src) >> return ()
copyRequiredFileParse :: ParseT Char IO ()
copyRequiredFileParse = list crfp >> return ()
where
crfp = do
list $ do
still $ parseNot () $ tokens "<%%"
spot $ const True
tokens "<%%"
list $ spot isSpace
tokens "import"
list $ spot isSpace
mn <- neList (spot $ not . isSpace) >>= skipRet (still $ spot $ isSpace)
let sfn = mn ++ ".hs"
dfn = ehaskellDir ++ sfn
lift $ whenM (doesFileExist sfn) $ whenM (doesNotExistOrOldThan dfn sfn) $
copyFile sfn dfn
list $ spot isSpace
tokens "%%>"
takeOptionO :: [String] -> Maybe String
takeOptionO [] = Nothing
takeOptionO ("-o":f:_) = Just f
takeOptionO (_:as) = takeOptionO as
dropOptionO :: [String] -> [String]
dropOptionO [] = []
dropOptionO ("-o":_:as) = as
dropOptionO (a:as) = a : dropOptionO as
parseAll, parseInner :: Parse Char String
parse, parseApply :: Parse Char [ (Bool, String) ]
parseText, parseN, parseEq, parseEqEq, parseEqShow, parseEqEqShow, parseDef
:: Parse Char (Bool, String)
parseApplyBegin, parseApplyContinue, parseApplyEnd
:: Parse Char String
mkOutputText, mkOutputHere,
mkOutputCode, mkOutputShowCode, mkOutputReturnCode, mkOutputReturnShowCode
:: String -> String
getHandleStr :: String
parseAll
= ( myConcat . ((False, "main = do {\n"++getHandleStr):)
. (++[(False, " hClose " ++ ehsHandleStr ++ " }\n")])
. ((True, "import System.IO (stdout, hPutStr, openFile, IOMode(WriteMode), hClose)\n"):)
. ((True, "import System.Environment (getArgs)\n"):) )
<$> parse >>= endOfInput
getHandleStr = " " ++ ehsHandleStr ++
" <- getArgs >>= (\\args -> " ++
"if null args then return stdout else openFile (head args) WriteMode);\n"
parse = concat <$>
( greedyNeList $ (single parseText >>= \r -> still (parseNot r $ parseText))
`mplus`
single parseN
`mplus`
single parseEq
`mplus`
single parseEqEq
`mplus`
single parseEqShow
`mplus`
single parseEqEqShow
`mplus`
single parseDef
`mplus`
parseApply )
where single = ((:[]) <$>)
myConcat :: [ (Bool, [a]) ] -> [a]
myConcat lst
= concat (map snd $ filter fst lst) ++
concat (map snd $ filter (not . fst) lst)
parseText = do
cont <- greedyNeList $ do
still $ parseNot () $ tokens "<%"
spot $ const True
return $ (False, mkOutputText cont)
parseN = do
tokens "<%" >> still (parseNot () $ spot $ flip elem "-=%")
code <- parseInner
still (parseNot () $ tokenBack '-')
tokens "%>"
return $ (False, mkOutputHere code)
parseEq = do
tokens "<%=" >> still (parseNot () $ spot $ flip elem "=$")
code <- parseInner
tokens "%>"
return $ (False, mkOutputReturnCode code)
parseEqEq = do
tokens "<%==" >> still (parseNot () $ token '$')
code <- parseInner
tokens "%>"
return $ (False, mkOutputCode code)
parseEqShow = do
tokens "<%=$"
code <- parseInner
tokens "%>"
return $ (False, mkOutputReturnShowCode code)
parseEqEqShow = do
tokens "<%==$"
code <- parseInner
tokens "%>"
return $ (False, mkOutputShowCode code)
parseDef = do
tokens "<%%"
code <- parseInner
tokens "%%>"
return $ (True, code ++ ";\n")
parseApply = do
b <- parseApplyBegin
c <- surround <$> parse
cs <- list $ do
ci <- parseApplyContinue
t <- surround <$> parse
return $ (False, ci) : t
e <- parseApplyEnd
return $ (False, b) : c ++ concat cs ++ [(False, e)] ++ [(False, ";\n")]
where
surround = ( ++ [ (False, " })") ] ).( (False, "(do{\n") : )
parseApplyBegin = do
tokens "<%"
code <- parseInner
tokens "-%>"
return code
parseApplyContinue = do
tokens "<%-"
code <- parseInner
tokens "-%>"
return code
parseApplyEnd = do
tokens "<%-"
code <- parseInner
tokens "%>"
return code
parseInner = do
greedyList (spot isSpace)
greedyList $ do
still (parseNot () $ tokens "%>")
spot (const True)
mkOutputText txt = " " ++ putStrStr ++ " $ " ++ show txt ++ ";\n"
mkOutputHere code = " " ++ code ++ ";\n"
mkOutputCode code = " " ++ code ++ " >>= " ++ putStrStr ++ ";\n"
mkOutputShowCode code = " " ++ code ++ " >>= " ++ putStrStr ++ ". show ;\n"
mkOutputReturnCode code = " " ++ putStrStr ++ " $ " ++ code ++ " ;\n"
mkOutputReturnShowCode code = " " ++ putStrStr ++ " $ show" ++ code ++ " ;\n"