1 -----------------------------------------------------------------------------
2 -- $Id: Main.hs,v 1.2 2000/11/07 15:28:36 simonmar Exp $
4 -- (originally "GlueHsc.hs" by Marcin 'Qrczak' Kowalczyk)
6 -- Program for converting .hsc files to .hs files, by converting the
7 -- file into a C program which is run to generate the Haskell source.
8 -- Certain items known only to the C compiler can then be used in
9 -- the Haskell module; for example #defined constants, byte offsets
10 -- within structures, etc.
12 -- See the documentation in the Users' Guide for more details.
15 import System (getProgName, getArgs, ExitCode(..), system, exitWith, exitFailure)
16 import Directory (removeFile)
18 import Monad (liftM, liftM2, when)
19 import Char (ord, intToDigit, isSpace, isAlphaNum, toUpper)
29 options :: [OptDescr Flag]
31 Option "t" ["template"] (ReqArg Template "FILE") "template file",
32 Option "" ["cc"] (ReqArg Compiler "PROG") "C compiler to use",
33 Option "" ["ld"] (ReqArg Linker "PROG") "linker to use",
34 Option "" ["cflag"] (ReqArg CompFlag "FLAG") "flag to pass to the C compiler",
35 Option "" ["lflag"] (ReqArg LinkFlag "FLAG") "flag to pass to the linker",
36 Option "" ["help"] (NoArg Help) "display this help and exit"]
41 let header = "Usage: "++prog++" [OPTIONS...] INPUT.hsc [...]"
43 case getOpt Permute options args of
44 (flags, _, _) | any isHelp flags -> putStrLn (usageInfo header options)
45 where isHelp Help = True; isHelp _ = False
46 (_, [], []) -> putStrLn (prog++": No input files")
47 (flags, files, []) -> mapM_ (processFile flags) files
50 putStrLn (usageInfo header options)
53 processFile :: [Flag] -> String -> IO ()
54 processFile flags name = do
55 parsed <- parseFromFile parser name
58 Right toks -> output flags name toks
62 | Special String String
64 parser :: Parser [Token]
65 parser = many (text <|> special)
68 text = liftM Text $ many1 $ satisfy (/= '#') <|> try (string "##" >> return '#')
70 special :: Parser Token
73 skipMany (oneOf " \t")
74 key <- liftM2 (:) (letter <|> char '_') (many (alphaNum <|> char '_'))
76 skipMany (oneOf " \t")
78 return (Special key arg)
80 argument :: Parser String -> Parser String
81 argument eol = liftM concat $ many
82 ( many1 (noneOf "\n\"\'()/[\\]{}")
84 <|> (do char '\"'; a <- cString '\''; char '\"'; return ("\""++a++"\""))
85 <|> (do char '\''; a <- cString '\"'; char '\''; return ("\'"++a++"\'"))
86 <|> (do char '('; a <- nested; char ')'; return ("("++a++")"))
87 <|> (do try (string "/*"); comment; return " ")
88 <|> (do try (string "//"); skipMany (satisfy (/= '\n')); return " ")
90 <|> (do char '['; a <- nested; char ']'; return ("["++a++"]"))
91 <|> (do char '\\'; a <- anyChar; return ['\\',a])
92 <|> (do char '{'; a <- nested; char '}'; return ("{"++a++"}"))
94 where nested = argument (string "\n")
97 comment = (do skipMany1 (noneOf "*"); comment)
98 <|> (do try (string "*/"); return ())
99 <|> (do char '*'; comment)
102 cString :: Char -> Parser String
103 cString otherQuote = liftM concat $ many
104 ( many1 (noneOf "\n\\\'\"")
105 <|> string [otherQuote]
106 <|> (do char '\\'; a <- anyChar; return ['\\',a])
107 <?> "C character or string")
109 output :: [Flag] -> String -> [Token] -> IO ()
110 output flags name toks = let
111 baseName = case reverse name of
112 'c':base -> reverse base
114 cProgName = baseName++"c_make_hs.c"
115 oProgName = baseName++"c_make_hs.o"
116 progName = baseName++"c_make_hs"
118 outHName = baseName++".h"
119 outCName = baseName++".c"
121 execProgName = case progName of
125 specials = [(key, arg) | Special key arg <- toks]
127 needsC = any (\(key, _) -> key=="def") specials
130 includeGuard = map fixChar outHName
132 fixChar c | isAlphaNum c = toUpper c
137 compiler <- case [c | Compiler c <- flags] of
140 _ -> onlyOne "compiler"
141 linker <- case [l | Linker l <- flags] of
144 _ -> onlyOne "linker"
146 writeFile cProgName $
147 concat ["#include \""++t++"\"\n" | Template t <- flags] ++
148 outHeaderCProg specials ++
149 "\nint main (void)\n{\n" ++
150 outHeaderHs (if needsH then Just outHName else Nothing) specials ++
151 concatMap outTokenHs toks ++
154 compilerStatus <- system $
157 concat [" "++f | CompFlag f <- flags]++
160 case compilerStatus of
161 e@(ExitFailure _) -> exitWith e
165 linkerStatus <- system $
167 concat [" "++f | LinkFlag f <- flags]++
171 e@(ExitFailure _) -> exitWith e
175 system (execProgName++" >"++outHsName)
178 when needsH $ writeFile outHName $
179 "#ifndef "++includeGuard++"\n\
180 \#define "++includeGuard++"\n\
181 \#include <HsFFI.h>\n"++
182 concatMap outTokenH specials++
185 when needsC $ writeFile outCName $
186 "#include \""++outHName++"\"\n"++
187 concatMap outTokenC specials
189 onlyOne :: String -> IO a
191 putStrLn ("Only one "++what++" may be specified")
194 outHeaderCProg :: [(String, String)] -> String
195 outHeaderCProg = concatMap $ \(key, arg) -> case key of
196 "include" -> "#include "++arg++"\n"
197 "define" -> "#define "++arg++"\n"
199 's':'t':'r':'u':'c':'t':' ':_ -> arg++"\n"
200 't':'y':'p':'e':'d':'e':'f':' ':_ -> arg++"\n"
202 _ | conditional key -> "#"++key++" "++arg++"\n"
205 outHeaderHs :: Maybe String -> [(String, String)] -> String
206 outHeaderHs inH toks =
207 concatMap outSpecial toks ++
209 " hsc_end_options();\n\n"
211 outSpecial (key, arg) = case key of
212 "include" -> case inH of
213 Nothing -> out ("-#include "++arg)
215 "define" -> case inH of
216 Nothing -> out ("-optc-D"++toOptD arg)
219 _ | conditional key -> "#"++key++" "++arg++"\n"
221 toOptD arg = case break isSpace arg of
223 (name, _:value) -> name++'=':dropWhile isSpace value
224 includeH = case inH of
226 Just name -> out ("-#include \""++name++"\"")
227 out s = " hsc_option (\""++showCString s++"\");\n"
229 outTokenHs :: Token -> String
230 outTokenHs (Text s) = " fputs (\""++showCString s++"\", stdout);\n"
231 outTokenHs (Special key arg) = case key of
236 _ | conditional key -> "#"++key++" "++arg++"\n"
237 _ -> " hsc_"++key++" ("++arg++");\n"
239 outTokenH :: (String, String) -> String
240 outTokenH (key, arg) = case key of
241 "include" -> "#include "++arg++"\n"
242 "define" -> "#define " ++arg++"\n"
244 's':'t':'r':'u':'c':'t':' ':_ -> arg++"\n"
245 't':'y':'p':'e':'d':'e':'f':' ':_ -> arg++"\n"
246 'i':'n':'l':'i':'n':'e':' ':_ ->
251 _ -> "extern "++header++";\n"
252 where header = takeWhile (\c -> c/='{' && c/='=') arg
253 _ | conditional key -> "#"++key++" "++arg++"\n"
256 outTokenC :: (String, String) -> String
257 outTokenC (key, arg) = case key of
259 's':'t':'r':'u':'c':'t':' ':_ -> ""
260 't':'y':'p':'e':'d':'e':'f':' ':_ -> ""
261 'i':'n':'l':'i':'n':'e':' ':_ ->
266 "\n#ifndef __GNUC__\n\
272 where (header, body) = span (\c -> c/='{' && c/='=') arg
273 _ | conditional key -> "#"++key++" "++arg++"\n"
276 conditional :: String -> Bool
277 conditional "if" = True
278 conditional "ifdef" = True
279 conditional "ifndef" = True
280 conditional "elif" = True
281 conditional "else" = True
282 conditional "endif" = True
283 conditional _ = False
285 showCString :: String -> String
286 showCString = concatMap showCChar
288 showCChar '\"' = "\\\""
289 showCChar '\'' = "\\\'"
290 showCChar '?' = "\\?"
291 showCChar '\\' = "\\\\"
292 showCChar c | c >= ' ' && c <= '~' = [c]
293 showCChar '\a' = "\\a"
294 showCChar '\b' = "\\b"
295 showCChar '\f' = "\\f"
296 showCChar '\n' = "\\n\"\n \""
297 showCChar '\r' = "\\r"
298 showCChar '\t' = "\\t"
299 showCChar '\v' = "\\v"
301 intToDigit (ord c `quot` 64),
302 intToDigit (ord c `quot` 8 `mod` 8),
303 intToDigit (ord c `mod` 8)]