package ocaml-lua

  1. Overview
  2. Docs

The Lua Application Program Interface (OCaml binding)

Difference with the original Lua API

Here is a list of functions of which you should read documentation:

Types definitions

type state

See lua_State documentation.

type oCamlFunction = state -> int

This type corresponds to lua_CFunction. See lua_CFunction documentation.

type thread_status =
  1. | LUA_OK
  2. | LUA_YIELD
  3. | LUA_ERRRUN
  4. | LUA_ERRSYNTAX
  5. | LUA_ERRMEM
  6. | LUA_ERRERR
  7. | LUA_ERRFILE

See lua_status documentation.

type gc_command =
  1. | GCSTOP
  2. | GCRESTART
  3. | GCCOLLECT
  4. | GCCOUNT
  5. | GCCOUNTB
  6. | GCSTEP
  7. | GCSETPAUSE
  8. | GCSETSTEPMUL

This type is not present in the official API and is used by the function gc

type lua_type =
  1. | LUA_TNONE
  2. | LUA_TNIL
  3. | LUA_TBOOLEAN
  4. | LUA_TLIGHTUSERDATA
  5. | LUA_TNUMBER
  6. | LUA_TSTRING
  7. | LUA_TTABLE
  8. | LUA_TFUNCTION
  9. | LUA_TUSERDATA
  10. | LUA_TTHREAD

This type is a collection of the possible types of a Lua value, as defined by the macros in lua.h. As a reference, see the documentation of the lua_type function, and the corresponding OCaml Lua_api_lib.type_.

type 'a lua_Reader = state -> 'a -> string option

See lua_Reader documentation.

type writer_status =
  1. | NO_WRITING_ERROR
    (*

    No errors, go on writing

    *)
  2. | WRITING_ERROR
    (*

    An error occurred, stop writing

    *)
type 'a lua_Writer = state -> string -> 'a -> writer_status

See lua_Writer documentation.

Constant values

val multret : int

Option for multiple returns in `Lua.pcall' and `Lua.call'. See lua_call documentation.

val registryindex : int

Pseudo-index to access the registry. See Registry documentation.

val environindex : int

Pseudo-index to access the environment of the running C function. See Registry documentation.

val globalsindex : int

Pseudo-index to access the thread environment (where global variables live). See Registry documentation.

Exceptions

exception Error of thread_status
exception Type_error of string

Functions not present in the Lua API

val thread_status_of_int : int -> thread_status

Convert an integer into a thread_status. Raises failure on invalid parameter.

val int_of_thread_status : thread_status -> int

Convert a thread_status into an integer.

val lua_type_of_int : int -> lua_type

Convert an integer into a lua_type. Raises failure on invalid parameter.

val int_of_lua_type : lua_type -> int

Convert a lua_type into an integer.

Lua API functions

val atpanic : state -> oCamlFunction -> oCamlFunction

See lua_atpanic documentation.

val call : state -> int -> int -> unit

See lua_call documentation.

val checkstack : state -> int -> bool

See lua_checkstack documentation.

The function lua_close is not present because all the data structures of a Lua state are managed by the OCaml garbage collector.

val concat : state -> int -> unit

See lua_concat documentation.

val cpcall : state -> oCamlFunction -> 'a -> thread_status

See lua_cpcall documentation.

NOTE: this function is not a binding of the original lua_cpcall, it's rather an OCaml function with the same semantics.

WARNING: the OCaml function you want to execute in a protected environment is actually protected againt Lua errors, even memory errors, but not against OCaml errors, i.e. exceptions. If for example you run:

let ls = LuaL.newstate ();;
let my_func ls = failwith "Sorry, my fault..."; 0;;
let cpcall_result = Lua.cpcall ls my_func 42;;

cpcall will actually raise a failure, because that exception is not generated by Lua but by OCaml.

val createtable : state -> int -> int -> unit

See lua_createtable documentation.

val dump : state -> 'a lua_Writer -> 'a -> writer_status

See lua_dump documentation.

val equal : state -> int -> int -> bool

See lua_equal documentation.

val error : state -> 'a

See lua_error documentation.

val gc : state -> gc_command -> int -> int

See lua_gc documentation.

lua_getallocf not implemented in this binding

val getfenv : state -> int -> unit

See lua_getfenv documentation.

val getfield : state -> int -> string -> unit

See lua_getfield documentation.

val getglobal : state -> string -> unit

See lua_getglobal documentation. Like in the original Lua source code this function is implemented in OCaml using getfield.

val getmetatable : state -> int -> bool

See lua_getmetatable documentation.

val gettable : state -> int -> unit

See lua_gettable documentation.

val gettop : state -> int

See lua_gettop documentation.

val insert : state -> int -> unit

See lua_insert documentation.

val isboolean : state -> int -> bool

See lua_isboolean documentation.

val iscfunction : state -> int -> bool

See lua_iscfunction documentation.

