Code archives/Miscellaneous/Lua State Wrapper

This code has been declared by its author to be Public Domain code.

Download source code

Lua State Wrapper by N2005
This is a fairly simple OO wrapper around the Lua API (not including the debugging API, and most of the auxiliary API is ignored). In most situations, names are the same or similar to the equivalent lua_* function.

There are Load* methods provided that will load a string, buffer, or URL as a function. No complementary Do(...) methods are provided, as you can simply call Load(...).Call() through the hacked method chaining (by returning Self for most methods).
SuperStrict

Import Pub.Lua

Private

Function _lsb@ Ptr(s@ Ptr, d@ Ptr, sz:Int Ptr)
	Const _LBUF_READ:Int=1024
	Local bufP:Byte Ptr Ptr = Byte Ptr Ptr(d+4)
	Local remainderP:Int Ptr = Int Ptr(d)
	If remainderP[0] = 0 Then
		sz[0] = 0
		Return Null
	EndIf
	Local p@ Ptr = bufP[0]
	sz[0] = Min(remainderP[0],_LBUF_READ)
	remainderP[0] :- sz[0]
	bufP[0] :+ sz[0]
	Return p
End Function

Function _ls_alloc@ Ptr(ud@ Ptr, p@ Ptr, osize:Int, nsize:Int)
	If nsize = 0 Then
		MemFree(p)
		Return NULL
	ElseIf p Then
		Return MemExtend(p, osize, nsize)
	Else
		Return MemAlloc(nsize)
	EndIf
End Function

Public

