PDA

View Full Version : using Delphi classes in lua :)



paul_nicholls
05-04-2006, 01:47 AM
Hi all,
I have whipped up a unit that allows me to make classes in Delphi and use them in Lua 5.x if anyone is interested :)


Unit LuaClasses;

Interface

Uses
SysUtils,
Classes,
Lua;

Type
TLuaClass = Class
Private
FMethods : TStringList;
FLibName: AnsiString;
Protected
Procedure RegisterLuaMethod(AName: AnsiString);
Function CallLuaMethod(L: lua_State; MethodIndex: Integer): Integer; Virtual;
Public
Constructor Create; Virtual;
Destructor Destroy; Override;

Procedure PushOntoLuaStack(L: lua_State);
Procedure RegisterMethodsWithLua(L: lua_State);

Property LibName: AnsiString Read FLibName Write FLibName;
End;

TLuaClassClass = Class Of TLuaClass;

TRegisteredLuaClass = Packed Record
ClassName: AnsiString;
ClassType: TLuaClassClass;
End;

TLuaClassFactory = Class(TLuaClass)
Private
FRegisteredClasses: Array Of TRegisteredLuaClass;

Function NewLuaClass(L: lua_State): Integer;
Function DestroyLuaClass(L: lua_State): Integer;
Protected
Function CallLuaMethod(L: lua_State; MethodIndex: Integer): Integer; Override;
Public
Constructor Create; Override;
Destructor Destroy; Override;

Procedure RegisterLuaClass(ClassName: AnsiString; ClassType: TLuaClassClass);
Function LuaClassExists(ClassName: AnsiString): Boolean;
Function NewLuaClassByName(ClassName: AnsiString): TLuaClass;
End;

Implementation

{................................................. ......}

{................................................. ......}
Function LuaMethodRedirector(L: lua_State): Integer; CDecl;
Var
LuaClass: TLuaClass;
Index : Integer;
Begin
LuaClass := lua_touserdata (L, lua_upvalueindex(1));
Index := Trunc(lua_tonumber(L, lua_upvalueindex(2)));

Result := LuaClass.CallLuaMethod(L,Index);
End;
{................................................. ......}

{................................................. ......}
Constructor TLuaClass.Create;
Begin
Inherited Create;

FMethods := TStringList.Create;
FLibName := '';
End;
{................................................. ......}

{................................................. ......}
Destructor TLuaClass.Destroy;
Begin
FMethods.Free;

Inherited Destroy;
End;
{................................................. ......}

{................................................. ......}
Procedure TLuaClass.RegisterLuaMethod(AName: AnsiString);
Begin
FMethods.Add(AName);
End;
{................................................. ......}

{................................................. ......}
Function TLuaClass.CallLuaMethod(L: lua_State; MethodIndex: Integer): Integer;
Begin
Result := 0;
End;
{................................................. ......}

{................................................. ......}
Procedure TLuaClass.PushOntoLuaStack(L: lua_State);
Var
Table : Integer;
MT : Integer;
i : Integer;
Begin
lua_newtable(L);

Table := lua_gettop(L);

For i := 0 To FMethods.Count - 1 Do
Begin
lua_pushstring (L,PChar(FMethods.Strings[i]));
lua_pushlightuserdata(L,Self);
lua_pushnumber(L,i);
lua_pushcclosure(L,LuaMethodRedirector,2);

lua_settable(L,Table);
End;

// create a meta table containing the object pointer
lua_newtable(L);

MT := lua_gettop(L);

lua_pushstring(L,'Self');
lua_pushlightuserdata(L,Self);
lua_settable(L,MT);

lua_setmetatable(L,Table);
End;
{................................................. ......}

{................................................. ......}
Procedure TLuaClass.RegisterMethodsWithLua(L: lua_State);
Var
i : Integer;
Table : Integer;
Begin
Table := LUA_GLOBALSINDEX;

If &#40;FLibName <> ''&#41; Then
Begin
lua_newtable&#40;L&#41;;

Table &#58;= lua_gettop&#40;L&#41;;
End;

For i &#58;= 0 To FMethods.Count - 1 Do
Begin
lua_pushstring &#40;L,PChar&#40;FMethods.Strings&#91;i&#93;&#41;&#41;;
lua_pushlightuserdata&#40;L,Self&#41;;
lua_pushnumber &#40;L,i&#41;;
lua_pushcclosure &#40;L,LuaMethodRedirector,2&#41;;

lua_settable&#40;L, Table&#41;;
End;

If &#40;FLibName <> ''&#41; Then
lua_setglobal&#40;L,PChar&#40;FLibName&#41;&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Constructor TLuaClassFactory.Create;
Begin
Inherited Create;

RegisterLuaMethod&#40;'NewLuaClass'&#41;;
RegisterLuaMethod&#40;'DestroyLuaClass'&#41;;

SetLength&#40;FRegisteredClasses,0&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Destructor TLuaClassFactory.Destroy;
Begin
SetLength&#40;FRegisteredClasses,0&#41;;

Inherited Destroy;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassFactory.CallLuaMethod&#40;L&#58; lua_State; MethodIndex&#58; Integer&#41;&#58; Integer;
Begin
Case MethodIndex Of
0 &#58; Result &#58;= NewLuaClass&#40;L&#41;;
1 &#58; Result &#58;= DestroyLuaClass&#40;L&#41;;
Else
lua_pushstring&#40;L,PChar&#40;Format&#40;
'TLuaClassFactory.CallLuaMethod&#58; MethodIndex "%d" out of bounds',
&#91;MethodIndex&#93;&#41;&#41;&#41;;
lua_error&#40;L&#41;;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure TLuaClassFactory.RegisterLuaClass&#40;ClassName&#58; AnsiString; ClassType&#58; TLuaClassClass&#41;;
Var
LuaClass&#58; TLuaClass;
Begin
If &#40;ClassName = ''&#41; Then
Exit;

If &#40;LuaClassExists&#40;ClassName&#41;&#41; Then
Raise Exception.Create&#40;'TLuaClassFactory.RegisterLuaClas s&#58; LuaClass "'+ClassName+'" is already registered'&#41;;

SetLength&#40;FRegisteredClasses,Length&#40;FRegisteredCla sses&#41; + 1&#41;;
FRegisteredClasses&#91;High&#40;FRegisteredClasses&#41;&#93;.Class Name &#58;= ClassName;
FRegisteredClasses&#91;High&#40;FRegisteredClasses&#41;&#93;.Class Type &#58;= ClassType;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassFactory.LuaClassExists&#40;ClassName&#58; AnsiString&#41;&#58; Boolean;
Var
i&#58; Integer;
Begin
Result &#58;= False;

For i &#58;= 0 To High&#40;FRegisteredClasses&#41; Do
Begin
If &#40;ClassName = FRegisteredClasses&#91;i&#93;.ClassName&#41; Then
Begin
Result &#58;= True;
Break;
End;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassFactory.NewLuaClassByName&#40;ClassName&#58; AnsiString&#41;&#58; TLuaClass;
Var
i&#58; Integer;
Begin
Result &#58;= Nil;

For i &#58;= 0 To High&#40;FRegisteredClasses&#41; Do
Begin
If &#40;ClassName = FRegisteredClasses&#91;i&#93;.ClassName&#41; Then
Begin
Result &#58;= FRegisteredClasses&#91;i&#93;.ClassType.Create;
Break;
End;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassFactory.NewLuaClass&#40;L&#58; lua_State&#41;&#58; Integer;
Var
Table &#58; Integer;
LuaClass &#58; TLuaClass;
i &#58; Integer;
ClassName &#58; AnsiString;
Begin
ClassName &#58;= luaL_checkstring&#40;L,1&#41;;

LuaClass &#58;= NewLuaClassByName&#40;ClassName&#41;;

If &#40;LuaClass = Nil&#41; Then
Begin
lua_pushstring&#40;L,PChar&#40;'NewLuaClass&#58; Unknown class name "'+ClassName+'"'&#41;&#41;;
lua_error&#40;L&#41;;
End;

LuaClass.PushOntoLuaStack&#40;L&#41;;

Result &#58;= 1;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassFactory.DestroyLuaClass&#40;L&#58; lua_State&#41;&#58; Integer;
Var
LuaRef &#58; Integer;
LuaClass&#58; TLuaClass;
Begin
luaL_checktype&#40;L,1,LUA_TTABLE&#41;;

lua_getmetatable&#40;L,1&#41;;

lua_pushstring&#40;L,'Self'&#41;;
lua_gettable&#40;L,-2&#41;;

LuaClass &#58;= lua_touserdata&#40;L,-1&#41;;

lua_pop&#40;L,1&#41;;

If &#40;Not&#40;LuaClass Is TLuaClass&#41;&#41; Then
Begin
lua_pushstring&#40;L,'LuaDestroyClass&#58; LuaClass expected'&#41;;
lua_error&#40;L&#41;;
End;

LuaClass.Free;

Result &#58;= 0;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
End.

paul_nicholls
05-04-2006, 01:49 AM
And here is some test code to show how to create and use a lua class :)

I hope people find this helpfull! :)

Test code:


Uses
SysUtils,
Lua,
LuaClasses;

Type
//
// a simple lua class counter as a test
//
TLuaClassCounter = Class&#40;TLuaClass&#41;
Private
FCount&#58; Integer;

Function IncCount&#40;L&#58; lua_State&#41;&#58; Integer;
Function GetCount&#40;L&#58; lua_State&#41;&#58; Integer;
Protected
Function CallLuaMethod&#40;L&#58; lua_State; MethodIndex&#58; Integer&#41;&#58; Integer; Override;
Public
Constructor Create; Override;
End;

Constructor TLuaClassCounter.Create;
Begin
Inherited Create;

RegisterLuaMethod&#40;'IncCount'&#41;;
RegisterLuaMethod&#40;'GetCount'&#41;;

FCount &#58;= 0;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassCounter.IncCount&#40;L&#58; lua_State&#41;&#58; Integer;
Begin
Inc&#40;FCount&#41;;

Result &#58;= 0;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassCounter.GetCount&#40;L&#58; lua_State&#41;&#58; Integer;
Begin
lua_pushnumber&#40;L,FCount&#41;;

