futhask-0.1.0: src/Conversion.hs
module Conversion where
import Data.Maybe
import Data.List (intercalate, partition, lookup)
import Data.Char (toUpper)
import Text.ParserCombinators.ReadP
data HeaderItem
= Preproc String
| Comment String
| Fun (String, String) [(String, String)]
| Var (String, String)
deriving Show
isWhiteSpace = (flip elem) " \t\n"
isNameChar = not.(flip elem) " \t\n;,()"
readPreproc = fmap Preproc $ (char '#') >> manyTill get (char '\n')
readComment = fmap Comment $ (string "/*") >> manyTill get (string "*/")
readOnelineComment = fmap Comment $ (string "//") >> manyTill get (char '\n')
readTypeName = skipSpaces
>> sepBy (munch1 (isNameChar)) (skipMany1 $ satisfy isWhiteSpace) >>= \ws
-> case ws of
[] -> return ("void", "")
[a] -> return (a, "")
a -> return ( intercalate " " (init a) ++ takeWhile (=='*') (last a)
, dropWhile (=='*') (last a) )
readVar = readTypeName >>= \tn -> skipSpaces >> char ';' >> return (Var tn)
readFun = readTypeName >>= \tn
-> skipSpaces
>> char '(' >> sepBy readTypeName (char ',') >>= \args -> char ')'
>> skipSpaces >> char ';'
>> return (Fun tn args)
readHeaderItem = skipSpaces >> readPreproc <++ readComment <++ readOnelineComment <++ readFun <++ readVar
readHeader fn = fmap (fst . head
. (readP_to_S $ many readHeaderItem >>= \his
-> skipSpaces >> eof >> return his))
$ readFile fn
varTable =
[ ("int", "Int")
, ("float", "Float")
, ("double", "Double")
, ("char", "CChar")
, ("bool", "CBool")
, ("void", "()")
, ("int8_t" , "Int8")
, ("int16_t", "Int16")
, ("int32_t", "Int32")
, ("int64_t", "Int64")
, ("uint8_t" , "Word8")
, ("uint16_t", "Word16")
, ("uint32_t", "Word32")
, ("uint64_t", "Word64")
, ("size_t", "CSize")
, ("cl_mem", "CLMem")
, ("cl_command_queue", "CLCommandQueue")
, ("CUdeviceptr", "DevicePtr ()") ]
varTable2 =
[ ("f32", "Float")
, ("f64", "Double")
, ("bool", "CBool")
, ("i8" , "Int8")
, ("i16", "Int16")
, ("i32", "Int32")
, ("i64", "Int64")
, ("u8" , "Word8")
, ("u16", "Word16")
, ("u32", "Word32")
, ("u64", "Word64") ]
capitalize (c:cs) = toUpper c:cs
wrapIfNotOneWord s = if elem ' ' s then "(" ++ s ++ ")" else s
haskellType s =
let pn = length $ dropWhile (/='*') s
ts = dropWhile (=="const") $ words $ takeWhile (/='*') s
in (intercalate "(" $ replicate pn "Ptr ")
++ (if head ts == "struct"
then capitalize $ ts !! 1
else (case lookup (head ts) varTable of
Just s -> s;
Nothing -> error $ "type \'" ++ s ++ "\' not found";))
++ replicate (pn-1) ')'
haskellDeclaration (Preproc s) = ""
haskellDeclaration (Comment s)
= intercalate "\n"
$ map (("--"++).dropWhile (==' ')) $ filter (/="") $ lines s
haskellDeclaration (Var (_, n)) = "data " ++ capitalize n
haskellDeclaration (Fun (ot, name) args)
= "foreign import ccall unsafe \"" ++ name ++ "\"\n "
++ drop 8 name ++ "\n :: "
++ intercalate "\n -> "
( (map haskellType $ filter (/="void") $ map fst args)
++ ["IO " ++ wrapIfNotOneWord (haskellType ot)] )
++ "\n"
rawImportString headerItems = intercalate "\n" $ map haskellDeclaration headerItems
instanceDeclarations (Var (_, n))
= (if isObject then objectString else "")
++ (if isArray then arrayString else "")
where cn = capitalize sn
rn = capitalize n
sn = drop 8 n
isObject = take 7 sn /= "context"
isArray = isObject && take 6 sn /= "opaque"
dim = if isArray
then read $ (:[]) $ last $ init sn
else 0
element = if isArray
then case lookup (takeWhile (/= '_') sn) varTable2 of
(Just t) -> t
Nothing -> error $ "ArrayType" ++ sn ++ " not found."
else ""
arrayString = "instance FutharkArray "++ cn ++ " Raw."++ rn
++ " M.Ix" ++ show dim ++ " " ++ element ++ " where\n"
++ " shapeFA = to" ++ show dim ++ "d Raw.shape_" ++ sn ++ "\n"
++ " newFA = from" ++ show dim ++ "d Raw.new_" ++ sn ++ "\n"
++ " valuesFA = Raw.values_" ++ sn ++ "\n"
objectString = "\nnewtype " ++ cn ++ " c = " ++ cn ++ " (F.ForeignPtr Raw." ++ rn ++ ")\n"
++ "instance FutharkObject " ++ cn ++ " Raw." ++ rn ++ " where\n"
++ " wrapFO = " ++ cn ++ "\n"
++ " freeFO = Raw.free_" ++ sn ++ "\n"
++ " withFO (" ++ cn ++ " fp) = F.withForeignPtr fp\n"
nfdataString = "instance NFData (" ++ cn ++" c) where rnf = rwhnf"
instanceDeclarations _ = ""
instanceDeclarationString headerItems = concatMap instanceDeclarations headerItems
haskellType' s =
let pn = length $ dropWhile (/='*') s
ts = dropWhile (=="const") $ words $ takeWhile (/='*') s
in if head ts == "struct"
then capitalize (drop 8 $ ts !! 1) ++ " c"
else (case lookup (head ts) varTable of
Just s -> s;
Nothing -> error $ "type " ++ s ++ "not found";)
entryCall (Fun (_, n) args)
= if isEntry
then "\n" ++ typeDeclaration ++ input ++ preCall ++ call ++ postCall
else ""
where
sn = drop 8 n
isEntry = take 5 sn == "entry"
en = drop 6 sn
isFO a = case lookup (takeWhile (/='*') $ last $ words $ fst a) varTable
of Just _ -> False; Nothing -> True;
(inArgs, outArgs) = partition ((=="in").take 2.snd) $ tail args
typeDeclaration = en ++ "\n :: "
++ concatMap (\i -> haskellType' (fst i) ++ "\n -> " ) inArgs
++ "FT c " ++ wrapIfNotOneWord (intercalate ", " $ map (\o -> haskellType' $ fst o) outArgs) ++ "\n"
input = unwords (en : map snd inArgs) ++ "\n = FT.unsafeLiftFromIO $ \\context\n -> "
preCall = concat
$ map (\i -> "T.withFO " ++ snd i ++ " $ \\" ++ snd i ++ "'\n -> ") (filter isFO inArgs)
++ map (\o -> "F.malloc >>= \\" ++ snd o ++ "\n -> ") outArgs
call = "C.inContextWithError context (\\context'\n -> Raw." ++ sn ++ " context' "
++ unwords ((map snd $ outArgs) ++ (map (\i -> if isFO i then snd i ++ "'" else snd i) inArgs)) ++ ")\n >> "
peek o = if isFO o then "U.peekFreeWrapIn context " else "U.peekFree "
postCall = (if length outArgs > 1
then concatMap (\o -> peek o ++ snd o ++ " >>= \\" ++ snd o ++ "'\n -> ") outArgs
++ "return " ++ wrapIfNotOneWord (intercalate ", " $ map (\o -> snd o ++ "'") outArgs)
else peek (head outArgs) ++ snd (head outArgs))
++ "\n"
entryCall _ = ""
entryCallString headerItems = concatMap entryCall headerItems