{-# LINE 1 "src/Foreign/Lua/FunctionCalling.hsc" #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Foreign.Lua.FunctionCalling
( Peekable (..)
, LuaCallFunc (..)
, ToHaskellFunction (..)
, HaskellFunction
, Pushable (..)
, PreCFunction
, toHaskellFunction
, callFunc
, freeCFunction
, newCFunction
, pushHaskellFunction
, pushPreCFunction
, registerHaskellFunction
) where
import Foreign.C (CInt (..))
import Foreign.Lua.Core as Lua
import Foreign.Lua.Core.Types (liftLua)
import Foreign.Lua.Raw.Call (hslua_pushhsfunction)
import Foreign.Lua.Types
import Foreign.Lua.Util (getglobal', popValue)
import Foreign.Ptr (freeHaskellFunPtr)
type PreCFunction = State -> IO NumResults
type HaskellFunction = Lua NumResults
class ToHaskellFunction a where
toHsFun :: StackIndex -> a -> Lua NumResults
instance {-# OVERLAPPING #-} ToHaskellFunction HaskellFunction where
toHsFun _ = id
instance Pushable a => ToHaskellFunction (Lua a) where
toHsFun _narg x = 1 <$ (x >>= push)
instance (Peekable a, ToHaskellFunction b) =>
ToHaskellFunction (a -> b) where
toHsFun narg f = getArg >>= toHsFun (narg + 1) . f
where
getArg = Lua.withExceptionMessage (errorPrefix <>) (peek narg)
errorPrefix = "could not read argument " <>
show (fromStackIndex narg) <> ": "
toHaskellFunction :: ToHaskellFunction a => a -> HaskellFunction
toHaskellFunction a = do
errConv <- Lua.errorConversion
let ctx = "Error during function call: "
Lua.exceptionToError errConv . Lua.addContextToException errConv ctx $
toHsFun 1 a
newCFunction :: ToHaskellFunction a => a -> Lua CFunction
newCFunction f = do
e2e <- Lua.errorConversion
liftIO . mkWrapper . flip (Lua.runWithConverter e2e) . toHaskellFunction $ f
foreign import ccall unsafe "wrapper"
mkWrapper :: PreCFunction -> IO CFunction
freeCFunction :: CFunction -> Lua ()
freeCFunction = liftIO . freeHaskellFunPtr
class LuaCallFunc a where
callFunc' :: String -> Lua () -> NumArgs -> a
instance Peekable a => LuaCallFunc (Lua a) where
callFunc' fnName pushArgs nargs = do
getglobal' fnName
pushArgs
call nargs 1
popValue
instance (Pushable a, LuaCallFunc b) => LuaCallFunc (a -> b) where
callFunc' fnName pushArgs nargs x =
callFunc' fnName (pushArgs *> push x) (nargs + 1)
callFunc :: (LuaCallFunc a) => String -> a
callFunc f = callFunc' f (return ()) 0
registerHaskellFunction :: ToHaskellFunction a => String -> a -> Lua ()
registerHaskellFunction n f = do
pushHaskellFunction f
setglobal n
pushHaskellFunction :: ToHaskellFunction a => a -> Lua ()
pushHaskellFunction hsFn = do
errConv <- Lua.errorConversion
preCFn <- return . flip (runWithConverter errConv) $ toHaskellFunction hsFn
pushPreCFunction preCFn
pushPreCFunction :: PreCFunction -> Lua ()
pushPreCFunction preCFn = liftLua $ \l ->
hslua_pushhsfunction l preCFn