val isfunction : state -> int -> bool

See lua_isfunction documentation.

val islightuserdata : state -> int -> bool

See lua_islightuserdata documentation.

val isnil : state -> int -> bool

See lua_isnil documentation.

val isnone : state -> int -> bool

See lua_isnone documentation.

val isnoneornil : state -> int -> bool

See lua_isnoneornil documentation.

val isnumber : state -> int -> bool

See lua_isnumber documentation.

val isstring : state -> int -> bool

See lua_isstring documentation.

val istable : state -> int -> bool

See lua_istable documentation.

val isthread : state -> int -> bool

See lua_isthread documentation.

val isuserdata : state -> int -> bool

See lua_isuserdata documentation.

val lessthan : state -> int -> int -> bool

See lua_lessthan documentation.

val load : state -> 'a lua_Reader -> 'a -> string -> thread_status

See lua_load documentation.

The function lua_newstate is not present because it makes very little sense to specify a custom allocator written in OCaml. To create a new Lua state, use the function Lua_aux_lib.newstate.

val newtable : state -> unit

See lua_newtable documentation.

val newthread : state -> state

See lua_newthread documentation.

When you create a new thread, this binding guaranties that the Lua object will remain "living" (protected from both the Lua and the OCaml garbage collectors) until a valid copy exists in at least one of the two contexts.

Remember that all the threads obtained by newthread and Lua_api_lib.tothread are shared copies, for example:

let state = LuaL.newstate ();;
let th = Lua.newthread state;;
let th' = match Lua.tothread state 1 with Some s -> s | None -> failwith "not an option!";;
Lua.settop state 0;;

Now the stack of state is empty and you have two threads, th and th', but they are actually the very same data structure and operations performed on the first will be visible on the second!

Another important issue regarding the scope of a state object representing a thread (coroutine): this binding don't prevent you from accessing invalid memory in case of misuse of the library. Please, carefully consider this fragment:

let f () =
  let state = LuaL.newstate () in
  let th = Lua.newthread state in
  th;;