Result &#58;= 1;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassCounter.CallLuaMethod&#40;L&#58; lua_State; MethodIndex&#58; Integer&#41;&#58; Integer;
Begin
Case MethodIndex Of
0 &#58; Result &#58;= IncCount&#40;L&#41;;
1 &#58; Result &#58;= GetCount&#40;L&#41;;
Else
lua_pushstring&#40;L,PChar&#40;Format&#40;
'TLuaClassCounter.CallLuaMethod&#58; MethodIndex "%d" out of bounds',
&#91;MethodIndex&#93;&#41;&#41;&#41;;
lua_error&#40;L&#41;;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;


// Using TLuaClass Test example

LuaClassFactory &#58; TLuaClassFactory;
LuaState &#58; lua_State;
TestScript &#58; AnsiString;
Begin
//open and initialize a lua state
&#123;...&#125;

LuaClassFactory &#58;= TLuaClassFactory.Create;
LuaClassFactory.LibName &#58;= '';

// if TLuaClass.LibName <> '' then
// the TLuaClass methods become part of a global table called .LibName
// just like a library instead of stand-alone global routines and
// LibName.<method name> needs to be used when calling them
// instead of <method name>

Try
LuaClassFactory.RegisterLuaClass&#40;'Counter',TLuaCla ssCounter&#41;;
Except
On E&#58; Exception Do
ShowMessage&#40;E.Message&#41;;
End;

LuaClassFactory.RegisterMethodsWithLua&#40;LuaState&#41;;

//run a lua script

TestScript &#58;=

'a = NewLuaClass&#40;"Counter"&#41;' +#13#10#13#10+

'for i = 1,10 do' +#13#10+
' a.IncCount&#40;&#41;' +#13#10+
'end' +#13#10#13#10+

'print&#40;a.GetCount&#40;&#41;&#41;' +#13#10#13#10+

'DestroyLuaClass&#40;a&#41;';

lua_dostring&#40;LuaState,PChar&#40;TestScript&#41;&#41;;

ReadLn;

//close the lua state
&#123;...&#125;

Robert Kosek
05-04-2006, 02:12 AM
Interesting.

Where did you get the Lua 5.x headers from? I've not figured out where to get them for Delphi. (I've got 2005 PE)

paul_nicholls
05-04-2006, 02:43 AM
Interesting.

Where did you get the Lua 5.x headers from? I've not figured out where to get them for Delphi. (I've got 2005 PE)

hmm, let me think...

I think I got the Delphi headers + Lua binaries from here:
http://www.matrix44.de/lua/

have fun! :)

cheers,
Paul.

Robert Kosek
05-04-2006, 04:21 AM
Thanks much, Paul.

Quick question since I only skimmed your code. Does this, or can this, emulate properties too? And functions with variables, not just a single function with no variables or returns?

paul_nicholls
05-04-2006, 05:17 AM
Thanks much, Paul.

Quick question since I only skimmed your code. Does this, or can this, emulate properties too? And functions with variables, not just a single function with no variables or returns?

The only way I can think to simulate properties, is to create Get/Set methods in the TLuaClass descendent and use those :-)

To your other question, your TLuaClass methods MUST take 1 input parameter of type lua_State and return a result as Integer like so:

Function MyMethodName(L: lua_State): Integer;

but they can then quite happily receive any number of Lua input parameters via the lua_State's stack, and return any number of Lua variables back onto the lua_State's stack like so:

This one takes no input parameters and returns no results

Function TLuaClassCounter.IncCount&#40;L&#58; lua_State&#41;&#58; Integer;
Begin
Inc&#40;FCount&#41;;

Result &#58;= 0;
End;

This one takes no input parameters and returns 1 result (the number)

Function TLuaClassCounter.GetCount&#40;L&#58; lua_State&#41;&#58; Integer;
Begin
lua_pushnumber&#40;L,FCount&#41;;

Result &#58;= 1;
End;

Here is an example of a TLuaClass method that takes 2 numbers, and returns the Sum and the Difference of the 2 numbers as 2 separate results

Function TLuaClassTest.SumDiff&#40;L&#58; lua_State&#41;&#58; Integer;
Var
a,b&#58; Single;
sum,diff&#58; Single;
Begin
// make sure that there are 2 inputs, and that they are numbers
a &#58;= luaL_checknumber&#40;L,1&#41;;
b &#58;= luaL_checknumber&#40;L,2&#41;;

sum &#58;= a + b;
diff &#58;= a - b;

// push both results onto the Lua stack
lua_pushnumber&#40;L,sum&#41;;
lua_pushnumber&#40;L,diff&#41;;

Result &#58;= 2;
End;

For example this may be called like so in a Lua script:


a,b = Test.SumDiff&#40;5,10&#41; -- assume it is created already

print&#40;a,b&#41;


This one calles the appropriate internal TLuaClass methods depending on the MethodIndex

Function TLuaClassCounter.CallLuaMethod&#40;L&#58; lua_State; MethodIndex&#58; Integer&#41;&#58; Integer;
Begin
Case MethodIndex Of
0 &#58; Result &#58;= IncCount&#40;L&#41;;
1 &#58; Result &#58;= GetCount&#40;L&#41;;
Else
lua_pushstring&#40;L,PChar&#40;Format&#40;
'TLuaClassCounter.CallLuaMethod&#58; MethodIndex "%d" out of bounds',
&#91;MethodIndex&#93;&#41;&#41;&#41;;
lua_error&#40;L&#41;;
End;
End;

jdarling
05-04-2006, 12:50 PM
WOW you mean I'm not the only freak using LUA in Delphi? Nice work thus far, I've been working on a way to create wrappers for units and objects for my own code. I'll have to take a look at what you've put together as it may aid along the way :).

Robert Kosek
05-04-2006, 02:56 PM
In that case you could use a function to recieve and return variables? It could be a little choppy, but I think it's doable.

jdarling
05-04-2006, 03:19 PM
If you write an __newindex handler (writer) that uses RTTI to look at the parent class and write a specialized __index handler (reader) then you can emulate properties quite well. I have this working some place, but the method callers arn't complete yet. Actually the code above gives me some ideas so I may be posting up my code for LUA some time soon too :)

Robert Kosek
05-04-2006, 04:06 PM
Cool. I think you two need to work together after the competition and combine your Lua units. ;)

paul_nicholls
05-04-2006, 10:45 PM
WOW you mean I'm not the only freak using LUA in Delphi?

LOL!


Nice work thus far, I've been working on a way to create wrappers for units and objects for my own code. I'll have to take a look at what you've put together as it may aid along the way :).

Thanks for the nice comments, and I am just glad to be able to occasionally help other people after myself getting lots of help from the net over the years :)

cheers,
Paul.

paul_nicholls
05-04-2006, 10:52 PM
If you write an __newindex handler (writer) that uses RTTI to look at the parent class and write a specialized __index handler (reader) then you can emulate properties quite well. I have this working some place, but the method callers arn't complete yet. Actually the code above gives me some ideas so I may be posting up my code for LUA some time soon too :)

I was thinking about '__index' as well, but as far as I can figure this would only help with making readable properties, not writable ones :(

How would using '__newindex' help with emulating properties? This is only called if one is adding a new key + value to a table, not for pre-existing keys...

I would be interested in how you got the '__newindex' working for this sort of thing :)

Also I have no clue as to if it is even possible to protect the existing 'class methods' created in the table from being accidently overwritten by a lua script using the table and assigning a value to that key...

Any thoughts on that one?

cheers,
Paul.

paul_nicholls
06-04-2006, 04:49 AM
I'm not sure if this will help us guys, but I found this post over on a Lua forum:

http://www.icynorth.com/forums/viewtopic.php?t=404&highlight=

looks interesting :-)

cheers,
Paul

jdarling
06-04-2006, 01:07 PM
If you write an __newindex handler (writer) that uses RTTI to look at the parent class and write a specialized __index handler (reader) then you can emulate properties quite well. I have this working some place, but the method callers arn't complete yet. Actually the code above gives me some ideas so I may be posting up my code for LUA some time soon too :)

I was thinking about '__index' as well, but as far as I can figure this would only help with making readable properties, not writable ones :(

How would using '__newindex' help with emulating properties? This is only called if one is adding a new key + value to a table, not for pre-existing keys...

I would be interested in how you got the '__newindex' working for this sort of thing :)

Also I have no clue as to if it is even possible to protect the existing 'class methods' created in the table from being accidently overwritten by a lua script using the table and assigning a value to that key...

Any thoughts on that one?

cheers,
Paul.

Just as soon as I have the code completed and cleaned up I'll post it up for everyone to use. The basic idea is this:

Create your "Class" using the methods provided in Luna thus hiding the metatable and object level manager from the Lua script in general. Overide the __index and __newindex (witch fires when an unknown is set) and make sure that ALL properties are removed from the meta table. Any time a person makes a request to the object using dot notation either __index (read) or __newindex (write) will be fired, using RTTI scan the object and find out if the property exists, if so then return/set the value otherwise throw an error or pass it back up to Lua as a new table entitity using lua_settable(L, TableIndex) lua_pushstring(L, [PropName]) lua_pushvalue(L, val). Thus the next time its accessed it won't fire __index or __newindex, since you have caught the ones that do fire these "events" (and I use the term loosly) your code will still execute. Now before you ask what if the person overrides __index or __newindex remember that we hid the MetaTable and ObjectTable from the user, they can still get at it using upval() but thats another topic for another day. You simply have to let your users know not to screw with the __index and __newindex methods attached to your objects :)

paul_nicholls
06-04-2006, 11:29 PM
If you write an __newindex handler (writer) that uses RTTI to look at the parent class and write a specialized __index handler (reader) then you can emulate properties quite well. I have this working some place, but the method callers arn't complete yet. Actually the code above gives me some ideas so I may be posting up my code for LUA some time soon too :)

I was thinking about '__index' as well, but as far as I can figure this would only help with making readable properties, not writable ones :(

How would using '__newindex' help with emulating properties? This is only called if one is adding a new key + value to a table, not for pre-existing keys...

I would be interested in how you got the '__newindex' working for this sort of thing :)

Also I have no clue as to if it is even possible to protect the existing 'class methods' created in the table from being accidently overwritten by a lua script using the table and assigning a value to that key...

Any thoughts on that one?

cheers,
Paul.

Just as soon as I have the code completed and cleaned up I'll post it up for everyone to use. The basic idea is this:

