Code archives/Miscellaneous/Simple OO Lua Interface

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

Download source code

Simple OO Lua Interface by Nilium
(Posted 21 years ago)
First off, this is a module, it goes in Cower.LuaInterface (obviously it's not required, you can place it anywhere you like). If you don't know how to add modules, then this is likely not something that will help you.

This is a simple object oriented interface to Lua to simplify useage. The idea is to derive a class from the base that implements data types and 'features' specific to your application.

An example of using the base itself without any extension(s):
Strict

Local l:Lua = Lua.Create( )

Function lua_Print( state@ Ptr )
	Local l:BaseLuaInterface = BaseLuaInterface.FromState( state )
	Local s:Int = l.Size( )
	Local i:Int
	For i = 1 To s
		Select l.GetType( i )
			Case LUA_TNUMBER
				Print l.GetNumber( i )
			Case LUA_TBOOLEAN
				Print l.GetBool( i )
			Case LUA_TSTRING
				Print l.GetString( i )
		End Select
	Next
	Return 0
End Function

l.RegisterFunction( "lPrint", lua_Print )
l.SetGlobalString( "aGlobal", "the value of a global variable" )
l.DoString( "lPrint( ~qPie~q, 5.4321, true, aGlobal )" )

l = Null

Input( "" )


An example of extending it...
Type CColor
	Field Red!
	Field Green!
	Field Blue!
	Field Alpha!
End Type

Type GLua Extends Lua
	Method PushColor( col:CColor )
		Validate( )
		CreateTable( )
		PushString( "red" )
		PushNumber( col.Red )
		SetTable( )
		PushString( "green" )
		PushNumber( col.Green )
		SetTable( )
		PushString( "blue" )
		PushNumber( col.Blue )
		SetTable( )
		PushString( "alpha" )
		PushNumber( col.Alpha )
		SetTable( )
	End Method
	
	Method GetColor:CColor( idx% = -1 )
		Validate( )
		If ( idx < 0 ) idx :+ Size( )
		Local c:CColor = New CColor
		
		PushString( "red" )
		GetTable( idx )
		PushString( "Green" )
		GetTable( idx )
		PushString( "Blue" )
		GetTable( idx )
		PushString( "Alpha" )
		GetTable( idx )
		
		c.Alpha = PopNumber( )
		c.Blue = PopNumber( )
		c.Green = PopNumber( )
		c.Red = PopNumber( )
		
		Return c
	End Method
End Method
Rem
    I do not support this code and will most likely not help you if you ask any
    questions, so I'd advise trying to help yourself.
EndRem

SuperStrict

Import Axe.Lua

Private

Function _lsb@ Ptr( s@ Ptr, d@ Ptr, sz% Ptr )
    Local i:Int Ptr = Int Ptr(d)
    sz[0] = Min(i[0],32)
    i[0] :- sz[0]
    Local p@ Ptr = Byte Ptr(i[1])
    i[1] :+ sz[0]
    Return p
End Function

Function _ls_alloc@ Ptr( ud@ Ptr, p@ Ptr, osize%, nsize% )
    If p Then
        If nsize = 0 Then
            MemFree( p )
        Else
            p = MemExtend( p, osize, nsize )
        EndIf
    Else
        If nsize = 0 Then
            Return Null
        Else
            Return MemAlloc( nsize )
        EndIf
    EndIf
End Function

Public

Rem
bbdoc: A base class for interfacing with Lua in an object-oriented manner.
EndRem
Type Lua
    Field __buffersLoaded% = 0      '' Used for DoString( )
    Field _disposed% = False
    Field _disposable% = False
    Field _state@ Ptr = Null

    Method New( )
    End Method

    Method Delete( )
        Dispose( True )
    End Method

    Rem
    bbdoc:  Checks whether or not the class is usable (valid).  If it isn't, it DebugLogs an exception.<br/>
    <br/>
    It's recommended that you wrap your own calls to this in a Try/Catch construct.
    EndRem
    Method Validate( )
        If ( _disposed = True ) DebugLog "Script interface is disposed" Return
        If ( _state = Null ) DebugLog "State is null" Return
    End Method

    Rem
    bbdoc:  Forces the disposal of the class and/or Lua state (depending on the settings specified on creation).
    EndRem
    Method Dispose( ignoreException% = False )
        If ( _disposed = True And ignoreException = False ) DebugLog "Script interface is already disposed" Return
        If ( _disposable = True And _state <> Null )
            lua_close( _state )
        EndIf
        _state = Null
        _disposed = True
    End Method

    Rem
    bbdoc:  Resets the class properties.
    EndRem
    Method Reset( s@ Ptr, canFree% = False )
        _disposed = False
        _state = s
        _disposable = canFree
    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.Reset( state, True/False )</pre>.
    EndRem
    Function FromState:Lua( s@ Ptr, canFree% = False )
        Local l:Lua = New Lua
        l.Reset( s, canFree )
        Return (l)
    End Function

    Rem
    bbdoc:  Creates a new Lua class and Lua state.  View @FromState for extra advice regarding implementation.
    EndRem
    Function Create:Lua( )
        Return Lua.FromState( lua_newstate( _ls_alloc, Null ), True )
    End Function

    Rem
    bbdoc:  Loads the lualib libraries.
    EndRem
    Method LoadLibraries( )
        Validate( )
        luaopen_base( _state )
        luaopen_table( _state )
        luaopen_string( _state )
        luaopen_math( _state )
        luaopen_debug( _state )
    End Method

    Rem
    bbdoc:  Tells the Lua state to execute a string.
    EndRem
    Method DoString( s$ )
        Validate( )
        Local length% = s.Length
        Local mem@ Ptr = s.ToCString( )
        Local r:Int = lua_load( _state, _lsb, [length, Int(mem)], "__sBuffer"+__buffersLoaded )
        Select ( r )
            Case 1
                DebugLog "Lua runtime error encountered during lua_load:~n"+PopString( )
            Case 2
                DebugLog "Lua file error encountered during lua_load:~n"+PopString( )
            Case 3
                DebugLog "Lua syntax error encountered during lua_load:~n"+PopString( )
        End Select
        __buffersLoaded :+ 1
        r = lua_pcall( _state, 0, 0, 0 )
        MemFree( mem )
        Select ( r )
            Case 1
                DebugLog "Lua runtime error encountered during lua_pcall"
            Case 4
                DebugLog "Lua memory allocation error encountered during lua_pcall"
            Case 5
                DebugLog "Error occurred while running the error handler function during lua_pcall"
        End Select
    End Method

    Rem
    bbdoc:  Tells the Lua state to execute a string or the contents of a stream (stream takes priority).
    EndRem
    Method Do( url:Object, length%, name$ )
        Validate( )
        Local p:String = ""
        Local s:TStream = OpenStream( url, True, False )
        If ( s = Null )
            p = String( url )
            If ( p )
                DoString( p )
                Return
            End If
        EndIf
        If name = "" Or name = Null Then
            name = url.ToString()
        EndIf
        Local mem@[length]
        s.ReadBytes( mem, length )
        s.Close( )
        s = Null
        'Local r:Int = lua_load( _state, _lsb, [length,Int(mem)], name )
        Local r:Int = lua_load( _state, _lsb, [length, Int(Varptr mem[0])], name )
        Select ( r )
            Case 1
                DebugLog "Lua runtime error encountered during lua_load:~n"+PopString( )
            Case 2
                DebugLog "Lua file error encountered during lua_load:~n"+PopString( )
            Case 3
                DebugLog "Lua syntax error encountered during lua_load:~n"+PopString( )
        End Select
        r = lua_pcall( _state, 0, 0, 0 )
        Select ( r )
            Case 1
                DebugLog "Lua runtime error encountered during lua_pcall:~n"+PopString( )
            Case 4
                DebugLog "Lua memory allocation error encountered during lua_pcall:~n"+PopString( )
            Case 5
                DebugLog "Error occurred while running the error handler function during lua_pcall:~n"+PopString( )
        End Select
    End Method
    
    Rem
    bbdoc:  Registers a callback so it's accessible to Lua scripts.
    EndRem
    Method RegisterFunction( name$, func:Int( state@ Ptr ) )
        Validate( )
        lua_pushlstring( _state, name, name.Length )
        lua_pushcclosure( _state, func, 0 )
        lua_rawset( _state, LUA_GLOBALSINDEX )
        ?Debug
        DebugLog "Registering 0x"+Hex(Int(Byte Ptr(func)))+" as "+name
        ?
    End Method

    Rem
    bbdoc:  Pops @n elements from the stack.
    EndRem
    Method Pop( n:Int = 1 )
        Validate( )
        If Size( ) = 0 Then Return
        lua_settop( _state, -(n+1) )
    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( idx% = -2 )
        Validate( )
        If Not Is(idx, LUA_TTABLE) Then
            DebugLog "Value at "+idx+" is not a table"
            Return
        EndIf
        lua_gettable( _state, idx )
    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( idx% = -3 )
        Validate( )
        If Not Is(idx, LUA_TTABLE) Then
            DebugLog "Value at "+idx+" is not a table"
            Return
        EndIf
        lua_settable( _state, idx )
    End Method

    Rem
    bbdoc:  Creates a table and pushes it onto the stack.
    EndRem
    Method CreateTable( narr%=0, nrec%=0 )
        Validate( )
        lua_createtable( _state, narr, nrec )
    End Method

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

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

    Rem
    bbdoc:  Pushes a number onto the stack.
    EndRem
    Method PushNumber( n! )
        Validate( )
        lua_pushnumber( _state, n )
    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( )
        Validate( )
        lua_pushnil( _state )
    End Method

    Rem
    bbdoc:  Pushes a pointer (light userdata) onto the stack.
    EndRem
    Method PushPtr( p@ Ptr )
        Validate( )
        lua_pushlightuserdata( _state, p )
    End Method

    ' Is( ) calls Validate, so calling Validate in these is unneccessary
    Rem
    bbdoc:  Gets a string from @idx.  This will DebugLog an exception if the value at idx is not a string.
    EndRem
    Method GetString$( idx% = -1 )
        If ( Not Is( idx, LUA_TSTRING ) )
            DebugLog "Data at "+idx+" is not a string ("+GetType(idx)+")"
            Select GetType( idx )
                Case LUA_TUSERDATA
                    DebugLog "Data at "+idx+" is a pointer: 0x"+Hex(Int(GetPtr(idx)))
                Case LUA_TBOOLEAN
                    DebugLog "Data at "+idx+" is a boolean: "+GetBool(idx)
                Case LUA_TNUMBER
                    DebugLog "Data at "+idx+" is a number: "+GetNumber(idx)
                Case LUA_TNIL
                    DebugLog "Data at "+idx+" is nil"
                Case LUA_TNONE
                    DebugLog "Data at "+idx+" is None"
                Default
            End Select
            Return Null
        EndIf
        Return String.FromCString(lua_tolstring( _state, idx, Null ))
    End Method

    Rem
    bbdoc:  Gets a boolean from @idx.  This will DebugLog an exception if the value at idx is not a boolean.
    EndRem
    Method GetBool%( idx% = -1 )
        If ( Not Is( idx, LUA_TBOOLEAN ) )
            DebugLog "Data at "+idx+" is not a bool ("+GetType(idx)+")"
            Select GetType( idx )
                Case LUA_TUSERDATA
                    DebugLog "Data at "+idx+" is a pointer: 0x"+Hex(Int(GetPtr(idx)))
                Case LUA_TSTRING
                    DebugLog "Data at "+idx+" is a string: <<<"+GetString(idx)+">>>"
                Case LUA_TNUMBER
                    DebugLog "Data at "+idx+" is a number: "+GetNumber(idx)
                Case LUA_TNIL
                    DebugLog "Data at "+idx+" is nil"
                Case LUA_TNONE
                    DebugLog "Data at "+idx+" is None"
                Default
            End Select
            Return -1
        EndIf
        Return lua_toboolean( _state, idx )
    End Method

    Rem
    bbdoc:  Gets a number from @idx.  This will DebugLog an exception if the value at idx is not a number.
    EndRem
    Method GetNumber!( idx% = -1 )
        If ( Not Is( idx, LUA_TNUMBER ) )
            DebugLog "Data at "+idx+" is not a number ("+GetType(idx)+")"
            Select GetType( idx )
                Case LUA_TSTRING
                    DebugLog "Data at "+idx+" is a string: <<<"+GetString(idx)+">>>"
                Case LUA_TBOOLEAN
                    DebugLog "Data at "+idx+" is a boolean: "+GetBool(idx)
                Case LUA_TUSERDATA
                    DebugLog "Data at "+idx+" is a pointer: 0x"+Hex(Int(GetPtr(idx)))
                Case LUA_TNIL
                    DebugLog "Data at "+idx+" is nil"
                Case LUA_TNONE
                    DebugLog "Data at "+idx+" is None"
                Default
            End Select
            Return 0
        EndIf
        Return lua_tonumber( _state, idx )
    End Method

    Rem
    bbdoc:  Gets a pointer (light userdata) from @idx.  This will DebugLog an exception if the value at idx is not a pointer.
    EndRem
    Method GetPtr@ Ptr( idx% = -1 )
        If ( Not Is( idx, LUA_TLIGHTUSERDATA ) )
            DebugLog "Data at "+idx+" is not a pointer ("+GetType(idx)+")"
            Select GetType( idx )
                Case LUA_TSTRING
                    DebugLog "Data at "+idx+" is a string: <<<"+GetString(idx)+">>>"
                Case LUA_TBOOLEAN
                    DebugLog "Data at "+idx+" is a boolean: "+GetBool(idx)
                Case LUA_TNUMBER
                    DebugLog "Data at "+idx+" is a number: "+GetNumber(idx)
                Case LUA_TNIL
                    DebugLog "Data at "+idx+" is nil"
                Case LUA_TNONE
                    DebugLog "Data at "+idx+" is None"
                Default
            End Select
            Return Null
        EndIf
        Return lua_touserdata( _state, idx )
    End Method
Rem
    Method PushMatrix( m:Matrix )
       CreateTable( )
       Local p! Ptr = Double Ptr(m.GetPtr( ))
       For Local i:Int = 0 To 15
           PushNumber( i )
           PushNumber( p[i] )
           SetTable( )
       Next
    End Method
    
    Method GetMatrix:Matrix( idx%=-1 )
        If Not Is(idx, LUA_TTABLE) Then
            Return Null
        EndIf
        Local m:Matrix = New Matrix
        Local p! Ptr = Double Ptr(m.GetPtr( ))
        For Local i:Int = 0 To 15
            PushNumber( i )
            GetTable( -3+idx )
            p[i] = PopNumber( )
        Next
        Return m
    End Method
    
    Method PopMatrix:Matrix( )
        Local m:Matrix = GetMatrix( )
        Pop( )
        Return m
    End Method
    
    Method PushVector( v:Vector3 )
        CreateTable( )
        PushString( "x" )
        PushNumber( v.x )
        SetTable( )
        PushString( "y" )
        PushNumber( v.y )
        SetTable( )
        PushString( "z" )
        PushNumber( v.z )
        SetTable( )
    End Method
    
    Method GetVector:Vector3( idx:Int = -1 )
        If Not Is(idx,LUA_TTABLE) Then
            Return Null
        EndIf
        Local v:Vector3 = New Vector3
        PushString("x")
        GetTable( idx-1 )
        v.x = PopNumber( )
        PushString("y")
        GetTable( idx-1 )
        v.y = PopNumber( )
        PushString("z")
        GetTable( idx-1 )
        v.z = PopNumber( )
        Return v
    End Method
    
    Method PopVector:Vector3( )
       Local v:Vector3 = GetVector( )
       Pop( )
       Return v
    End Method
EndRem

    Rem
    bbdoc:  Gets a string from the top of the stack and pops it from the stack.  If the value at the top of the stack is not a string an exception will be DebugLogn.
    EndRem
    Method PopString$( )
        Local r$ = GetString( )
        Pop( 1 )
        Return r
    End Method

    Rem
    bbdoc:  Gets a boolean from the top of the stack and pops it from the stack.  If the value at the top of the stack is not a boolean an exception will be DebugLogn.
    EndRem
    Method PopBool%( )
        Local r% = GetBool( )
        Pop( 1 )
        Return r
    End Method

    Rem
    bbdoc:  Gets a number from the top of the stack and pops it from the stack.  If the value at the top of the stack is not a number an exception will be DebugLogn.
    EndRem
    Method PopNumber!( )
        Local r! = GetNumber( )
        Pop( 1 )
        Return r
    End Method

    Rem
    bbdoc:  Gets a pointer (light userdata) from the top of the stack and pops it from the stack.  If the value at the top of the stack is not a pointer an exception will be DebugLogn.
    EndRem
    Method PopPtr@ Ptr( )
        Local r@ Ptr = GetPtr( )
        Pop( 1 )
        Return r
    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_TTABLE</li>
    <li>LUA_TFUNCTION</li>
    <li>LUA_TTHREAD</li>
    </ul>
    EndRem
    Method GetType%( idx% = -1 )
        Validate( )
        Return lua_type( _state, idx )
    End Method

    Rem
    bbdoc:  Returns whether or not the value at @idx is of @_type.
    EndRem
    Method Is%( idx%, _type% )
        Validate( )
        Return ( lua_type( _state, idx ) = _type )
    End Method

    Rem
    bbdoc:  Returns the lua_State pointer.
    Endrem
    Method GetState@ Ptr( )
        Validate( )
        Return _state
    End Method

    Rem
    bbdoc:  Calls a global function.<br/>
    <br/>
    You have to push all arguments onto the stack before calling the function.
    EndRem
    Method Call( name$, args%, results% )
        If name.Length Then
            PushString( name )
            GetTable( LUA_GLOBALSINDEX )
        EndIf
        If args > 0 Then lua_insert( _state, -(args+1) )
        Local r:Int = lua_pcall( _state, args, results, 0 )
        Select r
            Case 1
                DebugLog "Lua runtime error encountered during lua_pcall ("+name+"):~n"+PopString( )
            Case 4
                DebugLog "Lua memory allocation error encountered during lua_pcall ("+name+"):~n"+PopString( )
            Case 5
                DebugLog "Error occurred while running the error handler function during lua_pcall ("+name+"):~n"+PopString( )
        End Select
    End Method

    Rem
    bbdoc:  Sets a global string.
    EndRem
    Method SetGlobalString( name$, s$ )
        PushString( name )
        PushString( s )
        SetTable( LUA_GLOBALSINDEX )
    End Method

    Rem
    bbdoc:  Sets a global boolean.
    EndRem
    Method SetGlobalBool( name$, b% )
        PushString( name )
        PushBool( b )
        SetTable( LUA_GLOBALSINDEX )
    End Method

    Rem
    bbdoc:  Sets a global number.
    EndRem
    Method SetGlobalNumber( name$, n! )
        PushString( name )
        PushNumber( n )
        SetTable( LUA_GLOBALSINDEX )
    End Method

    Rem
    bbdoc:  Sets a global pointer.
    EndRem
    Method SetGlobalPtr( name$, p@ Ptr )
        PushString( name )
        PushPtr( p )
        SetTable( LUA_GLOBALSINDEX )
    End Method

    Rem
    bbdoc:  Gets a global string.
    EndRem
    Method GetGlobalString$( name$ )
        PushString( name )
        GetTable( LUA_GLOBALSINDEX )
        Return PopString( )
    End Method

    Rem
    bbdoc:  Gets a global boolean.
    EndRem
    Method GetGlobalBool%( name$ )
        PushString( name )
        GetTable( LUA_GLOBALSINDEX )
        Return PopBool( )
    End Method

    Rem
    bbdoc:  Gets a global number.
    EndRem
    Method GetGlobalNumber!( name$ )
        PushString( name )
        GetTable( LUA_GLOBALSINDEX )
        Return PopNumber( )
    End Method

    Rem
    bbdoc:  Gets a global pointer.
    EndRem
    Method GetGlobalPtr@ Ptr( name$ )
        PushString( name )
        GetTable( LUA_GLOBALSINDEX )
        Return PopPtr( )
    End Method

    Rem
    bbdoc:  Returns the size of the stack.
    EndRem
    Method Size%( )
        Validate( )
        Return lua_gettop( _State )
    End Method
End Type