let th' = f ();;
Gc.compact ();; (* This will collect [state] inside [f] *)
(* Here something using [th'] *)

After Gc.compact the value inside th' has lost any possible meaning, because it's a thread (a coroutine) of a state object that has been already collected. Using th' will lead to a segmentation fault, at best, and to an undefined behaviour if you are unlucky.

val default_gc : state -> int

This is the default "__gc" function attached to any new userdatum created with newuserdata. See documentation of newuserdata below.

val make_gc_function : oCamlFunction -> oCamlFunction

This function takes an oCamlFunction you have created to be executed as "__gc" metamethod and "decorates" it with some default actions needed to deallocate all the memory.

If you want to create a "__gc" method for your userdata, you must register the value from make_gc_function.

val newuserdata : state -> 'a -> unit

newuserdata is the binding of lua_newuserdata but it works in a different way if compared to the original function, and the signature is slightly different.

In C lua_newuserdata allocates an area for you, returns a void* and you cast it as needed. Moreover, it pushes the new userdata on the stack.

In OCaml, however, you never allocates a value and so the resulting signature provides you a way to push an already created value on the top of the Lua stack.

Very important remark, read carefully. The original Lua lua_newuserdata doesn't associate to the new userdatum any metatable, it's up to you to define a metatable with metamethods, if you need it. On the other hand, this binding silently creates a metatable with only one metamethod ("__gc") and associates the function default_gc defined above. This function takes care of managing the memory between the two garbage collectors when needed. This is transparent to you, unless you want to attach to the userdatum a metatable of your, which is very likely to happen.

In case you want to attach a metatable to your userdatum you must include the "__gc" metamethod, and you must create the function using make_gc_function described above.If you want a metatable for your userdatum but you don't need a "__gc", use in any case the default_gc. Don't create a userdatum with a metatable and without "__gc" or your program will leak memory!

WARNING: using this function could be harmful because it actually breaks the type system. It has the same semantics of Obj.magic, allowing the programmer to push an OCaml value into the Lua state, and then retrieve it with a different type. Be very careful!

val next : state -> int -> int

See lua_next documentation.

val objlen : state -> int -> int

See lua_objlen documentation.

val pcall : state -> int -> int -> int -> thread_status

See lua_pcall documentation.

val pop : state -> int -> unit

See lua_pop documentation.

val pushboolean : state -> bool -> unit

See lua_pushboolean documentation.

The function lua_pushcclosure is not present because it makes very little sense to specify a "closure" written in OCaml, using the Lua upvalues machinery. Use instead Lua_api_lib.pushcfunction

val pushcfunction : state -> oCamlFunction -> unit

See lua_pushcfunction documentation.

val pushocamlfunction : state -> oCamlFunction -> unit
val pushfstring : state -> ('a, unit, string, string) Stdlib.format4 -> 'a

Pushes onto the stack a formatted string and returns the string itself. It is similar to the standard library function sprintf.

Warning: this function has a different behavior with respect to the original lua_pushfstring because the conversion specifiers are not restricted as specified in the Lua documentation, but you can use all the conversions of the Printf module.

val pushinteger : state -> int -> unit

See lua_pushinteger documentation.

val pushlightuserdata : state -> 'a -> unit

See lua_pushlightuserdata documentation. Raises Not_a_block_value if you try to push a non-block value (e.g. an immediate integer) as a light userdata.

In Lua a light userdata is a way to store inside the Lua state a C pointer. It's up the programmer to carefully check for the lifetime of the data structures passed to Lua via a light userdata. If you malloc a pointer and pass it to Lua, then you free it from C and then you retrieve the same pointer from Lua (using lua_touserdata), you are most probably shooting yourself in the foot.

To avoid this class of problems I decided to implement some logic in the binding of this function. When you push an OCaml value as a Lua light userdata, a global reference to that (OCaml) value is kept inside the Lua state L. So, if the original value goes out of scope it is not collected by the garbage collector. In this scenario:

let push_something state =
  let ocaml_value = get_some_complex_value () in
  pushlightuserdata state ocaml_value;
  state
;;

when the push_something function returns the Lua state, the ocaml_value is not collected and can be retrieved at a later time from state.

This behaviour has a major drawback: while ensuring the lifetime of objects, it wastes memory. All the OCaml values pushed as light userdata will in fact be collected when the garbage collector decide to collect the Lua state itself. This means that if you have a long running task (e.g. a server) with a Lua state and you use pushlightuserdata, the values pushed will be never collected!

Moreover, if you push a value that have some resources associated with it (e.g. a channel, a socket or a DB handler) the resources will be released only when the Lua state goes out of scope.

val pushliteral : state -> string -> unit

See lua_pushliteral documentation.

val pushlstring : state -> string -> unit

See lua_pushlstring documentation.

val pushnil : state -> unit

See lua_pushnil documentation.

val pushnumber : state -> float -> unit

See lua_pushnumber documentation.

val pushstring : state -> string -> unit

See lua_pushstring documentation.

val pushthread : state -> bool

See lua_pushthread documentation.

val pushvalue : state -> int -> unit

See lua_pushvalue documentation.

val pushvfstring : state -> ('a, unit, string, string) Stdlib.format4 -> 'a
val rawequal : state -> int -> int -> bool

See lua_rawequal documentation.

val rawget : state -> int -> unit

See lua_rawget documentation.

val rawgeti : state -> int -> int -> unit

See lua_rawgeti documentation.

val rawset : state -> int -> unit

See lua_rawset documentation.

val rawseti : state -> int -> int -> unit

See lua_rawseti documentation.

val register : state -> string -> oCamlFunction -> unit

See lua_register documentation. The function is implemented in OCaml using pushcfunction and setglobal.

val remove : state -> int -> unit

See lua_remove documentation.

val replace : state -> int -> unit

See lua_replace documentation.

val resume : state -> int -> thread_status

See lua_resume documentation.

lua_setallocf not implemented in this binding

val setfenv : state -> int -> bool

See lua_setfenv documentation.

val setfield : state -> int -> string -> unit

See lua_setfield documentation.

val setglobal : state -> string -> unit

See lua_setglobal documentation.

val setmetatable : state -> int -> int

See lua_setmetatable documentation.

val settable : state -> int -> unit

See lua_settable documentation.

val settop : state -> int -> unit

See lua_settop documentation.

val status : state -> thread_status

See lua_status documentation.

val toboolean : state -> int -> bool

See lua_toboolean documentation.

val tocfunction : state -> int -> oCamlFunction option

See lua_tocfunction documentation.

val toocamlfunction : state -> int -> oCamlFunction option
val tointeger : state -> int -> int

See lua_tointeger documentation.

val tolstring : state -> int -> string option

See lua_tolstring documentation.

NOTE: The original len argument is missing because, unlike in C, there is no impedance mismatch between OCaml and Lua strings

val tonumber : state -> int -> float

See lua_tonumber documentation.

The function lua_topointer is not available

val tostring : state -> int -> string option
val tothread : state -> int -> state option

See lua_tothread documentation.

val touserdata : state -> int -> [> `Userdata of 'a | `Light_userdata of 'a ] option

If the value at the given acceptable index is a full userdata, returns its value as Some `Userdata v. If the value is a light userdata, returns its value as Some `Light_userdata v. Otherwise, returns None.

WARNING: using this function could be harmful because it actually breaks the type system. It has the same semantics of Obj.magic, allowing the programmer to push an OCaml value into the Lua state, and then retrieve it with a different type. Be very careful!

val type_ : state -> int -> lua_type

See lua_type documentation.

val typename : state -> lua_type -> string

See lua_typename documentation.

val xmove : state -> state -> int -> unit

See lua_xmove documentation.

val yield : state -> int -> int

See lua_yield documentation.