Create your "Class" using the methods provided in Luna thus hiding the metatable and object level manager from the Lua script in general. Overide the __index and __newindex (witch fires when an unknown is set) and make sure that ALL properties are removed from the meta table. Any time a person makes a request to the object using dot notation either __index (read) or __newindex (write) will be fired, using RTTI scan the object and find out if the property exists, if so then return/set the value otherwise throw an error or pass it back up to Lua as a new table entitity using lua_settable(L, TableIndex) lua_pushstring(L, [PropName]) lua_pushvalue(L, val). Thus the next time its accessed it won't fire __index or __newindex, since you have caught the ones that do fire these "events" (and I use the term loosly) your code will still execute. Now before you ask what if the person overrides __index or __newindex remember that we hid the MetaTable and ObjectTable from the user, they can still get at it using upval() but thats another topic for another day. You simply have to let your users know not to screw with the __index and __newindex methods attached to your objects :)

Ahh! that makes sense, with the starting with no properties! cool :)
I'll give it a go myself in the meantime to try it :)

cheers,
Paul.

jdarling
07-04-2006, 02:34 AM
Ok, this is going to be ugly as I haven't posted the TLUA component yet, but I think you can figure it out w/o it as basically all you have to do is replace its reference with a Plua_State :)

This code is my reference code for my Delphi->Lua application that is comming around:
unit TestLuaClass;

interface

uses
Classes, LuaWrapper, Lua, LuaUtils, Lauxlib, LuaLib;

type
TTestClass=class(TPersistent)
private
FBalance: Double;
procedure SetBalance(const Value: Double);
public
class procedure RegisterTo(LuaScript:TLUA);
published
procedure Deposit(Amount:Double);
procedure Withdraw(Amount:Double);
property Balance : Double read FBalance write SetBalance;
end;

implementation

uses
SysUtils,
TypInfo;

const
LuaClassName = 'TestClass';

function Check(L: Plua_State; Idx : Integer) : TTestClass;
begin
result := nil;
if lua_type(L, Idx) = LUA_TTABLE then
result := LuaGetTableLightUserData(L, Idx, '_Self')
else
luaL_typerror(L, Idx, LuaClassName);
end;

function new_TestClass(L: Plua_State): Integer; cdecl;
var
c : TTestClass;
begin
if lua_type(L, 1) <> LUA_TTABLE then
lua_remove(L, 1);
c := TTestClass.Create;
LuaSetTableLightUserData(L, 1, '_Self', c);
luaL_getmetatable(L, LuaClassName);
lua_setmetatable(L, -2);
result := 1;
end;

function gc_TestClass(L: Plua_State): Integer; cdecl;
var
c : TTestClass;
begin
c := Check(L, 1);
c.Free;
result := 0;
end;

function indexTestClass(L: Plua_State): Integer; cdecl;
var
c : TTestClass;
propName : String;
v : Variant;
begin
c := Check(L, 1);
lua_remove(L, 1);
propName := LuaToString(L, 1);
if IsPublishedProp(c, propName) then
begin
v := GetPropValue(c, propName);
LuaPushVariant(L, v);
end
else
luaL_error(L, PChar('Property "'+propName+'" does not exist'));
result := 1;
end;

function newindexTestClass(L:PLua_State): Integer; cdecl;
var
c : TTestClass;
propName : String;
v : Variant;
begin
c := Check(L, 1);
lua_remove(L, 1);
propName := LuaToString(L, 1);
v := LuaToVariant(L, 2);
if IsPublishedProp(c, propName) then
SetPropValue(c, propName, v)
else
luaL_error(L, PChar('Property "'+propName+'" does not exist'));
result := 0;
end;

function luaDeposit(L: Plua_State): Integer; cdecl;
var
c : TTestClass;
begin
c := Check(L, 1);
lua_remove(L, 1);
if lua_type(L, 1)<>LUA_TNUMBER then
luaL_error(L, 'Deposit expects one parameter (Amount) that should be an integer.')
else
c.Deposit(lua_tonumber(L, 1));
result := 0;
end;

function luaWithdraw(L: Plua_State): Integer; cdecl;
var
c : TTestClass;
begin
c := Check(L, 1);
lua_remove(L, 1);
if lua_type(L, 1)<>LUA_TNUMBER then
luaL_error(L, 'Withdraw expects one parameter (Amount) that should be an integer.')
else
c.Withdraw(lua_tonumber(L, 1));
result := 0;
end;

{ TTestClass }

procedure TTestClass.Deposit(Amount: Double);
begin
FBalance := FBalance + Amount;
end;

class procedure TTestClass.RegisterTo(LuaScript: TLUA);
var
MetaTable,
MethodTable,
Methods : Integer;
begin
lua_newtable(LuaScript.LuaState);
Methods := lua_gettop(LuaScript.LuaState);

luaL_newmetatable(LuaScript.LuaState, LuaClassName);
MetaTable := lua_gettop(LuaScript.LuaState);

lua_pushstring(LuaScript.LuaState, LuaClassName);
lua_pushvalue(LuaScript.LuaState, Methods);
lua_settable(LuaScript.LuaState, LUA_GLOBALSINDEX);

lua_pushliteral(LuaScript.LuaState, '__metatable');
lua_pushvalue(LuaScript.LuaState, Methods);
lua_settable(LuaScript.LuaState, metatable);

lua_pushliteral(LuaScript.LuaState, '__index');
lua_pushcfunction(LuaScript.LuaState, indexTestClass);
lua_settable(LuaScript.LuaState, MetaTable);

lua_pushliteral(LuaScript.LuaState, '__newindex');
lua_pushcfunction(LuaScript.LuaState, newindexTestClass);
lua_settable(LuaScript.LuaState, MetaTable);

lua_pushliteral(LuaScript.LuaState, '__gc');
lua_pushcfunction(LuaScript.LuaState, gc_TestClass);
lua_settable(LuaScript.LuaState, MetaTable);

lua_newtable(LuaScript.LuaState);
MethodTable := lua_gettop(LuaScript.LuaState);
lua_pushliteral(LuaScript.LuaState, '__call');
lua_pushcfunction(LuaScript.LuaState, new_TestClass);
lua_pushliteral(LuaScript.LuaState, 'new');
lua_pushvalue(LuaScript.LuaState, -2);
lua_settable(LuaScript.LuaState, Methods);
lua_settable(LuaScript.LuaState, MethodTable);
lua_setmetatable(LuaScript.LuaState, Methods);

//Test Method
lua_pushstring(LuaScript.LuaState, 'Deposit');
lua_pushcfunction(LuaScript.LuaState, luaDeposit);
lua_settable(LuaScript.LuaState, Methods);

lua_pushstring(LuaScript.LuaState, 'Withdraw');
lua_pushcfunction(LuaScript.LuaState, luaWithdraw);
lua_settable(LuaScript.LuaState, Methods);
//End of Test Method}

lua_pop(LuaScript.LuaState, 2);
end;

procedure TTestClass.SetBalance(const Value: Double);
begin
FBalance := Value;
end;

procedure TTestClass.Withdraw(Amount: Double);
begin
FBalance := FBalance - Amount;
end;

end.

paul_nicholls
07-04-2006, 03:29 AM
Awsome work chief! :)

:wink: That TTestClass.RegisterTo() method is almost scary! hehe

Paul.

paul_nicholls
07-04-2006, 03:33 AM
Actually, I was thinking. Instead of subclassing from TPersistent, you could possible do something like so instead for more flexability with base class types:


&#123;$M+&#125;
TTestClass=class
private
FBalance&#58; Double;
procedure SetBalance&#40;const Value&#58; Double&#41;;
public
class procedure RegisterTo&#40;LuaScript&#58;TLUA&#41;;
published
procedure Deposit&#40;Amount&#58;Double&#41;;
procedure Withdraw&#40;Amount&#58;Double&#41;;
property Balance &#58; Double read FBalance write SetBalance;
end;
&#123;$M-&#125;


I believe the ($M+} will also give you RTTI info as well :)

Paul.

jdarling
07-04-2006, 01:24 PM
Actually, I was thinking. Instead of subclassing from TPersistent, you could possible do something like so instead for more flexability with base class types:


&#123;$M+&#125;
TTestClass=class
private
FBalance&#58; Double;
procedure SetBalance&#40;const Value&#58; Double&#41;;
public
class procedure RegisterTo&#40;LuaScript&#58;TLUA&#41;;
published
procedure Deposit&#40;Amount&#58;Double&#41;;
procedure Withdraw&#40;Amount&#58;Double&#41;;
property Balance &#58; Double read FBalance write SetBalance;
end;
&#123;$M-&#125;


I believe the ($M+} will also give you RTTI info as well :)

Paul.

Well actually when complete the importer will just be ran on your source and will create a wrapper unit for it. The problem was I needed an object that was quick and easy to test on that exhibited everything I wanted to wrap up. The only one I haven't figured out yet is properties with array indexes. BTW: If you want to use object assignment for properties then you have to do a GetPropType(Instance, PropName) = tkClass (thats off the top of my head so its probiably wrong) and then get the table's reference to _Self. Other then that its pretty straight forward :). Hopefully I get everything completed some time soon.

paul_nicholls
13-04-2006, 02:17 AM
For you info, using jdarling's idea of an empty table + __index and __newindex I have updated my TLuaClass to check/protect methods + properties :)

example:

class declaration


TLuaClassCounter = Class&#40;TLuaClass&#41;
Private
FCount&#58; Integer;

Function IncCount&#40;L&#58; lua_State&#41;&#58; Integer;
Function GetCount&#40;L&#58; lua_State&#41;&#58; Integer;
Protected
Function CallLuaMethod&#40;L&#58; lua_State; MethodIndex&#58; Integer&#41;&#58; Integer; Override;
Procedure ReadPropertyValue&#40;L&#58; lua_State; PropertyIndex&#58; Integer&#41;; Override;
Procedure WritePropertyValue&#40;L&#58; lua_State; PropertyIndex&#58; Integer&#41;; Override;
Public
Constructor Create; Override;
End;

class body

Constructor TLuaClassCounter.Create;
Begin
Inherited Create;

RegisterLuaMethod &#40;'IncCount',0&#41;;
RegisterLuaMethod &#40;'GetCount',1&#41;;
RegisterLuaProperty&#40;'Count' ,0,eIntegerProperty,False&#41;;
RegisterLuaProperty&#40;'Count2x' ,1,eIntegerProperty,True&#41;;
RegisterLuaProperty&#40;'Self' ,2,eLuaClassProperty,True&#41;;

FCount &#58;= 0;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassCounter.IncCount&#40;L&#58; lua_State&#41;&#58; Integer;
Begin
Inc&#40;FCount&#41;;

