You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
344 lines
9.6 KiB
Lua
344 lines
9.6 KiB
Lua
-- 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
|
|
#if LUA_VERSION_NUM > 501
|
|
#define lua_objlen lua_rawlen
|
|
#endif
|
|
]]
|
|
|
|
local finis = [[
|
|
static const luaL_Reg $(cname)_funs[] = {
|
|
$(funs)
|
|
{NULL,NULL}
|
|
};
|
|
|
|
EXPORT int luaopen_$(cname) (lua_State *L) {
|
|
#if LUA_VERSION_NUM > 501
|
|
lua_newtable(L);
|
|
luaL_setfuncs (L,$(cname)_funs,0);
|
|
lua_pushvalue(L,-1);
|
|
lua_setglobal(L,"$(cname)");
|
|
#else
|
|
luaL_register(L,"$(cname)",$(cname)_funs);
|
|
#endif
|
|
$(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:name()
|
|
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
|
|
if not arg[2] then M.error("parameter must be TYPE NAME [= VALUE]") end
|
|
append(out,indent..mac..'('..arg[2][2]..','..i..');')
|
|
end
|
|
--append(out,space)
|
|
return table.concat(out,'\n')..space
|
|
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) {'}
|
|
if not btop.finalizers then M.error("not inside a module") end
|
|
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)
|
|
|
|
local load_lua = [[
|
|
static void load_lua_code (lua_State *L) {
|
|
luaL_dostring(L,lua_code_block);
|
|
}
|
|
]]
|
|
|
|
M.define('lua',function(get)
|
|
get:expecting '{'
|
|
local block = tostring(get:upto '}')
|
|
local code_name = 'lua_code_block'
|
|
local out = {'static const char *'.. code_name .. ' = ""\\'}
|
|
for line in block:gmatch('([^\r\n]+)') do
|
|
line = line:gsub('\\','\\\\'):gsub('"','\\"')
|
|
append(out,' "'..line..'\\n"\\')
|
|
end
|
|
append(out,';')
|
|
append(out,load_lua)
|
|
out = table.concat(out,'\n')
|
|
append(module.finalizers,'load_lua_code')
|
|
return out
|
|
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 'Str_geti(var,idx) lua_rawgeti(L,-1,idx); 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 'Int_geti(var,idx) lua_rawgeti(L,-1,idx); 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 'Number_get(var,name) lua_getfield(L,-1,name); var=lua_tonumber(L,-1); lua_pop(L,1)'
|
|
M.define 'Number_geti(var,idx) lua_rawgeti(L,-1,idx); var=lua_tonumber(L,-1); lua_pop(L,1)'
|
|
|
|
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 'Boolean_get(var,name) lua_getfield(L,-1,name); var=lua_toboolean(L,-1); lua_pop(L,1)'
|
|
M.define 'Boolean_geti(var,idx) lua_rawgeti(L,-1,idx); var=lua_toboolean(L,-1); lua_pop(L,1)'
|
|
|
|
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 int 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));
|
|
return 1;
|
|
}
|
|
|
|
]]
|
|
|
|
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);
|
|
#if LUA_VERSION_NUM > 501
|
|
luaL_setfuncs(L,$(klass)_methods,0);
|
|
#else
|
|
luaL_register(L,NULL,$(klass)_methods);
|
|
#endif
|
|
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)
|
|
fields = tostring(fields):gsub('%s+$','\n')
|
|
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=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)
|
|
|