Plan 9 from Bell Labs’s /usr/web/sources/contrib/fernan/nhc98/src/hsc2hs/Main.hs
{-# OPTIONS -cpp #-} ------------------------------------------------------------------------ -- Program for converting .hsc files to .hs files, by converting the -- file into a C program which is run to generate the Haskell source. -- Certain items known only to the C compiler can then be used in -- the Haskell module; for example #defined constants, byte offsets -- within structures, etc. -- -- See the documentation in the Users' Guide for more details. import Control.Monad ( MonadPlus(..), liftM, liftM2, when ) import Data.Char ( isAlpha, isAlphaNum, isSpace, isDigit, toUpper, intToDigit, ord ) import Data.List ( intersperse, isSuffixOf ) import System.Cmd ( system, rawSystem ) import System.Console.GetOpt import System.Directory ( removeFile, doesFileExist ) import System.Environment ( getProgName, getArgs ) import System.Exit ( ExitCode(..), exitWith ) import System.IO ( hPutStr, hPutStrLn, stderr ) #if __GLASGOW_HASKELL__ >= 604 || defined(__NHC__) || defined(__HUGS__) import System.Directory ( findExecutable ) #else import System.Directory ( getPermissions, executable ) import System.Environment ( getEnv ) import Control.Monad ( foldM ) #endif #if __GLASGOW_HASKELL__ >= 604 import System.Process ( runProcess, waitForProcess ) import System.IO ( openFile, IOMode(..), hClose ) #define HAVE_runProcess #endif #if ! BUILD_NHC import Paths_hsc2hs ( getDataFileName, version ) import Data.Version ( showVersion ) #else import System.Directory ( getCurrentDirectory ) getDataFileName s = do here <- getCurrentDirectory return (here++"/"++s) version = "0.67" -- TODO!!! showVersion = id #endif #ifdef __GLASGOW_HASKELL__ default_compiler = "ghc" #else default_compiler = "gcc" #endif #if defined(__GLASGOW_HASKELL__) && (__GLASGOW_HASKELL__ < 604) findExecutable :: String -> IO (Maybe FilePath) findExecutable cmd = let dir = dirname cmd in case dir of "" -> do -- search the shell environment PATH variable for candidates val <- getEnv "PATH" let psep = pathSep val dirs = splitPath psep "" val foldM (\a dir-> testFile a (dir++'/':cmd)) Nothing dirs _ -> do testFile Nothing cmd where splitPath :: Char -> String -> String -> [String] splitPath sep acc [] = [reverse acc] splitPath sep acc (c:path) | c==sep = reverse acc : splitPath sep "" path splitPath sep acc (c:path) = splitPath sep (c:acc) path pathSep s = if length (filter (==';') s) >0 then ';' else ':' testFile :: Maybe String -> String -> IO (Maybe String) testFile gotit@(Just _) path = return gotit testFile Nothing path = do ok <- doesFileExist path if ok then perms path else return Nothing perms file = do p <- getPermissions file return (if executable p then Just file else Nothing) dirname = reverse . safetail . dropWhile (not.(`elem`"\\/")) . reverse where safetail [] = [] safetail (_:x) = x #endif versionString :: String versionString = "hsc2hs version " ++ showVersion version ++ "\n" data Flag = Help | Version | Template String | Compiler String | Linker String | CompFlag String | LinkFlag String | NoCompile | Include String | Define String (Maybe String) | Output String | Verbose template_flag :: Flag -> Bool template_flag (Template _) = True template_flag _ = False include :: String -> Flag include s@('\"':_) = Include s include s@('<' :_) = Include s include s = Include ("\""++s++"\"") define :: String -> Flag define s = case break (== '=') s of (name, []) -> Define name Nothing (name, _:value) -> Define name (Just value) options :: [OptDescr Flag] options = [ Option ['o'] ["output"] (ReqArg Output "FILE") "name of main output file", Option ['t'] ["template"] (ReqArg Template "FILE") "template file", Option ['c'] ["cc"] (ReqArg Compiler "PROG") "C compiler to use", Option ['l'] ["ld"] (ReqArg Linker "PROG") "linker to use", Option ['C'] ["cflag"] (ReqArg CompFlag "FLAG") "flag to pass to the C compiler", Option ['I'] [] (ReqArg (CompFlag . ("-I"++)) "DIR") "passed to the C compiler", Option ['L'] ["lflag"] (ReqArg LinkFlag "FLAG") "flag to pass to the linker", Option ['i'] ["include"] (ReqArg include "FILE") "as if placed in the source", Option ['D'] ["define"] (ReqArg define "NAME[=VALUE]") "as if placed in the source", Option [] ["no-compile"] (NoArg NoCompile) "stop after writing *_hsc_make.c", Option ['v'] ["verbose"] (NoArg Verbose) "dump commands to stderr", Option ['?'] ["help"] (NoArg Help) "display this help and exit", Option ['V'] ["version"] (NoArg Version) "output version information and exit" ] main :: IO () main = do prog <- getProgramName let header = "Usage: "++prog++" [OPTIONS] INPUT.hsc [...]\n" args <- getArgs let (flags, files, errs) = getOpt Permute options args -- If there is no Template flag explicitly specified, -- use the file placed by the Cabal installation. flags_w_tpl <- if any template_flag flags then return flags else do templ <- getDataFileName "template-hsc.h" return (Template templ : flags) case (files, errs) of (_, _) | any isHelp flags_w_tpl -> bye (usageInfo header options) | any isVersion flags_w_tpl -> bye versionString where isHelp Help = True; isHelp _ = False isVersion Version = True; isVersion _ = False ((_:_), []) -> mapM_ (processFile flags_w_tpl) files (_, _ ) -> die (concat errs ++ usageInfo header options) getProgramName :: IO String getProgramName = liftM (`withoutSuffix` "-bin") getProgName where str `withoutSuffix` suff | suff `isSuffixOf` str = take (length str - length suff) str | otherwise = str bye :: String -> IO a bye s = putStr s >> exitWith ExitSuccess die :: String -> IO a die s = hPutStr stderr s >> exitWith (ExitFailure 1) processFile :: [Flag] -> String -> IO () processFile flags name = do let file_name = dosifyPath name s <- readFile file_name case parser of Parser p -> case p (SourcePos file_name 1) s of Success _ _ _ toks -> output flags file_name toks Failure (SourcePos name' line) msg -> die (name'++":"++show line++": "++msg++"\n") ------------------------------------------------------------------------ -- A deterministic parser which remembers the text which has been parsed. newtype Parser a = Parser (SourcePos -> String -> ParseResult a) data ParseResult a = Success !SourcePos String String a | Failure !SourcePos String data SourcePos = SourcePos String !Int updatePos :: SourcePos -> Char -> SourcePos updatePos pos@(SourcePos name line) ch = case ch of '\n' -> SourcePos name (line + 1) _ -> pos instance Monad Parser where return a = Parser $ \pos s -> Success pos [] s a Parser m >>= k = Parser $ \pos s -> case m pos s of Success pos' out1 s' a -> case k a of Parser k' -> case k' pos' s' of Success pos'' out2 imp'' b -> Success pos'' (out1++out2) imp'' b Failure pos'' msg -> Failure pos'' msg Failure pos' msg -> Failure pos' msg fail msg = Parser $ \pos _ -> Failure pos msg instance MonadPlus Parser where mzero = fail "mzero" Parser m `mplus` Parser n = Parser $ \pos s -> case m pos s of success@(Success _ _ _ _) -> success Failure _ _ -> n pos s getPos :: Parser SourcePos getPos = Parser $ \pos s -> Success pos [] s pos setPos :: SourcePos -> Parser () setPos pos = Parser $ \_ s -> Success pos [] s () message :: Parser a -> String -> Parser a Parser m `message` msg = Parser $ \pos s -> case m pos s of success@(Success _ _ _ _) -> success Failure pos' _ -> Failure pos' msg catchOutput_ :: Parser a -> Parser String catchOutput_ (Parser m) = Parser $ \pos s -> case m pos s of Success pos' out s' _ -> Success pos' [] s' out Failure pos' msg -> Failure pos' msg fakeOutput :: Parser a -> String -> Parser a Parser m `fakeOutput` out = Parser $ \pos s -> case m pos s of Success pos' _ s' a -> Success pos' out s' a Failure pos' msg -> Failure pos' msg lookAhead :: Parser String lookAhead = Parser $ \pos s -> Success pos [] s s satisfy :: (Char -> Bool) -> Parser Char satisfy p = Parser $ \pos s -> case s of c:cs | p c -> Success (updatePos pos c) [c] cs c _ -> Failure pos "Bad character" char_ :: Char -> Parser () char_ c = do satisfy (== c) `message` (show c++" expected") return () anyChar_ :: Parser () anyChar_ = do satisfy (const True) `message` "Unexpected end of file" return () any2Chars_ :: Parser () any2Chars_ = anyChar_ >> anyChar_ many :: Parser a -> Parser [a] many p = many1 p `mplus` return [] many1 :: Parser a -> Parser [a] many1 p = liftM2 (:) p (many p) many_ :: Parser a -> Parser () many_ p = many1_ p `mplus` return () many1_ :: Parser a -> Parser () many1_ p = p >> many_ p manySatisfy, manySatisfy1 :: (Char -> Bool) -> Parser String manySatisfy = many . satisfy manySatisfy1 = many1 . satisfy manySatisfy_, manySatisfy1_ :: (Char -> Bool) -> Parser () manySatisfy_ = many_ . satisfy manySatisfy1_ = many1_ . satisfy ------------------------------------------------------------------------ -- Parser of hsc syntax. data Token = Text SourcePos String | Special SourcePos String String parser :: Parser [Token] parser = do pos <- getPos t <- catchOutput_ text s <- lookAhead rest <- case s of [] -> return [] _:_ -> liftM2 (:) (special `fakeOutput` []) parser return (if null t then rest else Text pos t : rest) text :: Parser () text = do s <- lookAhead case s of [] -> return () c:_ | isAlpha c || c == '_' -> do anyChar_ manySatisfy_ (\c' -> isAlphaNum c' || c' == '_' || c' == '\'') text c:_ | isHsSymbol c -> do symb <- catchOutput_ (manySatisfy_ isHsSymbol) case symb of "#" -> return () '-':'-':symb' | all (== '-') symb' -> do return () `fakeOutput` symb manySatisfy_ (/= '\n') text _ -> do return () `fakeOutput` unescapeHashes symb text '\"':_ -> do anyChar_; hsString '\"'; text '\'':_ -> do anyChar_; hsString '\''; text '{':'-':_ -> do any2Chars_; linePragma `mplus` hsComment; text _:_ -> do anyChar_; text hsString :: Char -> Parser () hsString quote = do s <- lookAhead case s of [] -> return () c:_ | c == quote -> anyChar_ '\\':c:_ | isSpace c -> do anyChar_ manySatisfy_ isSpace char_ '\\' `mplus` return () hsString quote | otherwise -> do any2Chars_; hsString quote _:_ -> do anyChar_; hsString quote hsComment :: Parser () hsComment = do s <- lookAhead case s of [] -> return () '-':'}':_ -> any2Chars_ '{':'-':_ -> do any2Chars_; hsComment; hsComment _:_ -> do anyChar_; hsComment linePragma :: Parser () linePragma = do char_ '#' manySatisfy_ isSpace satisfy (\c -> c == 'L' || c == 'l') satisfy (\c -> c == 'I' || c == 'i') satisfy (\c -> c == 'N' || c == 'n') satisfy (\c -> c == 'E' || c == 'e') manySatisfy1_ isSpace line <- liftM read $ manySatisfy1 isDigit manySatisfy1_ isSpace char_ '\"' name <- manySatisfy (/= '\"') char_ '\"' manySatisfy_ isSpace char_ '#' char_ '-' char_ '}' setPos (SourcePos name (line - 1)) isHsSymbol :: Char -> Bool isHsSymbol '!' = True; isHsSymbol '#' = True; isHsSymbol '$' = True isHsSymbol '%' = True; isHsSymbol '&' = True; isHsSymbol '*' = True isHsSymbol '+' = True; isHsSymbol '.' = True; isHsSymbol '/' = True isHsSymbol '<' = True; isHsSymbol '=' = True; isHsSymbol '>' = True isHsSymbol '?' = True; isHsSymbol '@' = True; isHsSymbol '\\' = True isHsSymbol '^' = True; isHsSymbol '|' = True; isHsSymbol '-' = True isHsSymbol '~' = True isHsSymbol _ = False unescapeHashes :: String -> String unescapeHashes [] = [] unescapeHashes ('#':'#':s) = '#' : unescapeHashes s unescapeHashes (c:s) = c : unescapeHashes s lookAheadC :: Parser String lookAheadC = liftM joinLines lookAhead where joinLines [] = [] joinLines ('\\':'\n':s) = joinLines s joinLines (c:s) = c : joinLines s satisfyC :: (Char -> Bool) -> Parser Char satisfyC p = do s <- lookAhead case s of '\\':'\n':_ -> do any2Chars_ `fakeOutput` []; satisfyC p _ -> satisfy p charC_ :: Char -> Parser () charC_ c = do satisfyC (== c) `message` (show c++" expected") return () anyCharC_ :: Parser () anyCharC_ = do satisfyC (const True) `message` "Unexpected end of file" return () any2CharsC_ :: Parser () any2CharsC_ = anyCharC_ >> anyCharC_ manySatisfyC :: (Char -> Bool) -> Parser String manySatisfyC = many . satisfyC manySatisfyC_ :: (Char -> Bool) -> Parser () manySatisfyC_ = many_ . satisfyC special :: Parser Token special = do manySatisfyC_ (\c -> isSpace c && c /= '\n') s <- lookAheadC case s of '{':_ -> do anyCharC_ manySatisfyC_ isSpace sp <- keyArg (== '\n') charC_ '}' return sp _ -> keyArg (const False) keyArg :: (Char -> Bool) -> Parser Token keyArg eol = do pos <- getPos key <- keyword `message` "hsc keyword or '{' expected" manySatisfyC_ (\c' -> isSpace c' && c' /= '\n' || eol c') arg <- catchOutput_ (argument eol) return (Special pos key arg) keyword :: Parser String keyword = do c <- satisfyC (\c' -> isAlpha c' || c' == '_') cs <- manySatisfyC (\c' -> isAlphaNum c' || c' == '_') return (c:cs) argument :: (Char -> Bool) -> Parser () argument eol = do s <- lookAheadC case s of [] -> return () c:_ | eol c -> do anyCharC_; argument eol '\n':_ -> return () '\"':_ -> do anyCharC_; cString '\"'; argument eol '\'':_ -> do anyCharC_; cString '\''; argument eol '(':_ -> do anyCharC_; nested ')'; argument eol ')':_ -> return () '/':'*':_ -> do any2CharsC_; cComment; argument eol '/':'/':_ -> do any2CharsC_; manySatisfyC_ (/= '\n'); argument eol '[':_ -> do anyCharC_; nested ']'; argument eol ']':_ -> return () '{':_ -> do anyCharC_; nested '}'; argument eol '}':_ -> return () _:_ -> do anyCharC_; argument eol nested :: Char -> Parser () nested c = do argument (== '\n'); charC_ c cComment :: Parser () cComment = do s <- lookAheadC case s of [] -> return () '*':'/':_ -> do any2CharsC_ _:_ -> do anyCharC_; cComment cString :: Char -> Parser () cString quote = do s <- lookAheadC case s of [] -> return () c:_ | c == quote -> anyCharC_ '\\':_:_ -> do any2CharsC_; cString quote _:_ -> do anyCharC_; cString quote ------------------------------------------------------------------------ -- Write the output files. splitName :: String -> (String, String) splitName name = case break (== '/') name of (file, []) -> ([], file) (dir, sep:rest) -> (dir++sep:restDir, restFile) where (restDir, restFile) = splitName rest splitExt :: String -> (String, String) splitExt name = case break (== '.') name of (base, []) -> (base, []) (base, sepRest@(sep:rest)) | null restExt -> (base, sepRest) | otherwise -> (base++sep:restBase, restExt) where (restBase, restExt) = splitExt rest output :: [Flag] -> String -> [Token] -> IO () output flags name toks = do (outName, outDir, outBase) <- case [f | Output f <- flags] of [] -> if not (null ext) && last ext == 'c' then return (dir++base++init ext, dir, base) else if ext == ".hs" then return (dir++base++"_out.hs", dir, base) else return (dir++base++".hs", dir, base) where (dir, file) = splitName name (base, ext) = splitExt file [f] -> let (dir, file) = splitName f (base, _) = splitExt file in return (f, dir, base) _ -> onlyOne "output file" let cProgName = outDir++outBase++"_hsc_make.c" oProgName = outDir++outBase++"_hsc_make.o" progName = outDir++outBase++"_hsc_make" #if defined(mingw32_HOST_OS) || defined(__CYGWIN32__) -- This is a real hack, but the quoting mechanism used for calling the C preprocesseor -- via GHC has changed a few times, so this seems to be the only way... :-P * * * ++ ".exe" #endif outHFile = outBase++"_hsc.h" outHName = outDir++outHFile outCName = outDir++outBase++"_hsc.c" beVerbose = any (\ x -> case x of { Verbose -> True; _ -> False}) flags let execProgName | null outDir = dosifyPath ("./" ++ progName) | otherwise = progName let specials = [(pos, key, arg) | Special pos key arg <- toks] let needsC = any (\(_, key, _) -> key == "def") specials needsH = needsC let includeGuard = map fixChar outHName where fixChar c | isAlphaNum c = toUpper c | otherwise = '_' compiler <- case [c | Compiler c <- flags] of [] -> do mb_path <- findExecutable default_compiler case mb_path of Nothing -> die ("Can't find "++default_compiler++"\n") Just path -> return path [c] -> return c _ -> onlyOne "compiler" linker <- case [l | Linker l <- flags] of [] -> return compiler [l] -> return l _ -> onlyOne "linker" writeFile cProgName $ concatMap outFlagHeaderCProg flags++ concatMap outHeaderCProg specials++ "\nint main (int argc, char *argv [])\n{\n"++ outHeaderHs flags (if needsH then Just outHName else Nothing) specials++ outHsLine (SourcePos name 0)++ concatMap outTokenHs toks++ " return 0;\n}\n" -- NOTE: hbc compiles "[() | NoCompile <- flags]" into wrong code, -- so we use something slightly more complicated. :-P when (any (\x -> case x of NoCompile -> True; _ -> False) flags) $ exitWith ExitSuccess rawSystemL ("compiling " ++ cProgName) beVerbose compiler ( ["-c"] ++ [f | CompFlag f <- flags] ++ [cProgName] ++ ["-o", oProgName] ) removeFile cProgName rawSystemL ("linking " ++ oProgName) beVerbose linker ( [f | LinkFlag f <- flags] ++ [oProgName] ++ ["-o", progName] ) removeFile oProgName rawSystemWithStdOutL ("running " ++ execProgName) beVerbose execProgName [] outName removeFile progName when needsH $ writeFile outHName $ "#ifndef "++includeGuard++"\n" ++ "#define "++includeGuard++"\n" ++ "#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 409\n" ++ "#include <Rts.h>\n" ++ "#endif\n" ++ "#include <HsFFI.h>\n" ++ "#if __NHC__\n" ++ "#undef HsChar\n" ++ "#define HsChar int\n" ++ "#endif\n" ++ concatMap outFlagH flags++ concatMap outTokenH specials++ "#endif\n" when needsC $ writeFile outCName $ "#include \""++outHFile++"\"\n"++ concatMap outTokenC specials -- NB. outHFile not outHName; works better when processed -- by gcc or mkdependC. rawSystemL :: String -> Bool -> FilePath -> [String] -> IO () rawSystemL action flg prog args = do let cmdLine = prog++" "++unwords args when flg $ hPutStrLn stderr ("Executing: " ++ cmdLine) #ifndef HAVE_rawSystem exitStatus <- system cmdLine #else exitStatus <- rawSystem prog args #endif case exitStatus of ExitFailure _ -> die $ action ++ " failed\ncommand was: " ++ cmdLine ++ "\n" _ -> return () rawSystemWithStdOutL :: String -> Bool -> FilePath -> [String] -> FilePath -> IO () rawSystemWithStdOutL action flg prog args outFile = do let cmdLine = prog++" "++unwords args++" >"++outFile when flg (hPutStrLn stderr ("Executing: " ++ cmdLine)) #ifndef HAVE_runProcess exitStatus <- system cmdLine #else hOut <- openFile outFile WriteMode process <- runProcess prog args Nothing Nothing Nothing (Just hOut) Nothing exitStatus <- waitForProcess process hClose hOut #endif case exitStatus of ExitFailure _ -> die $ action ++ " failed\ncommand was: " ++ cmdLine ++ "\n" _ -> return () onlyOne :: String -> IO a onlyOne what = die ("Only one "++what++" may be specified\n") outFlagHeaderCProg :: Flag -> String outFlagHeaderCProg (Template t) = "#include \""++t++"\"\n" outFlagHeaderCProg (Include f) = "#include "++f++"\n" outFlagHeaderCProg (Define n Nothing) = "#define "++n++" 1\n" outFlagHeaderCProg (Define n (Just v)) = "#define "++n++" "++v++"\n" outFlagHeaderCProg _ = "" outHeaderCProg :: (SourcePos, String, String) -> String outHeaderCProg (pos, key, arg) = case key of "include" -> outCLine pos++"#include "++arg++"\n" "define" -> outCLine pos++"#define "++arg++"\n" "undef" -> outCLine pos++"#undef "++arg++"\n" "def" -> case arg of 's':'t':'r':'u':'c':'t':' ':_ -> outCLine pos++arg++"\n" 't':'y':'p':'e':'d':'e':'f':' ':_ -> outCLine pos++arg++"\n" _ -> "" _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n" "let" -> case break (== '=') arg of (_, "") -> "" (header, _:body) -> case break isSpace header of (name, args) -> outCLine pos++ "#define hsc_"++name++"("++dropWhile isSpace args++") " ++ "printf ("++joinLines body++");\n" _ -> "" where joinLines = concat . intersperse " \\\n" . lines outHeaderHs :: [Flag] -> Maybe String -> [(SourcePos, String, String)] -> String outHeaderHs flags inH toks = "#if " ++ "__GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 409\n" ++ " printf (\"{-# OPTIONS -optc-D" ++ "__GLASGOW_HASKELL__=%d #-}\\n\", " ++ "__GLASGOW_HASKELL__);\n" ++ "#endif\n"++ case inH of Nothing -> concatMap outFlag flags++concatMap outSpecial toks Just f -> outInclude ("\""++f++"\"") where outFlag (Include f) = outInclude f outFlag (Define n Nothing) = outOption ("-optc-D"++n) outFlag (Define n (Just v)) = outOption ("-optc-D"++n++"="++v) outFlag _ = "" outSpecial (pos, key, arg) = case key of "include" -> outInclude arg "define" | goodForOptD arg -> outOption ("-optc-D"++toOptD arg) | otherwise -> "" _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n" _ -> "" goodForOptD arg = case arg of "" -> True c:_ | isSpace c -> True '(':_ -> False _:s -> goodForOptD s toOptD arg = case break isSpace arg of (name, "") -> name (name, _:value) -> name++'=':dropWhile isSpace value outOption s = "#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 603\n" ++ " printf (\"{-# OPTIONS %s #-}\\n\", \""++ showCString s++"\");\n"++ "#else\n"++ " printf (\"{-# OPTIONS_GHC %s #-}\\n\", \""++ showCString s++"\");\n"++ "#endif\n" outInclude s = "#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 603\n" ++ " printf (\"{-# OPTIONS -#include %s #-}\\n\", \""++ showCString s++"\");\n"++ "#else\n"++ " printf (\"{-# INCLUDE %s #-}\\n\", \""++ showCString s++"\");\n"++ "#endif\n" outTokenHs :: Token -> String outTokenHs (Text pos txt) = case break (== '\n') txt of (allTxt, []) -> outText allTxt (first, _:rest) -> outText (first++"\n")++ outHsLine pos++ outText rest where outText s = " fputs (\""++showCString s++"\", stdout);\n" outTokenHs (Special pos key arg) = case key of "include" -> "" "define" -> "" "undef" -> "" "def" -> "" _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n" "let" -> "" "enum" -> outCLine pos++outEnum arg _ -> outCLine pos++" hsc_"++key++" ("++arg++");\n" outEnum :: String -> String outEnum arg = case break (== ',') arg of (_, []) -> "" (t, _:afterT) -> case break (== ',') afterT of (f, afterF) -> let enums [] = "" enums (_:s) = case break (== ',') s of (enum, rest) -> let this = case break (== '=') $ dropWhile isSpace enum of (name, []) -> " hsc_enum ("++t++", "++f++", " ++ "hsc_haskellize (\""++name++"\"), "++ name++");\n" (hsName, _:cName) -> " hsc_enum ("++t++", "++f++", " ++ "printf (\"%s\", \""++hsName++"\"), "++ cName++");\n" in this++enums rest in enums afterF outFlagH :: Flag -> String outFlagH (Include f) = "#include "++f++"\n" outFlagH (Define n Nothing) = "#define "++n++" 1\n" outFlagH (Define n (Just v)) = "#define "++n++" "++v++"\n" outFlagH _ = "" outTokenH :: (SourcePos, String, String) -> String outTokenH (pos, key, arg) = case key of "include" -> outCLine pos++"#include "++arg++"\n" "define" -> outCLine pos++"#define " ++arg++"\n" "undef" -> outCLine pos++"#undef " ++arg++"\n" "def" -> outCLine pos++case arg of 's':'t':'r':'u':'c':'t':' ':_ -> arg++"\n" 't':'y':'p':'e':'d':'e':'f':' ':_ -> arg++"\n" 'i':'n':'l':'i':'n':'e':' ':_ -> "#ifdef __GNUC__\n" ++ "extern\n" ++ "#endif\n"++ arg++"\n" _ -> "extern "++header++";\n" where header = takeWhile (\c -> c /= '{' && c /= '=') arg _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n" _ -> "" outTokenC :: (SourcePos, String, String) -> String outTokenC (pos, key, arg) = case key of "def" -> case arg of 's':'t':'r':'u':'c':'t':' ':_ -> "" 't':'y':'p':'e':'d':'e':'f':' ':_ -> "" 'i':'n':'l':'i':'n':'e':' ':arg' -> case span (\c -> c /= '{' && c /= '=') arg' of (header, body) -> outCLine pos++ "#ifndef __GNUC__\n" ++ "extern inline\n" ++ "#endif\n"++ header++ "\n#ifndef __GNUC__\n" ++ ";\n" ++ "#else\n"++ body++ "\n#endif\n" _ -> outCLine pos++arg++"\n" _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n" _ -> "" conditional :: String -> Bool conditional "if" = True conditional "ifdef" = True conditional "ifndef" = True conditional "elif" = True conditional "else" = True conditional "endif" = True conditional "error" = True conditional "warning" = True conditional _ = False outCLine :: SourcePos -> String outCLine (SourcePos name line) = "#line "++show line++" \""++showCString (snd (splitName name))++"\"\n" outHsLine :: SourcePos -> String outHsLine (SourcePos name line) = " hsc_line ("++show (line + 1)++", \""++ showCString name++"\");\n" showCString :: String -> String showCString = concatMap showCChar where showCChar '\"' = "\\\"" showCChar '\'' = "\\\'" showCChar '?' = "\\?" showCChar '\\' = "\\\\" showCChar c | c >= ' ' && c <= '~' = [c] showCChar '\a' = "\\a" showCChar '\b' = "\\b" showCChar '\f' = "\\f" showCChar '\n' = "\\n\"\n \"" showCChar '\r' = "\\r" showCChar '\t' = "\\t" showCChar '\v' = "\\v" showCChar c = ['\\', intToDigit (ord c `quot` 64), intToDigit (ord c `quot` 8 `mod` 8), intToDigit (ord c `mod` 8)] ----------------------------------------- -- Modified version from ghc/compiler/SysTools -- Convert paths foo/baz to foo\baz on Windows subst :: Char -> Char -> String -> String #if defined(mingw32_HOST_OS) || defined(__CYGWIN32__) subst a b = map (\x -> if x == a then b else x) #else subst _ _ = id #endif dosifyPath :: String -> String dosifyPath = subst '/' '\\'