Result &#58;= 0;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassCounter.GetCount&#40;L&#58; lua_State&#41;&#58; Integer;
Begin
lua_pushnumber&#40;L,FCount&#41;;

Result &#58;= 1;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassCounter.CallLuaMethod&#40;L&#58; lua_State; MethodIndex&#58; Integer&#41;&#58; Integer;
Begin
Result &#58;= 0;

Case MethodIndex Of
0 &#58; Result &#58;= IncCount&#40;L&#41;;
1 &#58; Result &#58;= GetCount&#40;L&#41;;
Else
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure TLuaClassCounter.ReadPropertyValue&#40;L&#58; lua_State; PropertyIndex&#58; Integer&#41;;
Begin
Case PropertyIndex Of
0 &#58; lua_pushnumber&#40;L,FCount&#41;;
1 &#58; lua_pushnumber&#40;L,FCount * 2&#41;;
2 &#58; PushOntoLuaStack&#40;L&#41;;
Else
End;

Result &#58;= 1;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure TLuaClassCounter.WritePropertyValue&#40;L&#58; lua_State; PropertyIndex&#58; Integer&#41;;
Var
Value&#58; Double;
Begin
Case PropertyIndex Of
0 &#58; FCount &#58;= Trunc&#40;lua_tonumber&#40;L,3&#41;&#41;;
Else
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;

Behind the scenes, no properties or methods can be overwritten in a Lua script, and read-only properties can't be written to in Lua script :)
Also, you can't assign the wrong type to writable properties and it will tell you :)

BTW, is there anywhere on this site that one can post largeish code/units to share with people?

cheers,
Paul

Robert Kosek
13-04-2006, 02:31 AM
You can post the unit here, I would offer some of my own webspace but just learned I need to clean up ... I got hacked apparantly.

paul_nicholls
13-04-2006, 04:29 AM
You can post the unit here, I would offer some of my own webspace but just learned I need to clean up ... I got hacked apparantly.

Sorry to hear you got hacked! :(

Ok, here is my code so far. BTW, the last code chunk is a snippit only as the whole unit is 1272 lines long! So I don't think I should really post it here?

The TLuaDataStore allows lua objects to be stored/retrieved by name across lua scripts as references are used.

TheTLuaDataStoreManager allows TLuaDataStore objects to be created and retrieved by name to allow for multiple stores :)

Here is an axample script


-- create the LuaClass
a = NewLuaClass&#40;"Counter"&#41;

a.Count = 4 + 5

a.IncCount&#40;&#41;

-- retrieve a DataStore called "Vault" &#40;created if necessary&#41;
b = DataStore&#40;"Vault"&#41;

-- store a in it under some name
b.StoreByName&#40;"SomeLuaThing",a&#41;

-- retrieve 'a' by name
c = b.RetrieveByName&#40;"SomeLuaThing"&#41;

print&#40;c.Count&#41;
print&#40;c.Count2x&#41;

DestroyLuaClass&#40;a&#41;

LuaClasses unit


Unit LuaClasses;

Interface

Uses
SysUtils,
Classes,
Lua,
LuaObjects;

Type
TLuaPropertyType =
&#40;
eUnknownProperty,
eIntegerProperty,
eNumberProperty,
eStringProperty,
eBooleanProperty,
eLuaClassProperty&#41;;

PLuaPropertyInfo = ^TLuaPropertyInfo;
TLuaPropertyInfo = Packed Record
Index &#58; Integer;
PropType &#58; TLuaPropertyType;
IsReadOnly&#58; Boolean;
End;

TLuaClass = Class
Private
FMethods &#58; TStringList;
FProperties &#58; TStringList;
FLibName&#58; AnsiString;
Protected
Function KeyIsReadonly&#40;Key&#58; AnsiString&#41;&#58; Boolean;
Function KeyIsProperty&#40;Key&#58; AnsiString&#41;&#58; Boolean;
Function KeyIsMethod &#40;Key&#58; AnsiString&#41;&#58; Boolean;
Function KeyType &#40;Key&#58; AnsiString&#41;&#58; TLuaPropertyType;
Procedure ReadPropertyValue&#40;L&#58; lua_State; PropertyIndex&#58; Integer&#41;; Virtual;
Procedure WritePropertyValue&#40;L&#58; lua_State; PropertyIndex&#58; Integer&#41;; Virtual;
Procedure PushMethodsOntoLuaStack&#40;L&#58; lua_State; Table&#58; Integer&#41;;
Procedure RegisterLuaMethod&#40;AName&#58; AnsiString; MethodIndex&#58; Integer&#41;;
Procedure RegisterLuaProperty&#40;AName&#58; AnsiString;
Index&#58; Integer;
PropType&#58; TLuaPropertyType;
IsReadOnly&#58; Boolean&#41;;
Procedure PushMethodOntoLuaStack&#40;L&#58; lua_State; Name&#58; AnsiString; Index&#58; Integer&#41;;
Function ReadKeyValue&#40;L&#58; lua_State; Key&#58; AnsiString&#41;&#58; Integer; Virtual;
Procedure WriteKeyValue&#40;L&#58; lua_State; Key&#58; AnsiString&#41;; Virtual;
Function CallLuaMethod&#40;L&#58; lua_State; MethodIndex&#58; Integer&#41;&#58; Integer; Virtual;
Public
Constructor Create; Virtual;
Destructor Destroy; Override;

Procedure PushOntoLuaStack&#40;L&#58; lua_State&#41;;
Procedure RegisterWithLua&#40;L&#58; lua_State&#41;;

Property LibName&#58; AnsiString Read FLibName Write FLibName;
End;

TLuaClassClass = Class Of TLuaClass;

TRegisteredLuaClass = Packed Record
ClassName&#58; AnsiString;
ClassType&#58; TLuaClassClass;
End;

TLuaClassFactory = Class&#40;TLuaClass&#41;
Private
FRegisteredClasses&#58; Array Of TRegisteredLuaClass;

Function NewLuaClass&#40;L&#58; lua_State&#41;&#58; Integer;
Function DestroyLuaClass&#40;L&#58; lua_State&#41;&#58; Integer;
Protected
Function CallLuaMethod&#40;L&#58; lua_State; MethodIndex&#58; Integer&#41;&#58; Integer; Override;
Public
Constructor Create; Override;
Destructor Destroy; Override;

Procedure RegisterLuaClass&#40;ClassName&#58; AnsiString; ClassType&#58; TLuaClassClass&#41;;
Function LuaClassExists&#40;ClassName&#58; AnsiString&#41;&#58; Boolean;
Function NewLuaClassByName&#40;ClassName&#58; AnsiString&#41;&#58; TLuaClass;
End;

TLuaDataStore = Class&#40;TLuaClass&#41;
Private
FDataStoreRefs&#58; TStringList;

Function StoreByName&#40;L&#58; lua_State&#41;&#58; Integer;
Function RetrieveByName&#40;L&#58; lua_State&#41;&#58; Integer;
Protected
Function CallLuaMethod&#40;L&#58; lua_State; MethodIndex&#58; Integer&#41;&#58; Integer; Override;
Public
Constructor Create; Override;
Destructor Destroy; Override;
End;

TLuaDataStoreManager = Class&#40;TLuaClass&#41;
Private
FDataStores&#58; TStringList;

Function DataStore&#40;L&#58; lua_State&#41;&#58; Integer;
Function GetDataStoreByKey&#40;Key&#58; AnsiString&#41;&#58; TLuaDataStore;
Protected
Function CallLuaMethod&#40;L&#58; lua_State; MethodIndex&#58; Integer&#41;&#58; Integer; Override;
Public
Constructor Create; Override;
Destructor Destroy; Override;
End;

Implementation

&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function LuaClassCheck&#40;L&#58; lua_State; Idx&#58; Integer&#41;&#58; TLuaClass;
Begin
Result &#58;= Nil;

If &#40;Not lua_isuserdata&#40;L,Idx&#41;&#41; Then
Begin
LuaDoError&#40;L,'LuaClass expected'&#41;;
Exit;
End;

Result &#58;= lua_touserdata&#40;L,Idx&#41;;

If &#40;Not&#40;Result Is TLuaClass&#41;&#41; Then
Begin
LuaDoError&#40;L,'LuaClass expected'&#41;;
Exit;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function LuaClassRedirector&#40;L&#58; lua_State&#41;&#58; Integer; CDecl;
Var
LuaClass&#58; TLuaClass;
Index &#58; Integer;
Begin
LuaClass &#58;= lua_touserdata &#40;L, lua_upvalueindex&#40;1&#41;&#41;;
Index &#58;= Trunc&#40;lua_tonumber&#40;L, lua_upvalueindex&#40;2&#41;&#41;&#41;;

Result &#58;= LuaClass.CallLuaMethod&#40;L,Index&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function LuaClassIndex&#40;L&#58; lua_State&#41;&#58; Integer; CDecl;
Var
LuaClass&#58; TLuaClass;
Key &#58; AnsiString;
Begin
LuaClass &#58;= LuaClassCheck&#40;L,lua_upvalueindex&#40;1&#41;&#41;;

Key &#58;= luaL_checkstring&#40;L,2&#41;;

If &#40;Not LuaClass.KeyIsProperty&#40;Key&#41;&#41; And &#40;Not LuaClass.KeyIsMethod&#40;Key&#41;&#41; Then
Begin
LuaDoError&#40;L,LuaClass.ClassName + '&#58; Unknown property/method "'+Key+'"'&#41;;
Exit;
End;

Result &#58;= LuaClass.ReadKeyValue&#40;L,Key&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function LuaClassNewIndex&#40;L&#58; lua_State&#41;&#58; Integer; CDecl;
Var
LuaClass &#58; TLuaClass;
Key &#58; AnsiString;
KeyType &#58; TLuaPropertyType;
ValueType &#58; Integer;
Begin
Result &#58;= 0;

LuaClass &#58;= LuaClassCheck&#40;L, lua_upvalueindex&#40;1&#41;&#41;;

Key &#58;= luaL_checkstring&#40;L,2&#41;;

If &#40;LuaClass.KeyIsMethod&#40;Key&#41;&#41; Then
Begin
LuaDoError&#40;L,LuaClass.ClassName + '&#58; Can''t assign values to method "'+Key+'"'&#41;;
Exit;
End
Else
If &#40;LuaClass.KeyIsProperty&#40;Key&#41;&#41; Then
Begin
If &#40;LuaClass.KeyIsReadOnly&#40;Key&#41;&#41; Then
Begin
LuaDoError&#40;L,LuaClass.ClassName + '&#58; Can''t assign values to read-only property "'+Key+'"'&#41;;
Exit;
End
Else
Begin
KeyType &#58;= LuaClass.KeyType&#40;Key&#41;;
ValueType &#58;= lua_type&#40;L,3&#41;;

