packages feed

llvm-ffi-tools-0.0.2: src/FunctionMangulation.hs

-- ToDo: filter out "const" using regexes
module FunctionMangulation (
   pattern,
   rewrite,
   rewriteFunction,
   ) where

import Text.Regex.Posix ((=~), (=~~))

import Control.Monad (forM)

import qualified Data.List.HT as ListHT
import Data.String.HT (trim)
import Data.Char (toLower)
import Data.List.HT (maybePrefixOf)
import Data.List (intercalate)


pattern :: String
pattern = "^(LLVM_C_ABI +|)([A-Za-z0-9_ ]+ ?\\*?)[ \t\n]*" ++
          "LLVM([A-Za-z0-9_]+)\\(([][a-zA-Z0-9_*, \t\n]+)\\);"

renameType :: String -> String
renameType t =
   case maybePrefixOf "LLVM" t of
      -- ToDo: we need two variants of rename
      Just suffix -> rename suffix
      Nothing -> rename t

rename :: String -> String
rename cname =
   case cname of
      "Bool" -> "LLVM.Bool"
      "int" -> "CInt"
      "unsigned" -> "CUInt"
      "const unsigned" -> "CUInt"
      "long long" -> "CLLong"
      "unsigned long long" -> "CULLong"
      "void" -> "()"
      "const char *" -> "CString"
      "float" -> "CFloat"
      "double" -> "CDouble"
      "char *" -> "CString"
      "size_t" -> "CSize"
      "uint8_t" -> "Word8"
      "uint16_t" -> "Word16"
      "uint32_t" -> "Word32"
      "uint64_t" -> "Word64"
      "const uint64_t []" -> "Ptr Word64"
      _ ->
         case ListHT.viewR cname of
            Just (ps,'*') -> "(Ptr " ++ rename (trim ps) ++ ")"
            _ -> trim cname

dropName :: String -> String
dropName s =
   case s =~ "^((const )?(unsigned long long|[A-Za-z0-9_]+)( \\*+)?) ?[A-Za-z0-9_]*(\\[\\])?$" of
      ((_:typ:_:_:_:"[]":_):_) -> typ ++ " []"
      ((_:typ:_):_) -> typ
      _ -> "{- oops! -} " ++ s

rewriteFunction :: String -> String -> String -> String
rewriteFunction cret cname cparams =
    let ret = "IO " ++ renameType (trim cret)
        renameParam = renameType . dropName . trim
        params = map renameParam . ListHT.chop (==',') $ cparams
        params' = if params == ["()"] then [] else params
        name = let (n:ame) = cname in toLower n : ame
    in "foreign import ccall unsafe \"LLVM" ++ cname ++ "\" " ++ name ++
           "\n    :: " ++ intercalate " -> " (params' ++ [ret])

rewrite :: String -> [[String]]
rewrite s = do
    matches <- s =~~ pattern
    forM matches $ \(_:_cabi:cret:cname:cparams:_) ->
         return (rewriteFunction cret cname cparams)