diff options
author | David Walter Seikel | 2014-03-28 15:44:43 +1000 |
---|---|---|
committer | David Walter Seikel | 2014-03-28 15:44:43 +1000 |
commit | 5018f9e7673f533bbe3240c2ecce703b47d76a73 (patch) | |
tree | c9cfa3b06f3a830d2819ee42ccd50f7a6378f343 | |
parent | TODO++ (diff) | |
download | SledjHamr-5018f9e7673f533bbe3240c2ecce703b47d76a73.zip SledjHamr-5018f9e7673f533bbe3240c2ecce703b47d76a73.tar.gz SledjHamr-5018f9e7673f533bbe3240c2ecce703b47d76a73.tar.bz2 SledjHamr-5018f9e7673f533bbe3240c2ecce703b47d76a73.tar.xz |
Make test_c a real skang module, and tweaks to the rest of the system to support C skang modules.
-rw-r--r-- | ClientHamr/GuiLua/skang.lua | 19 | ||||
-rw-r--r-- | ClientHamr/GuiLua/test.lua | 5 | ||||
-rw-r--r-- | ClientHamr/GuiLua/test_c.c | 177 |
3 files changed, 147 insertions, 54 deletions
diff --git a/ClientHamr/GuiLua/skang.lua b/ClientHamr/GuiLua/skang.lua index c5c2096..6f06131 100644 --- a/ClientHamr/GuiLua/skang.lua +++ b/ClientHamr/GuiLua/skang.lua | |||
@@ -77,10 +77,12 @@ local versions = { | |||
77 | 77 | ||
78 | -- Trying to capture best practices here for creating modules, especially since module() is broken and deprecated. | 78 | -- Trying to capture best practices here for creating modules, especially since module() is broken and deprecated. |
79 | -- TODO - Should parse in license type to. | 79 | -- TODO - Should parse in license type to. |
80 | moduleBegin = function (name, author, copyright, version, timestamp, skin) | 80 | moduleBegin = function (name, author, copyright, version, timestamp, skin, isLua) |
81 | local _M = {} -- This is what we return to require(). | 81 | local _M = {} -- This is what we return to require(). |
82 | local level = 2 | 82 | local level = 2 |
83 | 83 | ||
84 | if 'nil' == type(isLua) then isLua = true end | ||
85 | |||
84 | package.loaded[name] = _M -- Stuff the result into where require() can find it, instead of returning it at the end. | 86 | package.loaded[name] = _M -- Stuff the result into where require() can find it, instead of returning it at the end. |
85 | -- Returning it at the end does the same thing. | 87 | -- Returning it at the end does the same thing. |
86 | -- This is so that we can have all the module stuff at the top, in this function. | 88 | -- This is so that we can have all the module stuff at the top, in this function. |
@@ -102,6 +104,7 @@ moduleBegin = function (name, author, copyright, version, timestamp, skin) | |||
102 | _M._M = _M -- So that references to _M below the setfenv() actually go to the real _M. | 104 | _M._M = _M -- So that references to _M below the setfenv() actually go to the real _M. |
103 | _M._NAME = name | 105 | _M._NAME = name |
104 | _M._PACKAGE = string.gsub(_M._NAME, "[^.]*$", "") -- Strip the name down to the package name. | 106 | _M._PACKAGE = string.gsub(_M._NAME, "[^.]*$", "") -- Strip the name down to the package name. |
107 | _M.isLua = isLua | ||
105 | 108 | ||
106 | -- Parse in an entire copyright message, and strip that down into bits, to put back together. | 109 | -- Parse in an entire copyright message, and strip that down into bits, to put back together. |
107 | local date, owner = string.match(copyright, '[Cc]opyright (%d%d%d%d) (.*)') | 110 | local date, owner = string.match(copyright, '[Cc]opyright (%d%d%d%d) (.*)') |
@@ -129,12 +132,15 @@ moduleBegin = function (name, author, copyright, version, timestamp, skin) | |||
129 | 132 | ||
130 | setmetatable(_M, Thing) | 133 | setmetatable(_M, Thing) |
131 | _M.savedEnvironment = savedEnvironment | 134 | _M.savedEnvironment = savedEnvironment |
132 | -- setfenv() sets the environment for the FUNCTION, stack level deep. | 135 | -- NOTE - setfenv() wont work if the environment it refers to is a C function. Worse, getfenv() returns the global environment, so we can't tell. |
133 | -- The number is the stack level - | 136 | if isLua then |
134 | -- 0 running thread, 1 current function, 2 function that called this function, etc | 137 | -- setfenv() sets the environment for the FUNCTION, stack level deep. |
135 | setfenv(level, _M) -- Use the result for the modules internal global environment, so they don't need to qualify internal names. | 138 | -- The number is the stack level - |
139 | -- 0 running thread, 1 current function, 2 function that called this function, etc | ||
140 | setfenv(level, _M) -- Use the result for the modules internal global environment, so they don't need to qualify internal names. | ||
136 | -- Dunno if this causes problems with the do ... end style of joining modules. It does. So we need to restore in moduleEnd(). | 141 | -- Dunno if this causes problems with the do ... end style of joining modules. It does. So we need to restore in moduleEnd(). |
137 | -- Next question, does this screw with the environment of the skang module? No it doesn't, coz that's set up at require 'skang' time. | 142 | -- Next question, does this screw with the environment of the skang module? No it doesn't, coz that's set up at require 'skang' time. |
143 | end | ||
138 | 144 | ||
139 | print('Loaded module ' .. _M._NAME .. ' version ' .. _M.VERSION .. ', ' .. _M.COPYRIGHT .. '.\n ' .. _M.VERSION_DESC) | 145 | print('Loaded module ' .. _M._NAME .. ' version ' .. _M.VERSION .. ', ' .. _M.COPYRIGHT .. '.\n ' .. _M.VERSION_DESC) |
140 | 146 | ||
@@ -146,7 +152,7 @@ moduleEnd = function (module) | |||
146 | -- TODO - Look for _NAME.properties, and load it into the modules Things. | 152 | -- TODO - Look for _NAME.properties, and load it into the modules Things. |
147 | -- TODO - Parse command line parameters at some point. | 153 | -- TODO - Parse command line parameters at some point. |
148 | -- http://stackoverflow.com/questions/3745047/help-locate-c-sample-code-to-read-lua-command-line-arguments | 154 | -- http://stackoverflow.com/questions/3745047/help-locate-c-sample-code-to-read-lua-command-line-arguments |
149 | setfenv(2, module.savedEnvironment) | 155 | if module.isLua then setfenv(2, module.savedEnvironment) end |
150 | end | 156 | end |
151 | 157 | ||
152 | -- Call this now so that from now on, this is like any other module. | 158 | -- Call this now so that from now on, this is like any other module. |
@@ -424,6 +430,7 @@ thing = function (names, ...) | |||
424 | thing.required = params[5] or thing.required | 430 | thing.required = params[5] or thing.required |
425 | thing.acl = params[6] or thing.acl | 431 | thing.acl = params[6] or thing.acl |
426 | thing.boss = params[7] or thing.boss | 432 | thing.boss = params[7] or thing.boss |
433 | thing.module = params[8] or thing.module -- Mostly for things like C functions, where get/setfenv() wont do what we need. | ||
427 | 434 | ||
428 | -- PUll out named arguments. | 435 | -- PUll out named arguments. |
429 | for k, v in pairs(params) do | 436 | for k, v in pairs(params) do |
diff --git a/ClientHamr/GuiLua/test.lua b/ClientHamr/GuiLua/test.lua index 3c2e9ab..d566354 100644 --- a/ClientHamr/GuiLua/test.lua +++ b/ClientHamr/GuiLua/test.lua | |||
@@ -49,10 +49,11 @@ local skang = require 'skang' | |||
49 | local test = require 'test' | 49 | local test = require 'test' |
50 | local test_c = require 'test_c' | 50 | local test_c = require 'test_c' |
51 | 51 | ||
52 | print('foo = ' .. test.foo .. ' ->> ' .. skang.things.foo.help) | ||
53 | print('End ' .. test.bar .. ' ' .. test.VERSION .. ' ' .. skang.things.ffunc.help .. ' ->> ' .. skang.things.f.action) | 52 | print('End ' .. test.bar .. ' ' .. test.VERSION .. ' ' .. skang.things.ffunc.help .. ' ->> ' .. skang.things.f.action) |
53 | print('foo = ' .. test.foo .. ' ->> ' .. skang.things.foo.help) | ||
54 | print('cfunc ->> ' .. skang.things.cfunc.help) | ||
54 | test.ffunc('one', 2) | 55 | test.ffunc('one', 2) |
55 | test_c.ffunc(0, 'zero') | 56 | test_c.cfunc(0, 'zero') |
56 | --skang.things.ffunc('seven', 'aight') | 57 | --skang.things.ffunc('seven', 'aight') |
57 | print('') | 58 | print('') |
58 | 59 | ||
diff --git a/ClientHamr/GuiLua/test_c.c b/ClientHamr/GuiLua/test_c.c index 8d3ab81..e3fd801 100644 --- a/ClientHamr/GuiLua/test_c.c +++ b/ClientHamr/GuiLua/test_c.c | |||
@@ -8,9 +8,6 @@ http://lua.2524044.n2.nabble.com/C-Lua-modules-not-compatible-with-every-Lua-int | |||
8 | http://lua-users.org/wiki/LuaProxyDllFour | 8 | http://lua-users.org/wiki/LuaProxyDllFour |
9 | http://stackoverflow.com/questions/11492194/how-do-you-create-a-lua-plugin-that-calls-the-c-lua-api?rq=1 | 9 | http://stackoverflow.com/questions/11492194/how-do-you-create-a-lua-plugin-that-calls-the-c-lua-api?rq=1 |
10 | http://lua-users.org/lists/lua-l/2008-01/msg00671.html | 10 | http://lua-users.org/lists/lua-l/2008-01/msg00671.html |
11 | |||
12 | |||
13 | |||
14 | */ | 11 | */ |
15 | 12 | ||
16 | 13 | ||
@@ -19,72 +16,160 @@ http://lua-users.org/lists/lua-l/2008-01/msg00671.html | |||
19 | //#include <lualib.h> | 16 | //#include <lualib.h> |
20 | 17 | ||
21 | 18 | ||
22 | static int ffunc (lua_State *L) | 19 | static const char *ourName = "test_c"; |
20 | int skang, _M; | ||
21 | |||
22 | static int cfunc (lua_State *L) | ||
23 | { | 23 | { |
24 | double arg1 = luaL_checknumber(L, 1); | 24 | double arg1 = luaL_checknumber(L, 1); |
25 | const char *arg2 = luaL_checkstring(L, 2); | 25 | const char *arg2 = luaL_checkstring(L, 2); |
26 | 26 | ||
27 | printf("Inside test_c.ffunc(%f, %s)\n", arg1, arg2); | 27 | printf("Inside %s.cfunc(%f, %s)\n", ourName, arg1, arg2); |
28 | return 0; | 28 | return 0; |
29 | } | 29 | } |
30 | 30 | ||
31 | 31 | ||
32 | static const struct luaL_reg test_c [] = | 32 | static void dumpStack(lua_State *L, int i) |
33 | { | 33 | { |
34 | {"ffunc", ffunc}, | 34 | int type = lua_type(L, i); |
35 | {NULL, NULL} | 35 | |
36 | }; | 36 | switch (type) |
37 | 37 | { | |
38 | case LUA_TNONE : printf("Stack %d is empty\n", i); break; | ||
39 | case LUA_TNIL : printf("Stack %d is a nil\n", i); break; | ||
40 | case LUA_TBOOLEAN : printf("Stack %d is a boolean\n", i); break; | ||
41 | case LUA_TNUMBER : printf("Stack %d is a number\n", i); break; | ||
42 | case LUA_TSTRING : printf("Stack %d is a string - %s\n", i, lua_tostring(L, i)); break; | ||
43 | case LUA_TFUNCTION : printf("Stack %d is a function\n", i); break; | ||
44 | case LUA_TTHREAD : printf("Stack %d is a thread\n", i); break; | ||
45 | case LUA_TTABLE : printf("Stack %d is a table\n", i); break; | ||
46 | case LUA_TUSERDATA : printf("Stack %d is a userdata\n", i); break; | ||
47 | case LUA_TLIGHTUSERDATA : printf("Stack %d is a light userdata\n", i); break; | ||
48 | default : printf("Stack %d is unknown\n", i); break; | ||
49 | } | ||
50 | } | ||
38 | 51 | ||
39 | /* local test_c = require 'test_c' | 52 | /* local test_c = require 'test_c' |
40 | 53 | ||
41 | Lua's require() function will strip any stuff from the front of the name | 54 | Lua's require() function will strip any stuff from the front of the name |
42 | separated by a hypen, so 'GuiLua-test_c' -> 'test_c'. Then it will | 55 | separated by a hyphen, so 'GuiLua-test_c' -> 'test_c'. Then it will |
43 | search through a path, and eventually find this test_c.so (or test_c.dll | 56 | search through a path, and eventually find this test_c.so (or test_c.dll |
44 | or whatever), then call luaopen_test_c(), which should return a table. | 57 | or whatever), then call luaopen_test_c(), which should return a table. |
58 | The argument (only thing on the stack) for this function will be | ||
59 | 'test_c'. | ||
45 | 60 | ||
46 | Normally luaL_register() creates a table of functions, that is the table | 61 | Normally luaL_register() creates a table of functions, that is the table |
47 | returned, but we want to do something different with skang. | 62 | returned, but we want to do something different with skang. |
48 | |||
49 | */ | 63 | */ |
50 | int luaopen_test_c(lua_State *L) | 64 | int luaopen_test_c(lua_State *L) |
51 | { | 65 | { |
52 | // This is a moving target, old ways get deperecated, new ways get added, | ||
53 | // would have to check version before doing any of these. | ||
54 | // luaL_openlib(L, "test_c", test_c, 0); // Lua 5.0 way. | ||
55 | // luaL_register (L, "test_c", test_c); // Lua 5.1 way. | ||
56 | // luaL_newlib() or luaL_setfuncs() // Lua 5.2 way. | ||
57 | // Creates a global table "test_c", does the package.loaded[test_c] thing. | ||
58 | lua_newtable(L); | ||
59 | luaL_register (L, NULL, test_c); // Lua 5.1 way. | ||
60 | // Puts the funcions in a table on top of the stack. | ||
61 | |||
62 | /* BUT REALLY ... | ||
63 | |||
64 | We are in fact NOT putting any functions into the returned table. | ||
65 | |||
66 | skang.moduleBegin() returns the table we need to send back to Lua. | ||
67 | it saves getfenv(2) as the old environment, which should in theory be L | ||
68 | and setfenv(_M, 2) to set the tbale to be it's own environment | ||
69 | it does the package.loaded[test_c] thing for us | ||
70 | it returns the table it created, so we should just leave that on the stack as our result | ||
71 | |||
72 | skang.thing() also uses getfenv(2) to grab the module's table | ||
73 | 66 | ||
67 | // In theory, the only thing on the stack now is 'test_c' from the require() call. | ||
68 | |||
69 | // pseudo-indices, special tables that can be accessed like the stack - | ||
70 | // LUA_GLOBALSINDEX - thread environment, where globals are | ||
71 | // LUA_ENVIRONINDEX - C function environment, in this case luaopen_test_c() is the C function | ||
72 | // LUA_REGISTRYINDEX - C registry, global, for unique keys use the module name as a string, or a lightuserdata address to a C object in our module. | ||
73 | // lua_upvalueindex(n) - C function upvalues | ||
74 | |||
75 | // The only locals we care about are skang and _M. | ||
76 | // All modules go into package.loaded[name] as well. | ||
77 | // skang is essentially a global anyway. | ||
78 | // _M we pass back as the result, and our functions get added to it by skang.thing() | ||
79 | // Not entirely true, skang.things is a proxy table, skang.things.ffunc.func would be our function. | ||
80 | // skang.things.ffunc.module is our _M ('environment of our calling function', so that's the 'environment' right here, since we call skang.thing(). | ||
81 | // test_c.ffunc() -> test_c.__index(test_c, 'ffunc') -> skang.things[ffunc].value() -> test_c.c->ffunc() | ||
82 | // this is a C function, not a Lua function | ||
83 | |||
84 | // local skang = require 'skang' | ||
85 | lua_getglobal(L, "require"); | ||
86 | lua_pushstring(L, "skang"); | ||
87 | lua_call(L, 1, 1); | ||
88 | skang = lua_gettop(L); | ||
89 | // dumpStack(L, skang); | ||
90 | |||
91 | // local _M = skang.moduleBegin('test_c', nil, 'Copyright 2014 David Seikel', '0.1', '2014-03-27 03:57:00', nil, true) | ||
92 | lua_getfield(L, skang, "moduleBegin"); | ||
93 | lua_pushstring(L, ourName); | ||
94 | lua_pushnil(L); // Author comes from copyright. | ||
95 | lua_pushstring(L, "Copyright 2014 David Seikel"); | ||
96 | lua_pushstring(L, "0.1"); | ||
97 | lua_pushstring(L, "2014-03-27 03:57:00"); | ||
98 | lua_pushnil(L); // No skin. | ||
99 | lua_pushboolean(L, 0); // We are not a Lua module. | ||
100 | lua_call(L, 7, 1); // call 'skang.moduleBegin' with 7 arguments and 1 result. | ||
101 | _M = lua_gettop(L); | ||
102 | // dumpStack(L, _M); | ||
103 | |||
104 | // At this point the stack should be - 'test_c', skang, _M. Let's test that. | ||
105 | /* | ||
106 | int top = 0, i; | ||
107 | |||
108 | top = lua_gettop(L); | ||
109 | printf("MODULE test_c has %d stack items.\n", top); | ||
110 | for (i = 1; i <= top; i++) | ||
111 | dumpStack(L, i); | ||
74 | */ | 112 | */ |
75 | 113 | ||
76 | /* TODO - load skang, create things, etc. | 114 | // Save this module in the C registry. |
77 | 115 | lua_setfield(L, LUA_REGISTRYINDEX, ourName); | |
78 | local skang = require "skang" | 116 | |
79 | local _M = skang.moduleBegin("test_c", nil, "Copyright 2014 David Seikel", "0.1", "2014-03-27 03:57:00") | 117 | // skang.thing('cfooble,c', 'Help text goes here', 1, 'number', \"'edit', 'The fooble:', 1, 1, 10, 50\", true) |
118 | lua_getfield(L, skang, "thing"); | ||
119 | lua_pushstring(L, "cfooble,c"); | ||
120 | lua_pushstring(L, "Help text"); | ||
121 | lua_pushnumber(L, 1); | ||
122 | lua_pushstring(L, "number"); | ||
123 | lua_pushstring(L, "'edit', 'The cfooble:', 1, 1, 10, 50"); | ||
124 | lua_pushboolean(L, 1); // Is required. | ||
125 | lua_pushnil(L); // Default ACL. | ||
126 | lua_pushnil(L); // No boss. | ||
127 | lua_getfield(L, LUA_REGISTRYINDEX, ourName); // Coz getfenv() can't find C environment. | ||
128 | lua_call(L, 9, 0); | ||
129 | |||
130 | // skang.thing('cbar', 'Help text', 'Default') | ||
131 | lua_getfield(L, skang, "thing"); | ||
132 | lua_pushstring(L, "cbar"); | ||
133 | lua_pushstring(L, "Help text"); | ||
134 | lua_pushstring(L, "Default"); | ||
135 | lua_pushnil(L); // No type. | ||
136 | lua_pushnil(L); // No widget. | ||
137 | lua_pushnil(L); // Not required. | ||
138 | lua_pushnil(L); // Default ACL. | ||
139 | lua_pushnil(L); // No boss. | ||
140 | lua_getfield(L, LUA_REGISTRYINDEX, ourName); // Coz getfenv() can't find C environment. | ||
141 | lua_call(L, 9, 0); | ||
142 | |||
143 | // skang.thing('cfoo') | ||
144 | lua_getfield(L, skang, "thing"); | ||
145 | lua_pushstring(L, "cfoo"); | ||
146 | lua_pushnil(L); // No help. | ||
147 | lua_pushnil(L); // No default. | ||
148 | lua_pushnil(L); // No type. | ||
149 | lua_pushnil(L); // No widget. | ||
150 | lua_pushnil(L); // Not required. | ||
151 | lua_pushnil(L); // Default ACL. | ||
152 | lua_pushnil(L); // No boss. | ||
153 | lua_getfield(L, LUA_REGISTRYINDEX, ourName); // Coz getfenv() can't find C environment. | ||
154 | lua_call(L, 9, 0); | ||
155 | |||
156 | // skang.thing('cfunc', 'Help Text', ffunc, 'number,string') | ||
157 | lua_getfield(L, skang, "thing"); | ||
158 | lua_pushstring(L, "cfunc"); | ||
159 | lua_pushstring(L, "cfunc does nothing really"); | ||
160 | lua_pushcfunction(L, cfunc); | ||
161 | lua_pushstring(L, "number,string"); | ||
162 | lua_pushnil(L); // No widget. | ||
163 | lua_pushnil(L); // Not required. | ||
164 | lua_pushnil(L); // Default ACL. | ||
165 | lua_pushnil(L); // No boss. | ||
166 | lua_getfield(L, LUA_REGISTRYINDEX, ourName); // Coz getfenv() can't find C environment. | ||
167 | lua_call(L, 9, 0); | ||
168 | |||
169 | // skang.moduleEnd(_M) | ||
170 | lua_getfield(L, skang, "moduleEnd"); | ||
171 | lua_getfield(L, LUA_REGISTRYINDEX, ourName); | ||
172 | lua_call(L, 1, 1); | ||
80 | 173 | ||
81 | skang.thing("fooble,f", "Help text goes here", 1, "number", "'edit', 'The fooble:', 1, 1, 10, 50", true) | ||
82 | skang.thing("bar", "Help text", "Default") | ||
83 | skang.thing("foo") | ||
84 | skang.thing("ffunc", "Help Text", ffunc, "number,string") | ||
85 | |||
86 | skang.moduleEnd(_M) | ||
87 | |||
88 | */ | ||
89 | return 1; | 174 | return 1; |
90 | } | 175 | } |