If &#40;KeyType = eUnknownProperty&#41; Then
Begin
LuaDoError&#40;L,LuaClass.ClassName + '&#58; Can''t write to unknown property "'+Key+'"'&#41;;
Exit;
End
Else
Begin
If &#40;KeyType = eIntegerProperty&#41; And &#40;Not LuaIsInteger&#40;L,3,LuaClass.ClassName + '.' + Key&#41;&#41; Then
Begin
Result &#58;= 0;
Exit;
End
Else
If &#40;KeyType = eNumberProperty&#41; And &#40;Not LuaIsNumber&#40;L,3,LuaClass.ClassName + '.' + Key&#41;&#41; Then
Begin
Result &#58;= 0;
Exit;
End
Else
If &#40;KeyType = eStringProperty&#41; And &#40;Not LuaIsString&#40;L,3,LuaClass.ClassName + '.' + Key&#41;&#41; Then
Begin
Result &#58;= 0;
Exit;
End
Else
If &#40;KeyType = eBooleanProperty&#41; And &#40;Not LuaIsBoolean&#40;L,3,LuaClass.ClassName + '.' + Key&#41;&#41; Then
Begin
Result &#58;= 0;
Exit;
End
End;
End;
End
Else
Begin
LuaDoError&#40;L,LuaClass.ClassName + '&#58; Unknown property/method "'+Key+'"'&#41;;
Exit;
End;

LuaClass.WriteKeyValue&#40;L,Key&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Constructor TLuaClass.Create;
Begin
Inherited Create;

FMethods &#58;= TStringList.Create;
FMethods.Sorted &#58;= True;
FMethods.Duplicates &#58;= dupError;
FProperties &#58;= TStringList.Create;
FProperties.Sorted &#58;= True;
FProperties.Duplicates &#58;= dupError;
FLibName &#58;= '';
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Destructor TLuaClass.Destroy;
Var
i &#58; Integer;
PropertyInfo&#58; PLuaPropertyInfo;
Begin
For i &#58;= 0 To FProperties.Count - 1 Do
Begin
PropertyInfo &#58;= PLuaPropertyInfo&#40;FProperties.Objects&#91;i&#93;&#41;;

Dispose&#40;PropertyInfo&#41;;
End;

FProperties.Free;
FMethods.Free;

Inherited Destroy;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure TLuaClass.RegisterLuaMethod&#40;AName&#58; AnsiString; MethodIndex&#58; Integer&#41;;
Begin
FMethods.AddObject&#40;AName,Pointer&#40;MethodIndex&#41;&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure TLuaClass.RegisterLuaProperty&#40;AName&#58; AnsiString;
Index&#58; Integer;
PropType&#58; TLuaPropertyType;
IsReadOnly&#58; Boolean&#41;;
Var
PropertyInfo&#58; PLuaPropertyInfo;
Begin
New&#40;PropertyInfo&#41;;

PropertyInfo.Index &#58;= Index;
PropertyInfo.PropType &#58;= PropType;
PropertyInfo.IsReadOnly &#58;= IsReadOnly;

FProperties.AddObject&#40;AName,TObject&#40;PropertyInfo&#41;&#41; ;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClass.CallLuaMethod&#40;L&#58; lua_State; MethodIndex&#58; Integer&#41;&#58; Integer;
Begin
Result &#58;= 0;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClass.KeyIsReadonly&#40;Key&#58; AnsiString&#41;&#58; Boolean;
Var
Index &#58; Integer;
PropertyInfo&#58; PLuaPropertyInfo;
Begin
Result &#58;= False;

If &#40;FMethods.Find&#40;Key,Index&#41;&#41; Then
Begin
Result &#58;= True;
End
Else
If &#40;FProperties.Find&#40;Key,Index&#41;&#41; Then
Begin
PropertyInfo &#58;= PLuaPropertyInfo&#40;FProperties.Objects&#91;Index&#93;&#41;;

Result &#58;= PropertyInfo.IsReadOnly;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClass.KeyIsProperty&#40;Key&#58; AnsiString&#41;&#58; Boolean;
Var
Index&#58; Integer;
Begin
Result &#58;= FProperties.Find&#40;Key,Index&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClass.KeyIsMethod &#40;Key&#58; AnsiString&#41;&#58; Boolean;
Var
Index&#58; Integer;
Begin
Result &#58;= FMethods.Find&#40;Key,Index&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClass.KeyType &#40;Key&#58; AnsiString&#41;&#58; TLuaPropertyType;
Var
Index &#58; Integer;
PropInfo&#58; PLuaPropertyInfo;
Begin
Result &#58;= eUnknownProperty;

If &#40;FProperties.Find&#40;Key,Index&#41;&#41; Then
Begin
PropInfo &#58;= PLuaPropertyInfo&#40;FProperties.Objects&#91;Index&#93;&#41;;
Result &#58;= PropInfo.PropType;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure TLuaClass.ReadPropertyValue&#40;L&#58; lua_State; PropertyIndex&#58; Integer&#41;;
Begin
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure TLuaClass.WritePropertyValue&#40;L&#58; lua_State; PropertyIndex&#58; Integer&#41;;
Begin
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClass.ReadKeyValue&#40;L&#58; lua_State; Key&#58; AnsiString&#41;&#58; Integer;
Var
Index &#58; Integer;
PropInfo&#58; PLuaPropertyInfo;
Begin
If &#40;KeyIsMethod&#40;Key&#41;&#41; Then
Begin
FMethods.Find&#40;Key,Index&#41;;

PushMethodOntoLuaStack&#40;L,Key,Integer&#40;FMethods.Obje cts&#91;Index&#93;&#41;&#41;;
End
Else
Begin
PropInfo &#58;= PLuaPropertyInfo&#40;FProperties.Objects&#91;Index&#93;&#41;;

ReadPropertyValue&#40;L,PropInfo.Index&#41;;
End;

Result &#58;= 1;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure TLuaClass.WriteKeyValue&#40;L&#58; lua_State; Key&#58; AnsiString&#41;;
Var
Index &#58; Integer;
PropInfo&#58; PLuaPropertyInfo;
Begin
FProperties.Find&#40;Key,Index&#41;;

PropInfo &#58;= PLuaPropertyInfo&#40;FProperties.Objects&#91;Index&#93;&#41;;

WritePropertyValue&#40;L,PropInfo.Index&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure TLuaClass.PushMethodOntoLuaStack&#40;L&#58; lua_State; Name&#58; AnsiString; Index&#58; Integer&#41;;
Begin
lua_pushstring &#40;L,PChar&#40;Name&#41;&#41;;
lua_pushlightuserdata&#40;L,Self&#41;;
lua_pushnumber&#40;L,Index&#41;;
lua_pushcclosure&#40;L,LuaClassRedirector,2&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure TLuaClass.PushMethodsOntoLuaStack&#40;L&#58; lua_State; Table&#58; Integer&#41;;
Var
i&#58; Integer;
Begin
// push methods onto the stack
For i &#58;= 0 To FMethods.Count - 1 Do
Begin
PushMethodOntoLuaStack&#40;L,FMethods.Strings&#91;i&#93;,Integ er&#40;FMethods.Objects&#91;i&#93;&#41;&#41;;

lua_settable&#40;L,Table&#41;;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure TLuaClass.PushOntoLuaStack&#40;L&#58; lua_State&#41;;
Var
Table &#58; Integer;
MT &#58; Integer;
i &#58; Integer;
Begin
lua_newtable&#40;L&#41;;

Table &#58;= lua_gettop&#40;L&#41;;

// create a meta table containing the object pointer
lua_newtable&#40;L&#41;;

MT &#58;= lua_gettop&#40;L&#41;;

lua_pushstring&#40;L,'__index'&#41;;
lua_pushlightuserdata&#40;L,Self&#41;;
lua_pushnumber&#40;L,i&#41;;
lua_pushcclosure&#40;L,LuaClassIndex,2&#41;;
lua_settable&#40;L,MT&#41;;

lua_pushstring&#40;L,'__newindex'&#41;;
lua_pushlightuserdata&#40;L,Self&#41;;
lua_pushnumber&#40;L,i&#41;;
lua_pushcclosure&#40;L,LuaClassNewIndex,2&#41;;
lua_settable&#40;L,MT&#41;;

lua_pushstring&#40;L,'_Self'&#41;;
lua_pushlightuserdata&#40;L,Self&#41;;
lua_settable&#40;L,MT&#41;;

lua_setmetatable&#40;L,Table&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure TLuaClass.RegisterWithLua&#40;L&#58; lua_State&#41;;
Var
i &#58; Integer;
Table &#58; Integer;
Begin
Table &#58;= LUA_GLOBALSINDEX;

If &#40;FLibName <> ''&#41; Then
Begin
lua_newtable&#40;L&#41;;

Table &#58;= lua_gettop&#40;L&#41;;
End;

PushMethodsOntoLuaStack&#40;L,Table&#41;;

If &#40;FLibName <> ''&#41; Then
lua_setglobal&#40;L,PChar&#40;FLibName&#41;&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Constructor TLuaClassFactory.Create;
Begin
Inherited Create;

RegisterLuaMethod&#40;'NewLuaClass',0&#41;;
RegisterLuaMethod&#40;'DestroyLuaClass',1&#41;;

SetLength&#40;FRegisteredClasses,0&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Destructor TLuaClassFactory.Destroy;
Begin
SetLength&#40;FRegisteredClasses,0&#41;;

Inherited Destroy;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassFactory.CallLuaMethod&#40;L&#58; lua_State; MethodIndex&#58; Integer&#41;&#58; Integer;
Begin
Case MethodIndex Of
0 &#58; Result &#58;= NewLuaClass&#40;L&#41;;
1 &#58; Result &#58;= DestroyLuaClass&#40;L&#41;;
Else
LuaDoError&#40;L,Format&#40;
'TLuaClassFactory.CallLuaMethod&#58; MethodIndex "%d" out of bounds',
&#91;MethodIndex&#93;&#41;&#41;;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure TLuaClassFactory.RegisterLuaClass&#40;ClassName&#58; AnsiString; ClassType&#58; TLuaClassClass&#41;;
Var
LuaClass&#58; TLuaClass;
Begin
If &#40;ClassName = ''&#41; Then
Exit;

