inline-c 0.6.1.0 → 0.7.0.0
raw patch · 9 files changed
+140/−24 lines, 9 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Language.C.Inline.Context: instance Data.Semigroup.Semigroup Language.C.Inline.Context.Context
- Language.C.Types: instance Data.Semigroup.Semigroup Language.C.Types.Specifiers
+ Language.C.Inline: funPtr :: QuasiQuoter
+ Language.C.Inline.Context: instance GHC.Base.Semigroup Language.C.Inline.Context.Context
+ Language.C.Inline.Internal: [codeFunPtr] :: Code -> Bool
+ Language.C.Inline.Internal: funPtrQuote :: Safety -> QuasiQuoter
+ Language.C.Inline.Internal: instance GHC.Classes.Eq Language.C.Inline.Internal.FunPtrDecl
+ Language.C.Inline.Internal: instance GHC.Show.Show Language.C.Inline.Internal.FunPtrDecl
+ Language.C.Types: instance GHC.Base.Semigroup Language.C.Types.Specifiers
- Language.C.Inline: withPtr :: (Storable a) => (Ptr a -> IO b) -> IO (a, b)
+ Language.C.Inline: withPtr :: Storable a => (Ptr a -> IO b) -> IO (a, b)
- Language.C.Inline: withPtr_ :: (Storable a) => (Ptr a -> IO ()) -> IO a
+ Language.C.Inline: withPtr_ :: Storable a => (Ptr a -> IO ()) -> IO a
- Language.C.Inline.Context: SomeAntiQuoter :: (AntiQuoter a) -> SomeAntiQuoter
+ Language.C.Inline.Context: SomeAntiQuoter :: AntiQuoter a -> SomeAntiQuoter
- Language.C.Inline.Internal: Code :: Safety -> TypeQ -> String -> String -> Code
+ Language.C.Inline.Internal: Code :: Safety -> TypeQ -> String -> String -> Bool -> Code
- Language.C.Inline.Internal: inlineItems :: Safety -> TypeQ -> Type CIdentifier -> [(CIdentifier, Type CIdentifier)] -> String -> ExpQ
+ Language.C.Inline.Internal: inlineItems :: Safety -> Bool -> Maybe String -> TypeQ -> Type CIdentifier -> [(CIdentifier, Type CIdentifier)] -> String -> ExpQ
- Language.C.Inline.Internal: runParserInQ :: String -> TypeNames -> (forall m. CParser HaskellIdentifier m => m a) -> Q a
+ Language.C.Inline.Internal: runParserInQ :: Hashable ident => String -> CParserContext ident -> (forall m. CParser ident m => m a) -> Q a
- Language.C.Types: Array :: (ArrayType i) -> (Type i) -> Type i
+ Language.C.Types: Array :: ArrayType i -> Type i -> Type i
- Language.C.Types: Char :: (Maybe Sign) -> TypeSpecifier
+ Language.C.Types: Char :: Maybe Sign -> TypeSpecifier
- Language.C.Types: ParameterDeclaration :: Maybe i -> (Type i) -> ParameterDeclaration i
+ Language.C.Types: ParameterDeclaration :: Maybe i -> Type i -> ParameterDeclaration i
- Language.C.Types: Proto :: (Type i) -> [ParameterDeclaration i] -> Type i
+ Language.C.Types: Proto :: Type i -> [ParameterDeclaration i] -> Type i
- Language.C.Types: Ptr :: [TypeQualifier] -> (Type i) -> Type i
+ Language.C.Types: Ptr :: [TypeQualifier] -> Type i -> Type i
- Language.C.Types: [parameterDeclarationType] :: ParameterDeclaration i -> (Type i)
+ Language.C.Types: [parameterDeclarationType] :: ParameterDeclaration i -> Type i
- Language.C.Types: quickCParser :: CParserContext i -> String -> (ReaderT (CParserContext i) (Parsec String ()) a) -> a
+ Language.C.Types: quickCParser :: CParserContext i -> String -> ReaderT (CParserContext i) (Parsec String ()) a -> a
- Language.C.Types: quickCParser_ :: String -> (ReaderT (CParserContext CIdentifier) (Parsec String ()) a) -> a
+ Language.C.Types: quickCParser_ :: String -> ReaderT (CParserContext CIdentifier) (Parsec String ()) a -> a
- Language.C.Types: runCParser :: Stream s Identity Char => CParserContext i -> String -> s -> (ReaderT (CParserContext i) (Parsec s ()) a) -> Either ParseError a
+ Language.C.Types: runCParser :: Stream s Identity Char => CParserContext i -> String -> s -> ReaderT (CParserContext i) (Parsec s ()) a -> Either ParseError a
- Language.C.Types.Parse: AbstractDeclaratorParens :: (AbstractDeclarator i) -> DirectAbstractDeclarator i
+ Language.C.Types.Parse: AbstractDeclaratorParens :: AbstractDeclarator i -> DirectAbstractDeclarator i
- Language.C.Types.Parse: Array :: (ArrayType i) -> ArrayOrProto i
+ Language.C.Types.Parse: Array :: ArrayType i -> ArrayOrProto i
- Language.C.Types.Parse: ArrayOrProto :: (DirectDeclarator i) -> (ArrayOrProto i) -> DirectDeclarator i
+ Language.C.Types.Parse: ArrayOrProto :: DirectDeclarator i -> ArrayOrProto i -> DirectDeclarator i
- Language.C.Types.Parse: ArrayOrProtoHere :: (ArrayOrProto i) -> DirectAbstractDeclarator i
+ Language.C.Types.Parse: ArrayOrProtoHere :: ArrayOrProto i -> DirectAbstractDeclarator i
- Language.C.Types.Parse: ArrayOrProtoThere :: (DirectAbstractDeclarator i) -> (ArrayOrProto i) -> DirectAbstractDeclarator i
+ Language.C.Types.Parse: ArrayOrProtoThere :: DirectAbstractDeclarator i -> ArrayOrProto i -> DirectAbstractDeclarator i
- Language.C.Types.Parse: Declarator :: [Pointer] -> (DirectDeclarator i) -> Declarator i
+ Language.C.Types.Parse: Declarator :: [Pointer] -> DirectDeclarator i -> Declarator i
- Language.C.Types.Parse: DeclaratorParens :: (Declarator i) -> DirectDeclarator i
+ Language.C.Types.Parse: DeclaratorParens :: Declarator i -> DirectDeclarator i
- Language.C.Types.Parse: IsAbstractDeclarator :: (AbstractDeclarator i) -> DeclaratorOrAbstractDeclarator i
+ Language.C.Types.Parse: IsAbstractDeclarator :: AbstractDeclarator i -> DeclaratorOrAbstractDeclarator i
- Language.C.Types.Parse: IsDeclarator :: (Declarator i) -> DeclaratorOrAbstractDeclarator i
+ Language.C.Types.Parse: IsDeclarator :: Declarator i -> DeclaratorOrAbstractDeclarator i
- Language.C.Types.Parse: [declaratorDirect] :: Declarator i -> (DirectDeclarator i)
+ Language.C.Types.Parse: [declaratorDirect] :: Declarator i -> DirectDeclarator i
- Language.C.Types.Parse: quickCParser :: CParserContext i -> String -> (ReaderT (CParserContext i) (Parsec String ()) a) -> a
+ Language.C.Types.Parse: quickCParser :: CParserContext i -> String -> ReaderT (CParserContext i) (Parsec String ()) a -> a
- Language.C.Types.Parse: quickCParser_ :: String -> (ReaderT (CParserContext CIdentifier) (Parsec String ()) a) -> a
+ Language.C.Types.Parse: quickCParser_ :: String -> ReaderT (CParserContext CIdentifier) (Parsec String ()) a -> a
- Language.C.Types.Parse: runCParser :: Stream s Identity Char => CParserContext i -> String -> s -> (ReaderT (CParserContext i) (Parsec s ()) a) -> Either ParseError a
+ Language.C.Types.Parse: runCParser :: Stream s Identity Char => CParserContext i -> String -> s -> ReaderT (CParserContext i) (Parsec s ()) a -> Either ParseError a
Files
- changelog.md +1/−0
- examples/gsl-ode.hs +0/−4
- inline-c.cabal +3/−3
- src/Language/C/Inline.hs +12/−1
- src/Language/C/Inline/Internal.hs +108/−12
- src/Language/C/Inline/Interruptible.hs +1/−1
- src/Language/C/Inline/Unsafe.hs +1/−1
- test/Language/C/Types/ParseSpec.hs +1/−1
- test/tests.hs +13/−1
changelog.md view
@@ -1,3 +1,4 @@+- 0.7.0.0: Add `funPtr` quasi-quoter - 0.6.0.6: Support GHC 8.4 - 0.6.0.5: Update readme - 0.6.0.4: Remove QuickCheck dependency
examples/gsl-ode.hs view
@@ -17,10 +17,6 @@ import qualified Language.C.Inline.Unsafe as CU import System.IO.Unsafe (unsafePerformIO) -#if __GLASGOW_HASKELL__ < 710-import Data.Functor ((<$>))-#endif- C.context (C.baseCtx <> C.vecCtx <> C.funCtx) C.include "<gsl/gsl_errno.h>"
inline-c.cabal view
@@ -1,14 +1,14 @@ name: inline-c-version: 0.6.1.0+version: 0.7.0.0 synopsis: Write Haskell source files including C code inline. No FFI required. description: See <https://github.com/fpco/inline-c/blob/master/README.md>. license: MIT license-file: LICENSE author: Francesco Mazzoli, Mathieu Boespflug maintainer: francesco@fpcomplete.com-copyright: (c) 2015-2016 FP Complete Corporation, (c) 2017 Francesco Mazzoli+copyright: (c) 2015-2016 FP Complete Corporation, (c) 2017-2018 Francesco Mazzoli category: FFI-tested-with: GHC == 8.2.1+tested-with: GHC == 8.2.2, GHC == 8.4.3, GHC == 8.6.1 build-type: Simple cabal-version: >=1.10 Extra-Source-Files: README.md, changelog.md
src/Language/C/Inline.hs view
@@ -36,6 +36,7 @@ , exp , pure , block+ , funPtr , include , verbatim @@ -244,7 +245,17 @@ -- | C code blocks (i.e. statements). block :: TH.QuasiQuoter-block = genericQuote IO $ inlineItems TH.Safe+block = genericQuote IO $ inlineItems TH.Safe False Nothing++-- | Easily get a 'FunPtr':+--+-- @+-- let fp: FunPtr (Ptr CInt -> IO ()) = [C.funPtr| void poke42(int *ptr) { *ptr = 42; } |]+-- @+--+-- Especially useful to generate finalizers that require C code.+funPtr :: TH.QuasiQuoter+funPtr = funPtrQuote TH.Unsafe -- doesn't make much sense for this to be "safe", but it'd be good to verify what this means -- | Emits a CPP include directive for C code associated with the current -- module. To avoid having to escape quotes, the function itself adds them when
src/Language/C/Inline/Internal.hs view
@@ -49,6 +49,7 @@ -- * Utility functions for writing quasiquoters , genericQuote+ , funPtrQuote ) where import Control.Applicative@@ -74,6 +75,8 @@ import qualified Text.PrettyPrint.ANSI.Leijen as PP import qualified Data.List as L import qualified Data.Char as C+import Data.Hashable (Hashable)+import Foreign.Ptr (FunPtr) -- We cannot use getQ/putQ before 7.10.3 because of <https://ghc.haskell.org/trac/ghc/ticket/10596> #define USE_GETQ (__GLASGOW_HASKELL__ > 710 || (__GLASGOW_HASKELL__ == 710 && __GLASGOW_HASKELL_PATCHLEVEL1__ >= 3))@@ -242,6 +245,9 @@ -- ^ Name of the function to call in the code below. , codeDefs :: String -- ^ The C code.+ , codeFunPtr :: Bool+ -- ^ If 'True', the type will be wrapped in 'FunPtr', and+ -- the call will be static (e.g. prefixed by &). } -- TODO use the #line CPP macro to have the functions in the C file@@ -275,19 +281,23 @@ void $ emitVerbatim $ out codeDefs -- Create and add the FFI declaration. ffiImportName <- uniqueFfiImportName- dec <- TH.forImpD TH.CCall codeCallSafety codeFunName ffiImportName codeType+ dec <- if codeFunPtr+ then+ TH.forImpD TH.CCall codeCallSafety ("&" ++ codeFunName) ffiImportName [t| FunPtr $(codeType) |]+ else TH.forImpD TH.CCall codeCallSafety codeFunName ffiImportName codeType TH.addTopDecls [dec] TH.varE ffiImportName -uniqueCName :: TH.Q String-uniqueCName = do+uniqueCName :: Maybe String -> TH.Q String+uniqueCName mbPostfix = do -- The name looks like this:- -- inline_c_MODULE_INDEX+ -- inline_c_MODULE_INDEX_POSTFIX -- -- Where: -- * MODULE is the module name but with _s instead of .s; -- * INDEX is a counter that keeps track of how many names we're generating -- for each module.+ -- * POSTFIX is an optional postfix to ease debuggability -- -- we previously also generated a hash from the contents of the -- C code because of problems when cabal recompiled but now this@@ -297,7 +307,10 @@ module_ <- TH.loc_module <$> TH.location let replaceDot '.' = '_' replaceDot c = c- return $ "inline_c_" ++ map replaceDot module_ ++ "_" ++ show c'+ let postfix = case mbPostfix of+ Nothing -> ""+ Just s -> "_" ++ s ++ "_"+ return $ "inline_c_" ++ map replaceDot module_ ++ "_" ++ show c' ++ postfix -- | Same as 'inlineCItems', but with a single expression. --@@ -323,7 +336,7 @@ -- ^ The C expression -> TH.ExpQ inlineExp callSafety type_ cRetType cParams cExp =- inlineItems callSafety type_ cRetType cParams cItems+ inlineItems callSafety False Nothing type_ cRetType cParams cItems where cItems = case cRetType of C.TypeSpecifier _quals C.Void -> cExp ++ ";"@@ -337,6 +350,7 @@ -- c_cos :: Double -> Double -- c_cos = $(inlineItems -- TH.Unsafe+-- False -- [t| Double -> Double |] -- (quickCParser_ \"double\" parseType) -- [("x", quickCParser_ \"double\" parseType)]@@ -345,6 +359,10 @@ inlineItems :: TH.Safety -- ^ Safety of the foreign call+ -> Bool+ -- ^ Whether to return as a FunPtr or not+ -> Maybe String+ -- ^ Optional postfix for the generated name -> TH.TypeQ -- ^ Type of the foreign call -> C.Type C.CIdentifier@@ -354,10 +372,10 @@ -> String -- ^ The C items -> TH.ExpQ-inlineItems callSafety type_ cRetType cParams cItems = do+inlineItems callSafety funPtr mbPostfix type_ cRetType cParams cItems = do let mkParam (id', paramTy) = C.ParameterDeclaration (Just id') paramTy let proto = C.Proto cRetType (map mkParam cParams)- funName <- uniqueCName+ funName <- uniqueCName mbPostfix cFunName <- case C.cIdentifierFromString funName of Left err -> fail $ "inlineItems: impossible, generated bad C identifier " ++ "funName:\n" ++ err@@ -371,19 +389,23 @@ , codeType = type_ , codeFunName = funName , codeDefs = defs+ , codeFunPtr = funPtr } ------------------------------------------------------------------------ -- Parsing runParserInQ- :: String -> C.TypeNames -> (forall m. C.CParser HaskellIdentifier m => m a) -> TH.Q a-runParserInQ s typeNames' p = do+ :: (Hashable ident)+ => String+ -> C.CParserContext ident+ -> (forall m. C.CParser ident m => m a) -> TH.Q a+runParserInQ s ctx p = do loc <- TH.location let (line, col) = TH.loc_start loc let parsecLoc = Parsec.newPos (TH.loc_filename loc) line col let p' = lift (Parsec.setPosition parsecLoc) *> p <* lift Parser.eof- case C.runCParser (haskellCParserContext typeNames') (TH.loc_filename loc) s p' of+ case C.runCParser ctx (TH.loc_filename loc) s p' of Left err -> do -- TODO consider prefixing with "error while parsing C" or similar fail $ show err@@ -531,7 +553,9 @@ genericQuote purity build = quoteCode $ \s -> do ctx <- getContext ParseTypedC cType cParams cExp <-- runParserInQ s (typeNamesFromTypesTable (ctxTypesTable ctx)) $ parseTypedC $ ctxAntiQuoters ctx+ runParserInQ s+ (haskellCParserContext (typeNamesFromTypesTable (ctxTypesTable ctx)))+ (parseTypedC (ctxAntiQuoters ctx)) hsType <- cToHs ctx cType hsParams <- forM cParams $ \(_cId, cTy, parTy) -> do case parTy of@@ -590,6 +614,78 @@ r -> r) where (ty, body) = span (/= '{') s trim x = L.dropWhileEnd C.isSpace (dropWhile C.isSpace x)++-- | Data to parse for the 'funPtr' quasi-quoter.+data FunPtrDecl = FunPtrDecl+ { funPtrReturnType :: C.Type C.CIdentifier+ , funPtrParameters :: [(C.CIdentifier, C.Type C.CIdentifier)]+ , funPtrBody :: String+ , funPtrName :: Maybe String+ } deriving (Eq, Show)++funPtrQuote :: TH.Safety -> TH.QuasiQuoter+funPtrQuote callSafety = quoteCode $ \code -> do+ ctx <- getContext+ FunPtrDecl{..} <- runParserInQ code (C.cCParserContext (typeNamesFromTypesTable (ctxTypesTable ctx))) parse+ hsRetType <- cToHs ctx funPtrReturnType+ hsParams <- forM funPtrParameters (\(_ident, typ_) -> cToHs ctx typ_)+ let hsFunType = convertCFunSig hsRetType hsParams+ inlineItems callSafety True funPtrName hsFunType funPtrReturnType funPtrParameters funPtrBody+ where+ cToHs :: Context -> C.Type C.CIdentifier -> TH.TypeQ+ cToHs ctx cTy = do+ mbHsTy <- convertType IO (ctxTypesTable ctx) cTy+ case mbHsTy of+ Nothing -> fail $ "Could not resolve Haskell type for C type " ++ pretty80 cTy+ Just hsTy -> return hsTy++ convertCFunSig :: TH.Type -> [TH.Type] -> TH.TypeQ+ convertCFunSig retType params0 = do+ go params0+ where+ go [] =+ [t| IO $(return retType) |]+ go (paramType : params) = do+ [t| $(return paramType) -> $(go params) |]++ parse :: C.CParser C.CIdentifier m => m FunPtrDecl+ parse = do+ -- skip spaces+ Parser.spaces+ -- parse a proto+ C.ParameterDeclaration mbName protoTyp <- C.parseParameterDeclaration+ case protoTyp of+ C.Proto retType paramList -> do+ args <- forM paramList $ \decl -> case C.parameterDeclarationId decl of+ Nothing -> fail $ pretty80 $+ "Un-named captured variable in decl" <+> PP.pretty decl+ Just declId -> return (declId, C.parameterDeclarationType decl)+ -- get the rest of the body+ void (Parser.symbolic '{')+ body <- parseBody+ return FunPtrDecl+ { funPtrReturnType = retType+ , funPtrParameters = args+ , funPtrBody = body+ , funPtrName = fmap C.unCIdentifier mbName+ }+ _ -> fail $ "Expecting function declaration"++ parseBody :: C.CParser C.CIdentifier m => m String+ parseBody = do+ s <- Parser.manyTill Parser.anyChar $+ Parser.lookAhead (Parser.char '}')+ s' <- msum+ [ do Parser.try $ do -- Try because we might fail to parse the 'eof'+ -- 'symbolic' because we want to consume whitespace+ void $ Parser.symbolic '}'+ Parser.eof+ return ""+ , do void $ Parser.char '}'+ s' <- parseBody+ return ("}" ++ s')+ ]+ return (s ++ s') ------------------------------------------------------------------------ -- Utils
src/Language/C/Inline/Interruptible.hs view
@@ -43,4 +43,4 @@ -- | C code blocks (i.e. statements). block :: TH.QuasiQuoter-block = genericQuote IO $ inlineItems TH.Interruptible+block = genericQuote IO $ inlineItems TH.Unsafe False Nothing
src/Language/C/Inline/Unsafe.hs view
@@ -46,4 +46,4 @@ -- | C code blocks (i.e. statements). block :: TH.QuasiQuoter-block = genericQuote IO $ inlineItems TH.Unsafe+block = genericQuote IO $ inlineItems TH.Unsafe False Nothing
test/Language/C/Types/ParseSpec.hs view
@@ -104,7 +104,7 @@ , _pdwtnParameterDeclaration :: (ParameterDeclaration i) } deriving (Typeable, Eq, Show) -data (QC.Arbitrary i) => ArbitraryContext i = ArbitraryContext+data ArbitraryContext i = ArbitraryContext { acTypeNames :: TypeNames , acIdentToString :: i -> String }
test/tests.hs view
@@ -12,6 +12,8 @@ import Prelude import qualified Test.Hspec as Hspec import Text.RawString.QQ (r)+import Foreign.Marshal.Alloc (alloca)+import Foreign.Storable (peek, poke) import qualified Language.C.Inline as C import qualified Language.C.Inline.Unsafe as CU@@ -51,11 +53,14 @@ [t| Int -> Int -> Int |] -- Call type "francescos_add" -- Call name -- C Code- [r| int francescos_add(int x, int y) { int z = x + y; return z; } |])+ [r| int francescos_add(int x, int y) { int z = x + y; return z; } |]+ False) -- not a function pointer c_add 3 4 `Hspec.shouldBe` 7 Hspec.it "inlineItems" $ do let c_add3 = $(C.inlineItems TH.Unsafe+ False -- not a function pointer+ Nothing -- no postfix [t| CInt -> CInt |] (C.quickCParser_ "int" C.parseType) [("x", C.quickCParser_ "int" C.parseType)]@@ -204,3 +209,10 @@ let ä = 3 void $ [C.exp| int { $(int ä) } |] void $ [C.exp| int { $(int Prelude.maxBound) } |]+ Hspec.it "Function pointers" $ do+ alloca $ \x_ptr -> do+ poke x_ptr 7+ let fp = [C.funPtr| void poke42(int *ptr) { *ptr = 42; } |]+ [C.exp| void { $(void (*fp)(int *))($(int *x_ptr)) } |]+ x <- peek x_ptr+ x `Hspec.shouldBe` 42