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 System.FilePath (takeDirectory, takeFileName) 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) import Data.Function.Tools (applyUnless) 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 edir = let d = takeDirectory infile in applyUnless (null d) ((d ++ "/") ++) ehaskellDir exeName = gsubRegexPR "\\." "_" $ takeFileName infile exeFile = edir ++ exeName srcFile = edir ++ exeName ++ haskellSffx cont <- readFile infile unlessM (doesDirectoryExist edir) $ createDirectory edir copyRequiredFile edir cont whenM (doesNotExistOrOldThan srcFile infile) $ writeFile srcFile $ fst $ head $ runParse parseAll ("", cont) whenM (doesNotExistOrOldThan exeFile srcFile) $ ghcMake exeName edir >> return () runProcess exeFile (maybeToList outfile) Nothing Nothing Nothing Nothing Nothing >>= waitForProcess return () copyRequiredFile :: FilePath -> String -> IO () copyRequiredFile dir src = evalParseT (copyRequiredFileParse dir) ("",src) >> return () copyRequiredFileParse :: FilePath -> ParseT Char IO () copyRequiredFileParse dir = 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 = dir ++ 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"