If &#40;LuaClassExists&#40;ClassName&#41;&#41; Then
Raise Exception.Create&#40;'TLuaClassFactory.RegisterLuaClas s&#58; LuaClass "'+ClassName+'" is already registered'&#41;;

SetLength&#40;FRegisteredClasses,Length&#40;FRegisteredCla sses&#41; + 1&#41;;
FRegisteredClasses&#91;High&#40;FRegisteredClasses&#41;&#93;.Class Name &#58;= ClassName;
FRegisteredClasses&#91;High&#40;FRegisteredClasses&#41;&#93;.Class Type &#58;= ClassType;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassFactory.LuaClassExists&#40;ClassName&#58; AnsiString&#41;&#58; Boolean;
Var
i&#58; Integer;
Begin
Result &#58;= False;

For i &#58;= 0 To High&#40;FRegisteredClasses&#41; Do
Begin
If &#40;ClassName = FRegisteredClasses&#91;i&#93;.ClassName&#41; Then
Begin
Result &#58;= True;
Break;
End;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassFactory.NewLuaClassByName&#40;ClassName&#58; AnsiString&#41;&#58; TLuaClass;
Var
i&#58; Integer;
Begin
Result &#58;= Nil;

For i &#58;= 0 To High&#40;FRegisteredClasses&#41; Do
Begin
If &#40;ClassName = FRegisteredClasses&#91;i&#93;.ClassName&#41; Then
Begin
Result &#58;= FRegisteredClasses&#91;i&#93;.ClassType.Create;
Break;
End;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassFactory.NewLuaClass&#40;L&#58; lua_State&#41;&#58; Integer;
Var
Table &#58; Integer;
LuaClass &#58; TLuaClass;
i &#58; Integer;
ClassName &#58; AnsiString;
Begin
ClassName &#58;= luaL_checkstring&#40;L,1&#41;;

LuaClass &#58;= NewLuaClassByName&#40;ClassName&#41;;

If &#40;LuaClass = Nil&#41; Then
Begin
LuaDoError&#40;L,'NewLuaClass&#58; Unknown class type "'+ClassName+'"'&#41;;
Exit;
End;

LuaClass.PushOntoLuaStack&#40;L&#41;;

Result &#58;= 1;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaClassFactory.DestroyLuaClass&#40;L&#58; lua_State&#41;&#58; Integer;
Var
LuaRef &#58; Integer;
LuaClass&#58; TLuaClass;
Begin
luaL_checktype&#40;L,1,LUA_TTABLE&#41;;

lua_getmetatable&#40;L,1&#41;;

lua_pushstring&#40;L,'_Self'&#41;;
lua_gettable&#40;L,-2&#41;;

LuaClass &#58;= LuaClassCheck&#40;L,-1&#41;;

If &#40;LuaClass = Nil&#41; Then
Exit;

LuaClass.Free;

Result &#58;= 0;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Constructor TLuaDataStore.Create;
Begin
Inherited Create;

FDataStoreRefs &#58;= TStringList.Create;
FDataStoreRefs.Sorted &#58;= True;

RegisterLuaMethod&#40;'StoreByName',0&#41;;
RegisterLuaMethod&#40;'RetrieveByName',1&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Destructor TLuaDataStore.Destroy;
Begin
FDataStoreRefs.Free;

Inherited Destroy;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaDataStore.CallLuaMethod&#40;L&#58; lua_State; MethodIndex&#58; Integer&#41;&#58; Integer;
Begin
Case MethodIndex Of
0 &#58; Result &#58;= StoreByName&#40;L&#41;;
1 &#58; Result &#58;= RetrieveByName&#40;L&#41;;
Else
LuaDoError&#40;L,Format&#40;
'TLuaDataStore.CallLuaMethod&#58; MethodIndex "%d" out of bounds',
&#91;MethodIndex&#93;&#41;&#41;;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaDataStore.StoreByName&#40;L&#58; lua_State&#41;&#58; Integer;
Var
DataStoreKey&#58; AnsiString;
DataStoreRef&#58; Integer;
Index &#58; Integer;
Begin
luaL_checktype&#40;L,1,LUA_TSTRING&#41;;

DataStoreKey &#58;= lua_tostring&#40;L,1&#41;;

DataStoreRef &#58;= luaL_ref&#40;L,LUA_REGISTRYINDEX&#41;;

If &#40;FDataStoreRefs.Find&#40;DataStoreKey,Index&#41;&#41; Then
Begin
FDataStoreRefs.Objects&#91;Index&#93; &#58;= Pointer&#40;DataStoreRef&#41;;
End
Else
FDataStoreRefs.AddObject&#40;DataStorekey,Pointer&#40;Data StoreRef&#41;&#41;;

Result &#58;= 0;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaDataStore.RetrieveByName&#40;L&#58; lua_State&#41;&#58; Integer;
Var
DataStoreKey&#58; AnsiString;
DataStoreRef&#58; Integer;
Index &#58; Integer;
Begin
luaL_checktype&#40;L,1,LUA_TSTRING&#41;;

DataStoreKey &#58;= lua_tostring&#40;L,1&#41;;

If &#40;FDataStoreRefs.Find&#40;DataStoreKey,Index&#41;&#41; Then
Begin
DataStoreRef &#58;= Integer&#40;FDataStoreRefs.Objects&#91;Index&#93;&#41;;

lua_rawgeti&#40;L, LUA_REGISTRYINDEX, DataStoreRef&#41;;
End
Else
lua_pushnil&#40;L&#41;;

Result &#58;= 1;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Constructor TLuaDataStoreManager.Create;
Begin
Inherited Create;

FDataStores &#58;= TStringList.Create;
FDataStores.Sorted &#58;= True;

RegisterLuaMethod&#40;'DataStore',0&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Destructor TLuaDataStoreManager.Destroy;
Var
i&#58; Integer;
Begin
For i &#58;= 0 To FDataStores.Count - 1 Do
TLuaDataStore&#40;FDataStores.Objects&#91;i&#93;&#41;.Free;

FDataStores.Free;

Inherited Destroy;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaDataStoreManager.CallLuaMethod&#40;L&#58; lua_State; MethodIndex&#58; Integer&#41;&#58; Integer;
Begin
Case MethodIndex Of
0 &#58; Result &#58;= DataStore&#40;L&#41;;
Else
LuaDoError&#40;L,Format&#40;
'TLuaDataStoreManager.CallLuaMethod&#58; MethodIndex "%d" out of bounds',
&#91;MethodIndex&#93;&#41;&#41;;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaDataStoreManager.GetDataStoreByKey&#40;Key&#58; AnsiString&#41;&#58; TLuaDataStore;
Var
Index&#58; Integer;
Begin
// store DataStores in a list and retrieve by name if it exists,
// and only create it if it doesn't exist.

If &#40;FDataStores.Find&#40;Key,Index&#41;&#41; Then
Result &#58;= TLuaDataStore&#40;FDataStores.Objects&#91;Index&#93;&#41;
Else
Begin
Result &#58;= TLuaDataStore.Create;

FDataStores.AddObject&#40;Key,Result&#41;;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function TLuaDataStoreManager.DataStore&#40;L&#58; lua_State&#41;&#58; Integer;
Var
Var
Table &#58; Integer;
DataStore &#58; TLuaDataStore;
DataStoreKey&#58; AnsiString;
Begin
luaL_checktype&#40;L,1,LUA_TSTRING&#41;;

DataStoreKey &#58;= lua_tostring&#40;L,1&#41;;

DataStore &#58;= GetDataStoreByKey&#40;DataStoreKey&#41;;

DataStore.PushOntoLuaStack&#40;L&#41;;

Result &#58;= 1;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
End.

necessary code snippit of my LuaObjects unit.


Procedure LuaDoError&#40;L&#58; lua_State; ErrorMsg&#58; String&#41;;
Function LuaIsInteger&#40;L&#58; lua_State; Idx&#58; Integer; ErrorMsg&#58; String&#41;&#58; Boolean;
Function LuaIsNumber&#40;L&#58; lua_State; Idx&#58; Integer; ErrorMsg&#58; String&#41;&#58; Boolean;
Function LuaIsString&#40;L&#58; lua_State; Idx&#58; Integer; ErrorMsg&#58; String&#41;&#58; Boolean;
Function LuaIsBoolean&#40;L&#58; lua_State; Idx&#58; Integer; ErrorMsg&#58; String&#41;&#58; Boolean;

Implementation

Procedure LuaDoError&#40;L&#58; lua_State; ErrorMsg&#58; String&#41;;
Begin
lua_pushstring&#40;L,PChar&#40;ErrorMsg&#41;&#41;;
lua_error&#40;L&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function LuaIsInteger&#40;L&#58; lua_State; Idx&#58; Integer; ErrorMsg&#58; String&#41;&#58; Boolean;
Var
Value&#58; Double;
Begin
Result &#58;= True;

If &#40;lua_type&#40;L,idx&#41; = LUA_TNUMBER&#41; Then
Value &#58;= lua_tonumber&#40;L,Idx&#41;
Else
Begin
Result &#58;= False;
If &#40;ErrorMsg <> ''&#41; Then
LuaDoError&#40;L,ErrorMsg + '&#58; "integer" expected, received "' + lua_typename&#40;L,lua_type&#40;L,Idx&#41;&#41; + '"'&#41;
Else
LuaDoError&#40;L,'"integer" expected, received "' + lua_typename&#40;L,lua_type&#40;L,Idx&#41;&#41; + '"'&#41;;
Exit;
End;

If &#40;Abs&#40;Value - Trunc&#40;Value&#41;&#41; > 0.0001&#41; Then
Begin
Result &#58;= False;
If &#40;ErrorMsg <> ''&#41; Then
LuaDoError&#40;L,ErrorMsg + '&#58; "integer" expected, received "' + lua_typename&#40;L,lua_type&#40;L,Idx&#41;&#41; + '"'&#41;
Else
LuaDoError&#40;L,'"integer" expected, received "' + lua_typename&#40;L,lua_type&#40;L,Idx&#41;&#41; + '"'&#41;;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function LuaIsNumber&#40;L&#58; lua_State; Idx&#58; Integer; ErrorMsg&#58; String&#41;&#58; Boolean;
Begin
Result &#58;= True;