Rem
bbdoc: A class for interfacing with Lua in an object-oriented manner.
EndRem
Type Lua
	Field _disposable:Int = False
	Field _state@ Ptr = Null
	
	Method Delete()
		Dispose()
	End Method

	Rem
	bbdoc:	Forces the disposal of the class and/or Lua state (depending on the settings specified
	on creation).
	EndRem
	Method Dispose:Lua()
		If _disposable = True And _state <> Null Then
			lua_close(_state)
		EndIf
		_state = Null
		Return Self
	End Method
	
	Method InitWithState:Lua(L@Ptr, canFree:Int = False)
		_state = L
		_disposable = canFree=True
		Return Self
	End Method
	
	Method Init:Lua()
		Return InitWithState(lua_newstate(_ls_alloc, Null), True)
	End Method
	
	Method IsValid:Int()
		Return (_state<>Null)
	End Method
	
	Rem
	bbdoc:	Creates a new Lua from an existing Lua state.<br/>
	<br/>
	If you pass True to @canFree, the state will be freed when the class is disposed of (either on
	garbage collection or when the @Dispose method is called).<br/>
	<br/>
	When extending this class, you should override this function so that it creates the extended
	class.	 That, or you can just do
	<pre>New YourExtendedClass.InitWithState(state, True/False)</pre>, provided you override
	InitWithState.
	EndRem
	Function FromState:Lua(s@ Ptr, canFree:Int = False)
		Return New Lua.InitWithState(s, canFree)
	End Function

	Rem
	bbdoc:	Creates a new Lua class and Lua state.	View @FromState for extra advice regarding
	implementation.
	EndRem
	Function Create:Lua()
		Return New Lua.Init()
	End Function

	Rem
	bbdoc:	Loads the lualib libraries.
	EndRem
	Method LoadLibraries:Lua()
		luaopen_base(_state)
		luaopen_package(_state)
		luaopen_string(_state)
		luaopen_math(_state)
		luaopen_table(_state)
		luaopen_io(_state)
		luaopen_os(_state)
		luaopen_debug(_state)
		Return Self
	End Method
	
	Method LoadBaseLibrary:Lua()
		luaopen_base(_state)
		Return Self
	End Method
	
	Method LoadPackageLibrary:Lua()
		luaopen_package(_state)
		Return Self
	End Method
	
	Method LoadStringLibrary:Lua()
		luaopen_string(_state)
		Return Self
	End Method
	
	Method LoadMathLibrary:Lua()
		luaopen_math(_state)
		Return Self
	End Method
	
	Method LoadTableLibrary:Lua()
		luaopen_table(_state)
		Return Self
	End Method
	
	Method LoadIOLibrary:Lua()
		luaopen_io(_state)
		Return Self
	End Method
	
	Method LoadOSLibrary:Lua()
		luaopen_os(_state)
		Return Self
	End Method
	
	Method LoadDebugLibrary:Lua()
		luaopen_debug(_state)
		Return Self
	End Method
	
	Method LoadBuffer:Lua(buf@ Ptr, length:Int, name$=Null)
		Global _lua_load:Int(L@Ptr,R@Ptr(L@Ptr,O@Ptr,S:Int Ptr),O@Ptr,N@Ptr) = Byte Ptr(lua_load)
		Local mem:Int Ptr = Int Ptr(MemAlloc(8))
		mem[0] = length
		mem[1] = Int(buf)
		
		Local nstr@ Ptr = Null
		If name Then nstr = name.ToCString()
		
		Local r:Int = _lua_load(_state, _lsb, mem, nstr)
		MemFree(mem)
		If nstr Then MemFree(nstr)
		
		Local err$
		If r <> 0 Then
			err = GetString()
			Pop 1
		EndIf
		Select r
		Case 0	' success
		Case 1
			Throw "Runtime error during lua_load:~n"+err
		Case 2
			Throw "File error during lua_load:~n"+err
		Case 3
			Throw "Syntax error during lua_load:~n"+err
		Default
			Throw "Error during lua_load:~n"+err
		End Select
		
		Return Self
	End Method
	
	Rem
	bbdoc:	Loads a string as a function and pushes it onto the stack.
	EndRem
	Method LoadString:Lua(s$, name$=Null)
		Local str@ Ptr = s.ToCString()
		Try
			LoadBuffer(str, s.Length, name)
		Catch ex:Object
			MemFree(str)
			Throw ex
		End Try
		MemFree(str)
		Return Self
	End Method

	Rem
	bbdoc:	Loads the contents of a stream or a string as a function and pushes it onto the stack.
	EndRem
	Method LoadURL:Lua(url:Object, length:Int=-1, name$=Null)
		Local s:TStream = OpenStream(url, True, False)
		If s = Null
			Throw "Could not open stream for reading"
		EndIf
		If length = -1 Then length = s.Size()
		Local buf@ Ptr = MemAlloc(length)
		s.ReadBytes(buf, length)
		s.Close(); s = Null
		Try
			LoadBuffer(buf, length, name)
		Catch ex:Object
			MemFree(buf)
			Throw ex
		End Try
		MemFree(buf)
		Return Self
	End Method
	
	Rem
	bbdoc:	Registers a callback so it's accessible to Lua scripts.
	EndRem
	Method RegisterFunction:Lua(name$, func:Int(state@ Ptr), upvalues:Int=0)
		lua_pushlstring(_state, name, name.Length)
		lua_pushcclosure(_state, func, upvalues)
		lua_rawset(_state, LUA_GLOBALSINDEX)
		Return Self
	End Method

	Rem
	bbdoc:	Pops @n elements from the stack.
	EndRem
	Method Pop:Lua(n:Int = 1)
		lua_settop(_state, -(n+1))
		Return Self
	End Method

	Rem
	bbdoc:	Gets a value from a table.<br/>
	<br/>
	To use this, you have to push the key (be it a number or string or what have you) and call this
	with the correct index of the table.  The key will be popped and the resulting value will be
	pushed onto the stack.
	EndRem
	Method GetTable:Lua(idx:Int)
		lua_gettable(_state, idx)
		Return Self
	End Method

	Rem
	bbdoc:	Gets a value from a table.<br/>
	<br/>
	To use this, you have to push the key (be it a number or string or what have you) and value,
	then call this with the correct index of the table.  The key and value will be popped from the
	stack, no cleanup is neccessary.
	EndRem
	Method SetTable:Lua(idx:Int)
		lua_settable(_state, idx)
		Return Self
	End Method
	
	Method RawGet:Lua(idx:Int)
		lua_rawget(_state, idx)
		Return Self
	End Method
	
	Method RawSet:Lua(idx:Int)
		lua_rawset(_state, idx)
		Return Self
	End Method
	
	Method RawGetWithIndex:Lua(idx:Int, n%)
		lua_rawgeti(_state, idx, n)
		Return Self
	End Method
	
	Method RawSetWithIndex:Lua(idx:Int, n%)
		lua_rawseti(_state, idx, n)
		Return Self
	End Method
	
	Method GetNext:Int(idx:Int)
		Return lua_next(_state, idx)
	End Method

	Rem
	bbdoc:	Creates a table and pushes it onto the stack.
	EndRem
	Method CreateTable:Lua(narr:Int=0, nrec:Int=0)
		lua_createtable(_state, narr, nrec)
		Return Self
	End Method
	
	Rem
	bbdoc: Creates a new userdata with the specified @size, pushes it onto the stack, and returns
	a pointer to the userdata.
	EndRem
	Method CreateUserdata@ Ptr(size:Int)
		Return lua_newuserdata(_state, size)
	End Method
	
	Rem
	bbdoc: Creates a new thread, pushes it onto the stack, and returns a new Lua state for the new
	thread.
	EndRem
	Method CreateThread:Lua()
		Local L@Ptr = lua_newthread(_state)
		Return New Lua.InitWithState(L, False)
	End Method
	
	Method PushThread:Lua(thread:Lua)
		lua_pushthread(thread._state)
		Return Self
	End Method

	Rem
	bbdoc:	Pushes a string onto the stack.
	EndRem
	Method PushString:Lua(s$)
		lua_pushlstring(_state, s, s.Length)
		Return Self
	End Method

	Rem
	bbdoc:	Pushes a boolean value onto the stack.	True or False only.
	EndRem
	Method PushBool:Lua(b:Int)
		lua_pushboolean(_state, b)
		Return Self
	End Method

	Rem
	bbdoc:	Pushes a number onto the stack.
	EndRem
	Method PushNumber:Lua(n!)
		lua_pushnumber(_state, n)
		Return Self
	End Method
	
	Rem
	bbdoc:	Pushes an integer onto the stack.
	EndRem
	Method PushInteger:Lua(n%)
		lua_pushinteger(_state, n)
		Return Self
	End Method

	Rem
	bbdoc:	Pushes a nil (Null) value onto the stack.  This is <em>not</em> the same as pushing a
	Null pointer onto the stack.
	EndRem
	Method PushNil:Lua()
		lua_pushnil(_state)
		Return Self
	End Method

	Rem
	bbdoc:	Pushes a pointer (light userdata) onto the stack.
	EndRem
	Method PushLightUserdata:Lua(p@ Ptr)
		lua_pushlightuserdata(_state, p)
		Return Self
	End Method
	
	Rem
	bbdoc:	Pushes a C closure onto the stack.
	EndRem
	Method PushCClosure:Lua(fn:Int(L@Ptr), upvalues:Int=0)
		lua_pushcclosure(_state, fn, upvalues)
		Return Self
	End Method
	
	Rem
	bbdoc:	Pushes the value at @idx onto the top of the stack.
	EndRem
	Method PushValue:Lua(idx:Int)
		lua_pushvalue(_state, idx)
		Return Self
	End Method
	
	Rem
	bbdoc:	Inserts the value at the top of the stack to the stack position @idx.
	EndRem
	Method InsertValue:Lua(idx:Int)
		lua_insert(_state, idx)
		Return Self
	End Method
	
	Method RemoveValue:Lua(idx:Int)
		lua_remove(_state, idx)
		Return Self
	End Method
	
	Method ReplaceValue:Lua(idx:Int)
		lua_replace(_state, idx)
		Return Self
	End Method

	Rem
	bbdoc:	Gets a string from @idx.
	EndRem
	Method GetString$(idx:Int = -1)
		Return String.FromCString(lua_tolstring(_state, idx, Null))
	End Method

	Rem
	bbdoc:	Gets a boolean from @idx.
	EndRem
	Method GetBool:Int(idx:Int = -1)
		Return lua_toboolean(_state, idx)
	End Method

	Rem
	bbdoc:	Gets a number from @idx.
	EndRem
	Method GetNumber!(idx:Int = -1)
		Return lua_tonumber(_state, idx)
	End Method
	
	Rem
	bbdoc:	Gets an integer from @idx.
	EndRem
	Method GetInteger:Int(idx:Int = -1)
		Return lua_tointeger(_state, idx)
	End Method
	
	Method GetFunction%(L@Ptr)(idx%)
		Return lua_tocfunction(_state, idx)
	End Method

	Rem
	bbdoc:	Gets a pointer (light userdata) from @idx.
	EndRem
	Method GetUserdata@ Ptr(idx:Int = -1)
		Return lua_touserdata(_state, idx)
	End Method

	Rem
	bbdoc:	Gets a thread from @idx.
	EndRem
	Method GetThread:Lua(idx:Int = -1)
		Return New Lua.InitWithState(lua_tothread(_state, idx), False)
	End Method
	
	Rem
	bbdoc:	Concatenates @n values at the top of the stack and pushes the result.
	EndRem
	Method Concatenate:Lua(n:Int)
		lua_concat(_state, n)
		Return Self
	End Method
	
	Rem
	bbdoc:	Performs 'left < right' where left and right are the values at the indices provided by
	@left and @right.  Returns true if the left value is smaller than the right value, otherwise
	false.
	EndRem
	Method LessThan:Int(left:Int, right:Int)
		Return lua_lessthan(_state, left, right)
	End Method
	
	Rem
	bbdoc:	Performs 'left == right' where left and right are the values at the indices provided by
	@left and @right.  Returns true if the left value is smaller than the right value, otherwise
	false.
	EndRem
	Method Equal:Int(left:Int, right:Int)
		Return lua_equal(_state, left, right)
	End Method
	
	Method RawEqual:Int(left:Int, right:Int)
		Return lua_rawequal(_state, left, right)
	End Method
	
	Method Length:Int(idx%=-1)
		Return lua_objlen(_state, idx)
	End Method
	
	Rem
	bbdoc:	Generates a Lua error whose message is the value at the top of the stack.<br/><br/>
	This function never returns.
	EndRem
	Method Error:Lua()
		lua_error(_state)
		Return Self
	End Method
	
	Rem
	bbdoc:	Generates a Lua error with the specified @message.<br/><br/>
	This function never returns.
	EndRem
	Method ErrorWithMessage:Lua(message$)
		PushString(message)
		lua_error(_state)
		Return Self
	End Method
	
	Rem
	bbdoc:	Generates a Lua error with the specified @message (if provided) if @cond is true.<br/>
	<br/>
	This function never returns.
	EndRem
	Method ErrorIf:Lua(cond:Int, message$="")
		If cond Then
			PushString(message)
			Error
		EndIf
		Return Self
	End Method

	Rem
	bbdoc:	Returns the type of value at @idx.<br/>
	<br/>
	This can be any of the following values:<br/>
	<ul>
	<li>LUA_TNONE</li>
	<li>LUA_TNIL</li>
	<li>LUA_TBOOLEAN</li>
	<li>LUA_TNUMBER</li>
	<li>LUA_TSTRING</li>
	<li>LUA_TUSERDATA</li>
	<li>LUA_TLIGHTUSERDATA</li>
	<li>LUA_TTABLE</li>
	<li>LUA_TFUNCTION</li>
	<li>LUA_TTHREAD</li>
	<li>LUA_TNONE</li>
	</ul>
	EndRem
	Method GetType:Int(idx:Int = -1)
		Return lua_type(_state, idx)
	End Method
	
	Method GetTypename$(idx:Int = -1)
		Return lua_typename(_state, idx)
	End Method

	Rem
	bbdoc:	Returns whether or not the value at @idx is of the specified type (or can be converted
	to that type).<br/><br/>
	
	ORed combinations of types work, but will not check whether or not the value at @idx can be
	converted to the specified types.
	EndRem
	Method Is:Int(idx:Int, klass:Int)
		Select klass
		Case LUA_TTABLE
			Return lua_istable(_state, idx)
		Case LUA_TSTRING
			Return lua_isstring(_state, idx)
		Case LUA_TUSERDATA
			Return lua_isuserdata(_state, idx)
		Case LUA_TLIGHTUSERDATA
			Return lua_islightuserdata(_state, idx)
		Case LUA_TNUMBER
			Return lua_isnumber(_state, idx)
		Case LUA_TNIL
			Return lua_isnil(_state, idx)
		Case LUA_TNONE
			Return lua_isnone(_state, idx)
		Case LUA_TNIL|LUA_TNONE
			Return lua_isnoneornil(_state, idx)
		Case LUA_TTHREAD
			Return lua_isthread(_state, idx)
		Case LUA_TFUNCTION
			Return lua_isfunction(_state, idx)
		Case LUA_TBOOLEAN
			Return lua_isboolean(_state, idx)
		Default
			Return (klass&lua_type(_state, idx))
		End Select
	End Method
	
	Method IsNumber:Int(idx%)
		Return lua_isnumber(_state, idx)
	End Method
	
	Method IsBoolean:Int(idx%)
		Return lua_isboolean(_state, idx)
	End Method
	
	Method IsString:Int(idx%)
		Return lua_isstring(_state, idx)
	End Method
	
	Method IsCFunction:Int(idx%)
		Return lua_iscfunction(_state, idx)
	End Method
	
	Method IsFunction:Int(idx%)
		Return lua_isfunction(_state, idx)
	End Method
	
	Method IsTable:Int(idx%)
		Return lua_istable(_state, idx)
	End Method
	
	Method IsUserdata:Int(idx%)
		Return lua_isuserdata(_state, idx)
	End Method
	
	Method IsLightUserdata:Int(idx%)
		Return lua_islightuserdata(_state, idx)
	End Method
	
	Method IsNil:Int(idx%)
		Return lua_isnil(_state, idx)
	End Method
	
	Method IsNone:Int(idx%)
		Return lua_isnone(_state, idx)
	End Method
	
	Method IsNoneOrNil:Int(idx%)
		Return lua_isnoneornil(_state, idx)
	End Method
	
	Method IsThread:Int(idx%)
		Return lua_isthread(_state, idx)
	End Method

	Rem
	bbdoc:	Returns the lua_State pointer.
	Endrem
	Method State@ Ptr()
		Return _state
	End Method
	
	Method Call:Lua(args:Int=0, results:Int=0, protected:Int=True)
		If Not protected Then
			lua_call(_state, args, results)
			Return Self
		EndIf
		
		Local r:Int = lua_pcall(_state, args, results, 0)
		
		Local err$
		If r <> 0 Then
			err = GetString()
			Pop 1
		EndIf
		Select r
		Case 0
		Case 1
			Throw "Runtime error encountered during lua_pcall:~n"+err
		Case 4
			Throw "Memory allocation error encountered during lua_pcall:~n"+err
		Case 5
			Throw "Error calling the error handler function during lua_pcall:~n"+err
		Default
			Throw "Error during lua_pcall:~n"+err
		End Select
		
		Return Self
	End Method
	
	Method GetField:Lua(idx:Int, name$)
		lua_getfield(_state, idx, name)
		Return Self
	End Method
	
	Method SetField:Lua(idx:Int, name$)
		lua_setfield(_state, idx, name)
		Return Self
	End Method
	
	Method GetGlobal:Lua(name$)
		lua_getfield(_state, LUA_GLOBALSINDEX, name)
		Return Self
	End Method
	
	Method SetGlobal:Lua(name$)
		lua_setfield(_state, LUA_GLOBALSINDEX, name)
		Return Self
	End Method
	
	Method GetEnvironment:Lua(idx:Int)
		lua_getfenv(_state, idx)
		Return Self
	End Method
	
	Method SetEnvironment:Lua(idx:Int)
		lua_setfenv(_state, idx)
		Return Self
	End Method
	
	Method GetMetatable:Lua(idx:Int)
		lua_getmetatable(_state, idx)
		Return Self
	End Method
	
	Method SetMetatable:Lua(idx:Int)
		lua_setmetatable(_state, idx)
		Return Self
	End Method
	
	Method Resume:Int(nargs:Int)
		Return lua_resume(_state, nargs)
	End Method
	
	Method Yield:Int(results:Int)
		Return lua_yield(_state, results)
	End Method
	
	Method Status:Int()
		Return lua_status(_state)
	End Method
	
	Method ExchangeValues:Lua(with:Lua, n:Int)
		lua_xmove(_state, with._state, n)
		Return Self
	End Method
	
	Rem
	bbdoc:	Returns the size of the stack.
	EndRem
	Method GetTop:Int()
		Return lua_gettop(_State)
	End Method
	
	Method SetTop:Lua(top:Int)
		lua_settop(_state, top)
		Return Self
	End Method
	
	Method CheckStack:Int(extra:Int)
		Return lua_checkstack(_state, extra)
	End Method
End Type

Comments

N2005
Added documentation.


Diordna2006
For the life of me I cannot figure out what the parameters mean in Lua.Do(). URL explains itself, but why can't the function get the stream size itself? And what in the world should I put in name$?


Rook Zimbabwe2006
Since Noel is banned at the moment your answer may be slow in coming... that is a honkin nice bit o' coding though!


N2008
I've updated this to work with the latest Axe.Lua.


N2010
This has been updated again to provide wrappers around most of the Lua API. There might be bits and pieces missing, depending on how useful certain things are (e.g., lua_gc isn't wrapped because I rarely see a need to change the way the GC works). At any rate, this version is pretty much completely incompatible with the last version. Method names have changed, bits of the last version are gone, the Do(...) methods are gone (in favor of chaining Load and Call together).

Inheriting from this would probably be painful, since you would have to override all those methods return Self (and possibly the thread methods as well). I recommend just modifying it as need be, unlike the original where I'd have just subclassed it.

Documentation is iffy, although it's not really necessary since this is a fairly straight-forward wrapper around the Lua API.


Code Archives Forum