Copyright | © 2007–2012 Gracjan Polak 2012–2016 Ömer Sinan Ağacan 2017 Albert Krewinkel |
---|---|
License | MIT |
Maintainer | Albert Krewinkel <tarleb+hslua@zeitkraut.de> |
Stability | beta |
Portability | FlexibleInstances, ForeignFunctionInterface, ScopedTypeVariables |
Safe Haskell | None |
Language | Haskell98 |
Bindings, functions, and utilities enabling the integration of a lua interpreter into a haskell project.
- newtype Lua a = Lua {}
- luaState :: Lua LuaState
- runLuaWith :: LuaState -> Lua a -> IO a
- liftIO :: MonadIO m => forall a. IO a -> m a
- class FromLuaStack a where
- peekEither :: FromLuaStack a => StackIndex -> Lua (Either String a)
- toList :: FromLuaStack a => StackIndex -> Lua [a]
- pairsFromTable :: (FromLuaStack a, FromLuaStack b) => StackIndex -> Lua [(a, b)]
- class ToLuaStack a where
- pushList :: ToLuaStack a => [a] -> Lua ()
- type PreCFunction = LuaState -> IO NumResults
- type HaskellFunction = Lua NumResults
- callFunc :: LuaCallFunc a => String -> a
- newCFunction :: ToHaskellFunction a => a -> Lua CFunction
- freeCFunction :: CFunction -> Lua ()
- pushHaskellFunction :: ToHaskellFunction a => a -> Lua ()
- registerHaskellFunction :: ToHaskellFunction a => String -> a -> Lua ()
- runLua :: Lua a -> IO a
- runLuaEither :: Lua a -> IO (Either LuaException a)
- getglobal' :: String -> Lua ()
- module Foreign.Lua.Api
- module Foreign.Lua.Api.Types
- data LuaException = LuaException String
- catchLuaError :: Lua a -> (LuaException -> Lua a) -> Lua a
- throwLuaError :: String -> Lua a
- tryLua :: Lua a -> Lua (Either LuaException a)
Documentation
Lua computation
runLuaWith :: LuaState -> Lua a -> IO a Source #
Run lua computation with custom lua state. Errors are left unhandled, the caller of this function is responsible to catch lua errors.
Receiving values from Lua stack (Lua → Haskell)
class FromLuaStack a where Source #
A value that can be read from the Lua stack.
peek :: StackIndex -> Lua a Source #
Check if at index n
there is a convertible Lua value and if so return
it. Throws a
otherwise.LuaException
peekEither :: FromLuaStack a => StackIndex -> Lua (Either String a) Source #
Try to convert the value at the given stack index to a haskell value.
Returns Left
with an error message on failure.
toList :: FromLuaStack a => StackIndex -> Lua [a] Source #
Read a table into a list
pairsFromTable :: (FromLuaStack a, FromLuaStack b) => StackIndex -> Lua [(a, b)] Source #
Read a table into a list of pairs.
Pushing values to Lua stack (Haskell → Lua)
class ToLuaStack a where Source #
A value that can be pushed to the Lua stack.
Pushes a value onto Lua stack, casting it into meaningfully nearest Lua type.
pushList :: ToLuaStack a => [a] -> Lua () Source #
Push list as numerically indexed table.
Calling Functions
type PreCFunction = LuaState -> IO NumResults Source #
Type of raw haskell functions that can be made into CFunction
s.
type HaskellFunction = Lua NumResults Source #
Haskell function that can be called from Lua.
callFunc :: LuaCallFunc a => String -> a Source #
Call a Lua function. Use as:
v <- callfunc "proc" "abc" (1::Int) (5.0::Double)
newCFunction :: ToHaskellFunction a => a -> Lua CFunction Source #
Create new foreign Lua function. Function created can be called
by Lua engine. Remeber to free the pointer with freecfunction
.
freeCFunction :: CFunction -> Lua () Source #
Free function pointer created with newcfunction
.
pushHaskellFunction :: ToHaskellFunction a => a -> Lua () Source #
Pushes Haskell function converted to a Lua function. All values created will be garbage collected. Use as:
pushHaskellFunction myfun setglobal "myfun"
You are not allowed to use lua_error
anywhere, but
use an error code of (-1) to the same effect. Push
error message as the sole return value.
registerHaskellFunction :: ToHaskellFunction a => String -> a -> Lua () Source #
Imports a Haskell function and registers it at global name.
Utility functions
runLua :: Lua a -> IO a Source #
Run lua computation using the default HsLua state as starting point. Raised exceptions are passed through; error handling is the responsibility of the caller.
runLuaEither :: Lua a -> IO (Either LuaException a) Source #
Run the given Lua computation; exceptions raised in haskell code are caught, but other exceptions (user exceptions raised in haskell, unchecked type errors, etc.) are passed through.
getglobal' :: String -> Lua () Source #
Like getglobal
, but knows about packages. e. g.
getglobal' l "math.sin"
returns correct result
API
module Foreign.Lua.Api
module Foreign.Lua.Api.Types
Error handling in hslua
We are trying to keep error handling on the haskell side very simple and intuitive. However, when combined with error handling on the Lua side, it get's tricky: We can call Haskell from Lua which calls Lua again etc. At each language boundary we should check for errors and propagate them properly to the next level in stack. Hslua does this for you when returning from Lua to Haskell, but care must be taken when passing errors back into Lua.
Let's say we have this call stack: (stack grows upwards)
Haskell function Lua function Haskell program
and we want to report an error in the top-most Haskell function. We can't
use lua_error
from the Lua C API, because it uses longjmp
, which means
it skips layers of abstractions, including the Haskell RTS. There's no way
to prevent this longjmp
. lua_pcall
sets the jump target, but even with
lua_pcall
it's not safe. Consider this call stack:
Haskell function which calls lua_error Lua function, uses pcall Haskell program
This program jumps to Lua function, skipping Haskell RTS code that would run
before Haskell function returns. For this reason we can use
lua_pcall
(
) only for catching errors from Lua, and even in that case
we need to make sure there are no Haskell calls between error-throwing Lua
call and our pcall
call.pcall
To be able to catch errors from Haskell functions in Lua, we need to find a
convention. Currently hslua does this:
has same type as Lua's
lerror
lua_error
, but instead of calling real lua_error
, it's returning two
values: A special value _HASKELLERR
and error message as a string.
Using this, we can write a function to catch errors from Haskell like this:
function catch_haskell(ret, err_msg) if ret == _HASKELLERR then print("Error caught from Haskell land: " .. err_msg) return end return ret end
(_HASKELLERR
is created by newstate
)
(Type errors in Haskell functions are also handled using this convention. E.g., if you pass a Lua value with the wrong type to a Haskell function, the error will be reported in this way)
At this point our call stack is like this:
Lua function (Haskell function returned with error, which we caught) Haskell program
If we further want to propagate the error message to Haskell program, we we
can just use standard error
function and use
in Haskell side.
Note that if we use pcall
error
on the Lua side and forget to use pcall
in
the calling Haskell function, we would be starting to skip layers of
abstractions and would get a segfault in the best case. That's why hslua
wraps all API functions that can potentially fail in custom C functions.
Those functions behave idential to the functions they wrap, but catch all
errors and return error codes instead. Using error
within Lua should
hence be safe.
However, the raw C API bindings in
don't
provide these guarantees. Even an apparently harmless operations like
accessing a field via RawBindings
can call a meta method and trigger a
lua_getfield
longjmp
, causing the host program to crash.
The
function is not wrapped in additional C code but still safe.
The reason it's safe is because the pcall
lua_pcall
C function is calling the
Lua function using Lua C API, and when the called Lua function calls
error
it longjmp
s to lua_pcall
C function, without skipping any
layers of abstraction. lua_pcall
then returns to Haskell.
NOTE: If you're loading a hslua program compiled to a dynamic library from
a Lua program, you need to define _HASKELLERR = {}
manually, after
creating the Lua state.
data LuaException Source #
Exceptions raised by Lua-related operations.
catchLuaError :: Lua a -> (LuaException -> Lua a) -> Lua a Source #
Catch a
.LuaException
throwLuaError :: String -> Lua a Source #
Raise a
containing the given error message.LuaException