If &#40;lua_type&#40;L,idx&#41; <> LUA_TNUMBER&#41; Then
Begin
Result &#58;= False;
If &#40;ErrorMsg <> ''&#41; Then
LuaDoError&#40;L,ErrorMsg + '&#58; "number" expected, received "' + lua_typename&#40;L,lua_type&#40;L,Idx&#41;&#41; + '"'&#41;
Else
LuaDoError&#40;L,'"number" expected, received "' + lua_typename&#40;L,lua_type&#40;L,Idx&#41;&#41; + '"'&#41;;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function LuaIsString&#40;L&#58; lua_State; Idx&#58; Integer; ErrorMsg&#58; String&#41;&#58; Boolean;
Begin
Result &#58;= True;

If &#40;lua_type&#40;L,idx&#41; <> LUA_TSTRING&#41; Then
Begin
Result &#58;= False;
If &#40;ErrorMsg <> ''&#41; Then
LuaDoError&#40;L,ErrorMsg + '&#58; "string" expected, received "' + lua_typename&#40;L,lua_type&#40;L,Idx&#41;&#41; + '"'&#41;
Else
LuaDoError&#40;L,'"string" expected, received "' + lua_typename&#40;L,lua_type&#40;L,Idx&#41;&#41; + '"'&#41;;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function LuaIsBoolean&#40;L&#58; lua_State; Idx&#58; Integer; ErrorMsg&#58; String&#41;&#58; Boolean;
Begin
Result &#58;= True;

If &#40;lua_type&#40;L,idx&#41; <> LUA_TBOOLEAN&#41; Then
Begin
Result &#58;= False;
If &#40;ErrorMsg <> ''&#41; Then
LuaDoError&#40;L,ErrorMsg + '&#58; "boolean" expected, received "' + lua_typename&#40;L,lua_type&#40;L,Idx&#41;&#41; + '"'&#41;
Else
LuaDoError&#40;L,'"boolean" expected, received "' + lua_typename&#40;L,lua_type&#40;L,Idx&#41;&#41; + '"'&#41;;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;

Cheers,
Paul.

Robert Kosek
13-04-2006, 05:01 AM
Wow, that's a sizable unit, even snipped! :)

Well, after checking with the group I got the space at, the accounts weren't compromized, although somehow the hacker gained write access to the entire server. If you mail me a zip of the unit with your readme and license, I'll upload it. Send it to "thewickedflea <(at)> gmail >dot> com", you know the anti-spam drill. ;)

paul_nicholls
13-04-2006, 05:12 AM
Wow, that's a sizable unit, even snipped! :)

Well, after checking with the group I got the space at, the accounts weren't compromized, although somehow the hacker gained write access to the entire server. If you mail me a zip of the unit with your readme and license, I'll upload it. Send it to "thewickedflea <(at)> gmail >dot> com", you know the anti-spam drill. ;)

Cool, ok :)

I'll send you a zip file containing a complete demo source and all the needed units in their entirety :)

It would be cool if you could post a link to the zip file when done :-)

cheers,
Paul.

paul_nicholls
13-04-2006, 06:20 AM
[quote="Robert Kosek"]Wow, that's a sizable unit, even snipped! :)

Well, after checking with the group I got the space at, the accounts weren't compromized, although somehow the hacker gained write access to the entire server. If you mail me a zip of the unit with your readme and license, I'll upload it. Send it to "thewickedflea <(at)> gmail >dot> com", you know the anti-spam drill. ;)

Cool, ok :)

I'll send you a zip file containing a complete demo source and all the needed units in their entirety :)

It would be cool if you could post a ]

Hi Robert,
I tried sending my zip file to your email address (edited, non-spammed version) above but got a Delivery Status Notification (Failure) email in response :(

cheers,
Paul.

jdarling
13-04-2006, 01:10 PM
[quote="Robert Kosek"]Wow, that's a sizable unit, even snipped! :)

Well, after checking with the group I got the space at, the accounts weren't compromized, although somehow the hacker gained write access to the entire server. If you mail me a zip of the unit with your readme and license, I'll upload it. Send it to "thewickedflea <(at)> gmail >dot> com", you know the anti-spam drill. ;)

Cool, ok :)

I'll send you a zip file containing a complete demo source and all the needed units in their entirety :)

It would be cool if you could post a ]

Hi Robert,
I tried sending my zip file to your email address (edited, non-spammed version) above but got a Delivery Status Notification (Failure) email in response :(

cheers,
Paul.

If you want, send it to jdarling at eonclash dot com and I'll also be happy to place up a download link for you. Our servers have never been hacked, but they have crashed a few times. Now we have a NAS that backs everything up twice a day :).

On a side note, I've almost completed the class wrapper application that can read in a Delphi file and create class wrappers from it. Maybe you and I should team up and put our stuff together as I think between the two everything is more then covered and could be very useful to others.

jdarling
14-04-2006, 02:08 AM
After quite a bit of work the first Alpha version of Pas2Lua is ready for others to rip apart and help with development. The basic idea is that using a template file for output the unit parses a Pascal Unit, extracts the objects and their definitions and then creates a Lua Wrapper.

You can download the first Alpha exe and all files from:
http://www.eonclash.com/pas2lua/pas2lua.zip

The source (everything needed to compile except SynEdit) can be downloaded from:
http://www.eonclash.com/pas2lua/pas2lua_src.zip

I have performed minimal testing in that I know it parses and constructs for all of the unDelphiX and Delphi VCL components successfully.

Known problems:
It doesn't handle overloaded methods very nicely (you have to go in and play with the output)
Wrapper methods don't always exist for types, thus you have to create them some times (hey thats always going to be true)
Object Properties don't work right now (I'm lazy and don't have time)

Hope everyone finds this of some use and not just a waste of my time :)

paul_nicholls
17-04-2006, 10:45 AM
[quote="Robert Kosek"]Wow, that's a sizable unit, even snipped! :)

Well, after checking with the group I got the space at, the accounts weren't compromized, although somehow the hacker gained write access to the entire server. If you mail me a zip of the unit with your readme and license, I'll upload it. Send it to "thewickedflea <(at)> gmail >dot> com", you know the anti-spam drill. ;)

Cool, ok :)

I'll send you a zip file containing a complete demo source and all the needed units in their entirety :)

It would be cool if you could post a ]

Hi Robert,
I tried sending my zip file to your email address (edited, non-spammed version) above but got a Delivery Status Notification (Failure) email in response :(

cheers,
Paul.

If you want, send it to jdarling at eonclash dot com and I'll also be happy to place up a download link for you. Our servers have never been hacked, but they have crashed a few times. Now we have a NAS that backs everything up twice a day :).


cool, I have resent the zip file to Robert and yourself :-)



On a side note, I've almost completed the class wrapper application that can read in a Delphi file and create class wrappers from it.

nice :-)


Maybe you and I should team up and put our stuff together as I think between the two everything is more then covered and could be very useful to others.

I think that would be an excellent idea! :)
It isn't often I get to help others :-)

For your info, I don't have heaps of free time as I am looking after my wife and new baby daughter, but I would still like to help :)

cheers,
Paul.

jdarling
17-04-2006, 01:09 PM
Well I've found many problems with my importer and can safely say that the code it generates doesn't work right now. Soon though I hope to have it fixed. The basic problem is that I've screwed up some place and now its storing the Self Pointer in the Metatable. Witch means that each new instance changes the Self reference for all existing instances (oops). Have a message out to the LUA Mail Group, as they have helped me to find this type of error many times before.

If anyone is interested in seeing my unDelphiX wrappers at the broken stage you can view them at: http://www.eonclash.com/LUA/DXWrappersLuaImport.pas

Once I get everything working again though this file will be removed and I'll be placing the importer up for download.

Robert Kosek
17-04-2006, 03:21 PM
Interesting stuff guys. :) I'm too busy to try them at the moment, but I'll give you some thoughts on them in ~2 weeks.

You can grab Paul's lua work here: http://freepgs.com/thewickedflea/lua_class.zip

paul_nicholls
19-04-2006, 12:59 PM
Interesting stuff guys. :) I'm too busy to try them at the moment, but I'll give you some thoughts on them in ~2 weeks.

You can grab Paul's lua work here: http://freepgs.com/thewickedflea/lua_class.zip

Thanks again for the hosting of my file :)

cheers,
Paul.

paul_nicholls
01-05-2006, 07:29 AM
Interesting stuff guys. :) I'm too busy to try them at the moment, but I'll give you some thoughts on them in ~2 weeks.

You can grab Paul's lua work here: http://freepgs.com/thewickedflea/lua_class.zip

If for some reason that ]http://www.eonclash.com/LUA/Luaclass.zip[/url]

Paul.

paul_nicholls
08-05-2006, 07:03 AM
Hi al :)

I have been creating an update to my LuaClasses routines. Now I have made it easier to also add LuaRecords to scripts read/write fields :-)

Example Vector3f addition to Lua


Function Vector3f_Read&#40;L&#58; lua_State&#41;&#58; Integer; CDecl; Forward;
Function Vector3f_Write&#40;L&#58; lua_State&#41;&#58; Integer; CDecl; Forward;
Function Vector3f_Add&#40;L&#58; lua_State&#41;&#58; Integer; CDecl; Forward;
Function Vector3f_Sub&#40;L&#58; lua_State&#41;&#58; Integer; CDecl; Forward;
Function Vector3f_GC&#40;L&#58; lua_State&#41;&#58; Integer; CDecl; Forward;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function LuaVector3fCheck&#40;L&#58; lua_State; Idx&#58; Integer&#41;&#58; PVector3f;
Var
v3f &#58; PVector3f;
Begin
Result &#58;= Nil;

If &#40;Not lua_istable&#40;L,Idx&#41;&#41; Then
Begin
LuaDoError&#40;L,'Vector3f expected, found "'+lua_typename&#40;L,lua_type&#40;L,Idx&#41;&#41;+'"'&#41;;
Exit;
End;

lua_getmetatable&#40;L,Idx&#41;;

lua_pushstring&#40;L,'_Vector3f'&#41;;
lua_gettable&#40;L,-2&#41;;

If &#40;Not lua_isuserdata&#40;L,-1&#41;&#41; Then
Begin
LuaDoError&#40;L,'Vector3f expected, found "'+lua_typename&#40;L,lua_type&#40;L,Idx&#41;&#41;+'"'&#41;;
Exit;
End;

Result &#58;= lua_touserdata&#40;L,-1&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure Vector3f_PushOntoLuaStack&#40;L&#58; lua_State; V3f&#58; PVector3f&#41;;
Var
Table &#58; Integer;
MT &#58; Integer;
Begin
lua_newtable&#40;L&#41;;

