summaryrefslogtreecommitdiff
path: root/Tools/LuaMacro/tests/lc.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/LuaMacro/tests/lc.lua')
-rw-r--r--Tools/LuaMacro/tests/lc.lua297
1 files changed, 297 insertions, 0 deletions
diff --git a/Tools/LuaMacro/tests/lc.lua b/Tools/LuaMacro/tests/lc.lua
new file mode 100644
index 0000000..fc8973d
--- /dev/null
+++ b/Tools/LuaMacro/tests/lc.lua
@@ -0,0 +1,297 @@
+-- Simplifying writing C extensions for Lua
+-- Adds new module and class constructs;
+-- see class1.lc and str.lc for examples.
+local M = require 'macro'
+
+function dollar_subst(s,tbl)
+ return (s:gsub('%$%((%a+)%)',tbl))
+end
+
+-- reuse some machinery from the C-skin experiments
+local push,pop = table.insert,table.remove
+local bstack,btop = {},{}
+
+local function push_brace_stack (newv)
+ newv = newv or {}
+ newv.lev = 0
+ push(bstack,btop)
+ btop = newv
+end
+
+M.define('{',function()
+ if btop.lev then
+ btop.lev = btop.lev + 1
+ end
+ return nil,true --> pass-through macro
+end)
+
+M.define('}',function(get,put)
+ if not btop.lev then
+ return nil,true
+ elseif btop.lev == 0 then
+ local res
+ if btop.handler then res = btop.handler(get,put) end
+ if not res then res = put:space() '}' end
+ btop = pop(bstack)
+ return res
+ else
+ btop.lev = btop.lev - 1
+ return nil,true --> pass-through macro
+ end
+end)
+
+--------- actual implementation begins -------
+
+local append = table.insert
+local module
+
+local function register_functions (names,cnames)
+ local out = {}
+ for i = 1,#names do
+ append(out,(' {"%s",l_%s},'):format(names[i],cnames[i]))
+ end
+ return table.concat(out,'\n')
+end
+
+local function finalizers (names)
+ local out = {}
+ for i = 1,#names do
+ append(out,names[i].."(L);")
+ end
+ return table.concat(out,'\n')
+end
+
+local typedefs
+
+local preamble = [[
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+#ifdef WIN32
+#define EXPORT __declspec(dllexport)
+#else
+#define EXPORT
+#endif
+]]
+
+local finis = [[
+static const luaL_reg $(cname)_funs[] = {
+ $(funs)
+ {NULL,NULL}
+};
+
+EXPORT int luaopen_$(cname) (lua_State *L) {
+ luaL_register (L,"$(name)",$(cname)_funs);
+ $(finalizers)
+ return 1;
+}
+]]
+
+M.define('module',function(get)
+ local name = get:string()
+ local cname = name:gsub('%.','_')
+ get:expecting '{'
+ local out = preamble .. typedefs
+ push_brace_stack{
+ name = name, cname = cname,
+ names = {}, cnames = {}, finalizers = {},
+ handler = function()
+ local out = {}
+ local funs = register_functions(btop.names,btop.cnames)
+ local final = finalizers(btop.finalizers)
+ append(out,dollar_subst(finis, {
+ cname = cname,
+ name = name,
+ funs = funs,
+ finalizers = final
+ }))
+ return table.concat(out,'\n')
+ end }
+ module = btop
+ return out
+end)
+
+
+M.define('def',function(get)
+ local fname = get:iden()
+ local cname = (btop.ns and btop.ns..'_' or '')..fname
+ append(btop.names,fname)
+ append(btop.cnames,cname)
+ get:expecting '('
+ local args = get:list():strip_spaces()
+ get:expecting '{'
+ local t,space = get()
+ indent = space:gsub('^%s*[\n\r]',''):gsub('%s$','')
+ local out = {"static int l_"..cname.."(lua_State *L) {"}
+ if btop.massage_arg then
+ btop.massage_arg(args)
+ end
+ for i,arg in ipairs(args) do
+ local mac = arg[1][2]..'_init'
+ if arg[3] and arg[3][1] == '=' then
+ mac = mac .. 'o'
+ i = i .. ',' .. arg[4][2]
+ end
+ append(out,indent..mac..'('..arg[2][2]..','..i..');')
+ end
+ append(out,space)
+ return table.concat(out,'\n')
+end)
+
+M.define('constants',function(get,put)
+ get:expecting '{'
+ local consts = get:list '}' :strip_spaces()
+ --for k,v in pairs(btop) do io.stderr:write(k,'=',tostring(v),'\n') end
+ -- os.exit()
+ local fname = 'set_'..btop.cname..'_constants'
+ local out = { 'static void '..fname..'(lua_State *L) {'}
+ append(btop.finalizers,fname)
+ for _,c in ipairs(consts) do
+ local type,value,name
+ if #c == 1 then -- a simple int constant: CONST
+ name = c:pick(1)
+ type = 'Int'
+ value = name
+ else -- Type CONST [ = VALUE ]
+ type = c:pick(1)
+ name = c:pick(2)
+ if #c == 2 then
+ value = name
+ else
+ value = c:pick(4)
+ end
+ end
+ append(out,('%s_set("%s",%s);'):format(type,name,value ))
+ end
+ append(out,'}')
+ return table.concat(out,'\n')
+end)
+
+M.define('assign',function(get)
+ get:expecting '{'
+ local asses = get:list '}' :strip_spaces()
+ local out = {}
+ for _,c in ipairs(asses) do
+ append(out,('%s_set("%s",%s);\n'):format(c:pick(1),c:pick(2),c:pick(4)) )
+ end
+ return table.concat(out,'\n')
+end)
+
+typedefs = [[
+typedef const char *Str;
+typedef const char *StrNil;
+typedef int Int;
+typedef double Number;
+typedef int Boolean;
+]]
+
+
+M.define 'Str_init(var,idx) const char *var = luaL_checklstring(L,idx,NULL)'
+M.define 'Str_inito(var,idx,val) const char *var = luaL_optlstring(L,idx,val,NULL)'
+M.define 'Str_set(name,value) lua_pushstring(L,value); lua_setfield(L,-2,name)'
+M.define 'Str_get(var,name) lua_getfield(L,-1,name); var=lua_tostring(L,-1); lua_pop(L,1)'
+
+M.define 'StrNil_init(var,idx) const char *var = lua_tostring(L,idx)'
+
+M.define 'Int_init(var,idx) int var = luaL_checkinteger(L,idx)'
+M.define 'Int_inito(var,idx,val) int var = luaL_optinteger(L,idx,val)'
+M.define 'Int_set(name,value) lua_pushinteger(L,value); lua_setfield(L,-2,name)'
+M.define 'Int_get(var,name) lua_getfield(L,-1,name); var=lua_tointeger(L,-1); lua_pop(L,1)'
+
+M.define 'Number_init(var,idx) double var = luaL_checknumber(L,idx)'
+M.define 'Number_inito(var,idx,val) double var = luaL_optnumber(L,idx,val)'
+M.define 'NUmber_set(name,value) lua_pushnumber(L,value); lua_setfield(L,-2,name)'
+
+M.define 'Boolean_init(var,idx) int var = lua_toboolean(L,idx)'
+M.define 'Boolean_set(name,value) lua_pushboolean(L,value); lua_setfield(L,-2,name)'
+
+M.define 'Value_init(var,idx) int var = idx'
+
+M.define('lua_tests',function(get)
+ get:expecting '{'
+ local body = get:upto '}'
+ local f = io.open(M.filename..'.lua','w')
+ f:write(tostring(body))
+ f:close()
+end)
+
+------ class support ----------------------
+
+local klass_ctor = "static void $(klass)_ctor(lua_State *L, $(klass) *this, $(fargs))"
+
+local begin_klass = [[
+
+typedef struct {
+ $(fields)
+} $(klass);
+
+define_ $(klass)_init(var,idx) $(klass) *var = $(klass)_arg(L,idx)
+
+#define $(klass)_MT "$(klass)"
+
+$(klass) * $(klass)_arg(lua_State *L,int idx) {
+ $(klass) *this = ($(klass) *)luaL_checkudata(L,idx,$(klass)_MT);
+ luaL_argcheck(L, this != NULL, idx, "$(klass) expected");
+ return this;
+}
+
+$(ctor);
+
+static void push_new_$(klass)(lua_State *L,$(fargs)) {
+ $(klass) *this = ($(klass) *)lua_newuserdata(L,sizeof($(klass)));
+ luaL_getmetatable(L,$(klass)_MT);
+ lua_setmetatable(L,-2);
+ $(klass)_ctor(L,this,$(aargs));
+}
+
+]]
+
+local end_klass = [[
+
+static const struct luaL_reg $(klass)_methods [] = {
+ $(methods)
+ {NULL, NULL} /* sentinel */
+};
+
+static void $(klass)_register (lua_State *L) {
+ luaL_newmetatable(L,$(klass)_MT);
+ luaL_register(L,NULL,$(klass)_methods);
+ lua_pushvalue(L,-1);
+ lua_setfield(L,-2,"__index");
+ lua_pop(L,1);
+}
+]]
+
+M.define('class',function(get)
+ local name = get:iden()
+ get:expecting '{'
+ local fields = get:upto (function(t,v)
+ return t == 'iden' and v == 'constructor'
+ end)
+ get:expecting '('
+ local out = {}
+ local args = get:list()
+ local f_args = args:strip_spaces()
+ local a_args = f_args:pick(2)
+ f_args = table.concat(args:__tostring(),',')
+ a_args = table.concat(a_args,',')
+ local subst = {klass=name,fields=tostring(fields),fargs=f_args,aargs=a_args }
+ local proto = dollar_subst(klass_ctor,subst)
+ subst.ctor = proto
+ append(out,dollar_subst(begin_klass,subst))
+ append(out,proto)
+ local pp = {{'iden',name},{'iden','this'}}
+ push_brace_stack{
+ names = {}, cnames = {}, ns = name, cname = name,
+ massage_arg = function(args)
+ table.insert(args,1,pp)
+ end,
+ handler = function(get,put)
+ append(module.finalizers,name.."_register")
+ local methods = register_functions(btop.names,btop.cnames)
+ return dollar_subst(end_klass,{methods=methods,klass=name,fargs=f_args,aargs=a_args})
+ end
+ }
+ return table.concat(out,'\n')
+end)
+