Table &#58;= lua_gettop&#40;L&#41;;

// create a meta table
lua_newtable&#40;L&#41;;

MT &#58;= lua_gettop&#40;L&#41;;

lua_pushstring&#40;L,'__index'&#41;;
lua_pushcfunction&#40;L,Vector3f_Read&#41;;
lua_settable&#40;L,MT&#41;;

lua_pushstring&#40;L,'__newindex'&#41;;
lua_pushcfunction&#40;L,Vector3f_Write&#41;;
lua_settable&#40;L,MT&#41;;

lua_pushstring&#40;L,'__add'&#41;;
lua_pushcfunction&#40;L,Vector3f_Add&#41;;
lua_settable&#40;L,MT&#41;;

lua_pushstring&#40;L,'__sub'&#41;;
lua_pushcfunction&#40;L,Vector3f_Sub&#41;;
lua_settable&#40;L,MT&#41;;

lua_pushstring&#40;L,'__gc'&#41;;
lua_pushcfunction&#40;L,Vector3f_GC&#41;;
lua_settable&#40;L,MT&#41;;

lua_pushstring&#40;L,'_Vector3f'&#41;;
lua_pushlightuserdata&#40;L,V3f&#41;;
lua_settable&#40;L,MT&#41;;

lua_setmetatable&#40;L,Table&#41;;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function Vector3f_New&#40;L&#58; lua_State&#41;&#58; Integer; CDecl;
Var
V3f &#58; PVector3f;
Begin
New&#40;V3f&#41;;

V3f^.x &#58;= luaL_checknumber&#40;L,1&#41;;
V3f^.y &#58;= luaL_checknumber&#40;L,2&#41;;
V3f^.z &#58;= luaL_checknumber&#40;L,3&#41;;

Vector3f_PushOntoLuaStack&#40;L,V3f&#41;;

Result &#58;= 1;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function Vector3f_Read&#40;L&#58; lua_State&#41;&#58; Integer; CDecl;
Var
V3f &#58; PVector3f;
Key &#58; AnsiString;
Begin
Result &#58;= 0;

V3f &#58;= LuaVector3fCheck&#40;L,1&#41;;

If &#40;V3f = Nil&#41; Then
Exit;

Key &#58;= luaL_checkstring&#40;L,2&#41;;

If &#40;Key = 'x'&#41; Then
lua_pushnumber&#40;L,V3f^.x&#41;
Else
If &#40;Key = 'y'&#41; Then
lua_pushnumber&#40;L,V3f^.y&#41;
Else
If &#40;Key = 'z'&#41; Then
lua_pushnumber&#40;L,V3f^.z&#41;
Else
Begin
LuaDoError&#40;L,'Vector3f&#58; unknown field "' + Key + '"'&#41;;
End;

Result &#58;= 1;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function Vector3f_Write&#40;L&#58; lua_State&#41;&#58; Integer; CDecl;
Var
V3f &#58; PVector3f;
Key &#58; AnsiString;
Begin
Result &#58;= 0;

V3f &#58;= LuaVector3fCheck&#40;L,1&#41;;

If &#40;V3f = Nil&#41; Then
Exit;

Key &#58;= luaL_checkstring&#40;L,2&#41;;

If &#40;Key = 'x'&#41; Then
V3f^.x &#58;= luaL_checknumber&#40;L,3&#41;
Else
If &#40;Key = 'y'&#41; Then
V3f^.y &#58;= luaL_checknumber&#40;L,3&#41;
Else
If &#40;Key = 'z'&#41; Then
V3f^.z &#58;= luaL_checknumber&#40;L,3&#41;
Else
Begin
LuaDoError&#40;L,'Vector3f&#58; unknown field "' + Key + '"'&#41;;
End;

Result &#58;= 0;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function Vector3f_Add&#40;L&#58; lua_State&#41;&#58; Integer; CDecl;
Var
v1,v2,v3 &#58; PVector3f;
Key &#58; AnsiString;
Begin
Result &#58;= 0;

v1 &#58;= LuaVector3fCheck&#40;L,1&#41;;
v2 &#58;= LuaVector3fCheck&#40;L,2&#41;;

If &#40;v1 = Nil&#41; Or &#40;v2 = Nil&#41; Then
Exit;

New&#40;v3&#41;;

v3^.x &#58;= v1^.x + v2^.x;
v3^.y &#58;= v1^.y + v2^.y;
v3^.z &#58;= v1^.z + v2^.z;

Vector3f_PushOntoLuaStack&#40;L,v3&#41;;

Result &#58;= 1;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function Vector3f_Sub&#40;L&#58; lua_State&#41;&#58; Integer; CDecl;
Var
v1,v2,v3 &#58; PVector3f;
Key &#58; AnsiString;
Begin
Result &#58;= 0;

v1 &#58;= LuaVector3fCheck&#40;L,1&#41;;
v2 &#58;= LuaVector3fCheck&#40;L,2&#41;;

If &#40;v1 = Nil&#41; Or &#40;v2 = Nil&#41; Then
Exit;

New&#40;v3&#41;;

v3^.x &#58;= v1^.x - v2^.x;
v3^.y &#58;= v1^.y - v2^.y;
v3^.z &#58;= v1^.z - v2^.z;

Vector3f_PushOntoLuaStack&#40;L,v3&#41;;

Result &#58;= 1;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Function Vector3f_GC&#40;L&#58; lua_State&#41;&#58; Integer; CDecl;
Var
V3f &#58; PVector3f;
Begin
V3f &#58;= LuaVector3fCheck&#40;L,1&#41;;

If &#40;V3f = Nil&#41; Then
Exit;

Dispose&#40;V3f&#41;;

Result &#58;= 0;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;

how to register the record with Lua


// register each record type
RegisterLuaRecord&#40;'Vector3f',Vector3f_New&#41;;

// register all registered records to the lua state
RegisterLuaRecordsWithLua&#40;Script.State&#41;;

Lua script using the record


a = Vector3f&#40;10,20,30&#41;
b = Vector3f&#40;4,5,6&#41;

c = a + b

print&#40;c.x,c.y,c.z&#41;

LuaRecords unit code


Unit LuaRecords;
&#123;................................................. ......&#125;
// created by Paul Nicholls
// Copyright 2006
// Use for anything that you want, but
// please email me with any improvements that you may make &#58;&#41;
//
// Email&#58;
// paul_nicholls.hotmail.com
&#123;................................................. ......&#125;

Interface

Uses
Lua;

Procedure RegisterLuaRecordProcs&#40;ARecordName&#58; AnsiString;
ANewRecordProc&#58; lua_CFunction&#41;;
Procedure RegisterLuaRecordsWithLua&#40;L&#58; lua_State&#41;;

Implementation

Type
TLuaRecord = Packed Record
RecordName &#58; AnsiString;
ANewRecordProc&#58; lua_CFunction;
End;

TLuaRecordArray = Array Of TLuaRecord;

Var
RegisteredLuaRecords&#58; TLuaRecordArray;

&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure RegisterLuaRecordProcs&#40;ARecordName&#58; AnsiString;
ANewRecordProc&#58; lua_CFunction&#41;;
Begin
If &#40;Not Assigned&#40;ANewRecordProc&#41;&#41; Then
Exit;

SetLength&#40;RegisteredLuaRecords,Length&#40;RegisteredLu aRecords&#41; + 1&#41;;

RegisteredLuaRecords&#91;High&#40;RegisteredLuaRecords&#41;&#93;.R ecordName &#58;= ARecordName;
RegisteredLuaRecords&#91;High&#40;RegisteredLuaRecords&#41;&#93;.A NewRecordProc &#58;= ANewRecordProc;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Procedure RegisterLuaRecordsWithLua&#40;L&#58; lua_State&#41;;
Var
i &#58; Integer;
LuaRecord &#58; TLuaRecord;
Begin
For i &#58;= 0 To High&#40;RegisteredLuaRecords&#41; Do
Begin
LuaRecord &#58;= RegisteredLuaRecords&#91;i&#93;;

lua_register&#40;L,PChar&#40;LuaRecord.RecordName&#41;,LuaReco rd.ANewRecordProc&#41;;
End;
End;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
Initialization
SetLength&#40;RegisteredLuaRecords,0&#41;;
&#123;................................................. ......&#125;

&#123;................................................. ......&#125;
End.

paul_nicholls
08-05-2006, 07:05 AM
Can someone delete the first post I did?
The second one is more correct and I can't seem to delete it :(

savage
08-05-2006, 08:27 AM
I hope I deleted the right one.

paul_nicholls
08-05-2006, 10:34 PM
I hope I deleted the right one.

Thanks savage, yes you did delete the correct one :)

BTW, I have made another small change to the code.

I have renamed the 'LuaVector3fCheck' function to 'LuaRecordCheck' and am now passing in the RecordName into the function as well to make it a generic LuaRecord checking function.

I then moved it to the LuaRecords unit along with the appropriate interface header for the function.

So you have to change rename the function, make the small code changes to it, move it to the LuaRecords unit and change all references of 'LuaVector3fCheck' in the example code to 'LuaRecordCheck' and add in the 'Vector3f' parameter when using it (substitute for the correct record name when using it for other LuaRecord types like so:


V3f &#58;= LuaRecordCheck&#40;L,'Vector3f',1&#41;;

New LuaRecordCheck function now in the LuaRecords unit


Function LuaRecordCheck&#40;L&#58; lua_State; RecordName&#58; AnsiString; Idx&#58; Integer&#41;&#58; Pointer;
Begin
Result &#58;= Nil;

If &#40;Not lua_istable&#40;L,Idx&#41;&#41; Then
Begin
LuaDoError&#40;L,RecordName + ' expected, found "'+lua_typename&#40;L,lua_type&#40;L,Idx&#41;&#41;+'"'&#41;;
Exit;
End;

lua_getmetatable&#40;L,Idx&#41;;

lua_pushstring&#40;L,PChar&#40;'_' + RecordName&#41;&#41;;
lua_gettable&#40;L,-2&#41;;

If &#40;Not lua_isuserdata&#40;L,-1&#41;&#41; Then
Begin
LuaDoError&#40;L,RecordName + ' expected, found "'+lua_typename&#40;L,lua_type&#40;L,Idx&#41;&#41;+'"'&#41;;
Exit;
End;

Result &#58;= lua_touserdata&#40;L,-1&#41;;
End;

Enjoy :)

cheers,
Paul.