507 lines
12 KiB
Lua
507 lines
12 KiB
Lua
-- flags
|
|
local disable_virtual_hooks = false
|
|
local enable_pure_virtual = true
|
|
local default_private_access = false
|
|
|
|
local access = {public = 0, protected = 1, private = 2}
|
|
|
|
function preparse_hook(p)
|
|
|
|
if default_private_access then
|
|
-- we need to make all structs 'public' by default
|
|
p.code = string.gsub(p.code, "(struct[^;]*{)", "%1\npublic:\n")
|
|
end
|
|
end
|
|
|
|
|
|
function parser_hook(s)
|
|
|
|
local container = classContainer.curr -- get the current container
|
|
|
|
if default_private_access then
|
|
if not container.curr_member_access and container.classtype == 'class' then
|
|
-- default access for classes is private
|
|
container.curr_member_access = access.private
|
|
end
|
|
end
|
|
|
|
-- try labels (public, private, etc)
|
|
do
|
|
local b,e,label = string.find(s, "^%s*(%w*)%s*:[^:]") -- we need to check for [^:], otherwise it would match 'namespace::type'
|
|
if b then
|
|
|
|
-- found a label, get the new access value from the global 'access' table
|
|
if access[label] then
|
|
container.curr_member_access = access[label]
|
|
end -- else ?
|
|
|
|
return strsub(s, e) -- normally we would use 'e+1', but we need to preserve the [^:]
|
|
end
|
|
end
|
|
|
|
|
|
local ret = nil
|
|
|
|
if disable_virtual_hooks then
|
|
|
|
return ret
|
|
end
|
|
|
|
local b,e,decl,arg = string.find(s, "^%s*virtual%s+([^%({~]+)(%b())")
|
|
local const
|
|
if b then
|
|
local ret = string.sub(s, e+1)
|
|
if string.find(ret, "^%s*const") then
|
|
const = "const"
|
|
ret = string.gsub(ret, "^%s*const", "")
|
|
end
|
|
local purev = false
|
|
if string.find(ret, "^%s*=%s*0") then
|
|
purev = true
|
|
ret = string.gsub(ret, "^%s*=%s*0", "")
|
|
end
|
|
ret = string.gsub(ret, "^%s*%b{}", "")
|
|
|
|
local func = Function(decl, arg, const)
|
|
func.pure_virtual = purev
|
|
--func.access = access
|
|
func.original_sig = decl
|
|
|
|
local curflags = classContainer.curr.flags
|
|
if not curflags.virtual_class then
|
|
|
|
curflags.virtual_class = VirtualClass()
|
|
end
|
|
curflags.virtual_class:add(func)
|
|
curflags.pure_virtual = curflags.pure_virtual or purev
|
|
|
|
return ret
|
|
end
|
|
|
|
return ret
|
|
end
|
|
|
|
|
|
-- class VirtualClass
|
|
classVirtualClass = {
|
|
classtype = 'class',
|
|
name = '',
|
|
base = '',
|
|
type = '',
|
|
btype = '',
|
|
ctype = '',
|
|
}
|
|
classVirtualClass.__index = classVirtualClass
|
|
setmetatable(classVirtualClass,classClass)
|
|
|
|
function classVirtualClass:add(f)
|
|
|
|
local parent = classContainer.curr
|
|
pop()
|
|
|
|
table.insert(self.methods, {f=f})
|
|
|
|
local name,sig
|
|
|
|
-- doble negative means positive
|
|
if f.name == 'new' and ((not self.flags.parent_object.flags.pure_virtual) or (enable_pure_virtual)) then
|
|
|
|
name = self.original_name
|
|
elseif f.name == 'delete' then
|
|
name = '~'..self.original_name
|
|
else
|
|
if f.access ~= 2 and (not f.pure_virtual) and f.name ~= 'new' and f.name ~= 'delete' then
|
|
name = f.mod.." "..f.type..f.ptr.." "..self.flags.parent_object.lname.."__"..f.name
|
|
end
|
|
end
|
|
|
|
if name then
|
|
sig = name..self:get_arg_list(f, true)..";\n"
|
|
push(self)
|
|
sig = preprocess(sig)
|
|
self:parse(sig)
|
|
pop()
|
|
end
|
|
|
|
push(parent)
|
|
end
|
|
|
|
function preprocess(sig)
|
|
|
|
sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
|
|
sig = gsub(sig,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*'
|
|
sig = gsub(sig,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*'
|
|
sig = gsub(sig,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*'
|
|
|
|
return sig
|
|
end
|
|
|
|
function classVirtualClass:get_arg_list(f, decl)
|
|
|
|
local ret = ""
|
|
local sep = ""
|
|
local i=1
|
|
while f.args[i] do
|
|
|
|
local arg = f.args[i]
|
|
if decl then
|
|
local ptr
|
|
if arg.ret ~= '' then
|
|
ptr = arg.ret
|
|
else
|
|
ptr = arg.ptr
|
|
end
|
|
local def = ""
|
|
if arg.def and arg.def ~= "" then
|
|
|
|
def = " = "..arg.def
|
|
end
|
|
ret = ret..sep..arg.mod.." "..arg.type..ptr.." "..arg.name..def
|
|
else
|
|
ret = ret..sep..arg.name
|
|
end
|
|
|
|
sep = ","
|
|
i = i+1
|
|
end
|
|
|
|
return "("..ret..")"
|
|
end
|
|
|
|
function classVirtualClass:add_parent_virtual_methods(parent)
|
|
|
|
parent = parent or _global_classes[self.flags.parent_object.btype]
|
|
|
|
if not parent then return end
|
|
|
|
if parent.flags.virtual_class then
|
|
|
|
local vclass = parent.flags.virtual_class
|
|
for k,v in ipairs(vclass.methods) do
|
|
if v.f.name ~= 'new' and v.f.name ~= 'delete' and (not self:has_method(v.f)) then
|
|
table.insert(self.methods, {f=v.f})
|
|
end
|
|
end
|
|
end
|
|
|
|
parent = _global_classes[parent.btype]
|
|
if parent then
|
|
self:add_parent_virtual_methods(parent)
|
|
end
|
|
end
|
|
|
|
function classVirtualClass:has_method(f)
|
|
|
|
for k,v in pairs(self.methods) do
|
|
-- just match name for now
|
|
if v.f.name == f.name then
|
|
return true
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function classVirtualClass:add_constructors()
|
|
|
|
local i=1
|
|
while self.flags.parent_object[i] do
|
|
|
|
local v = self.flags.parent_object[i]
|
|
if getmetatable(v) == classFunction and (v.name == 'new' or v.name == 'delete') then
|
|
|
|
self:add(v)
|
|
end
|
|
|
|
i = i+1
|
|
end
|
|
|
|
end
|
|
|
|
--[[
|
|
function classVirtualClass:requirecollection(t)
|
|
|
|
self:add_constructors()
|
|
local req = classClass.requirecollection(self, t)
|
|
if req then
|
|
output('class ',self.name,";")
|
|
end
|
|
return req
|
|
end
|
|
--]]
|
|
|
|
function classVirtualClass:supcode()
|
|
|
|
-- pure virtual classes can have no default constructors on gcc 4
|
|
|
|
if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
|
|
output('#if (__GNUC__ == 4) || (__GNUC__ > 4 ) // I hope this works on Microsoft Visual studio .net server 2003 XP Compiler\n')
|
|
end
|
|
|
|
local ns
|
|
if self.prox.classtype == 'namespace' then
|
|
output('namespace ',self.prox.name, " {")
|
|
ns = true
|
|
end
|
|
|
|
output("class "..self.original_name.." : public "..self.btype..", public ToluaBase {")
|
|
|
|
output("public:\n")
|
|
|
|
self:add_parent_virtual_methods()
|
|
|
|
self:output_methods(self.btype)
|
|
self:output_parent_methods()
|
|
|
|
self:add_constructors()
|
|
|
|
-- no constructor for pure virtual classes
|
|
if (not self.flags.parent_object.flags.pure_virtual) or enable_pure_virtual then
|
|
|
|
self:output_constructors()
|
|
end
|
|
|
|
output("};\n\n")
|
|
|
|
if ns then
|
|
output("};")
|
|
end
|
|
|
|
classClass.supcode(self)
|
|
|
|
if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
|
|
output('#endif // __GNUC__ >= 4\n')
|
|
end
|
|
|
|
-- output collector for custom class if required
|
|
if self:requirecollection(_collect) and _collect[self.type] then
|
|
|
|
output('\n')
|
|
output('/* function to release collected object via destructor */')
|
|
output('#ifdef __cplusplus\n')
|
|
--for i,v in pairs(collect) do
|
|
i,v = self.type, _collect[self.type]
|
|
output('\nstatic int '..v..' (lua_State* tolua_S)')
|
|
output('{')
|
|
output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);')
|
|
output(' delete self;')
|
|
output(' return 0;')
|
|
output('}')
|
|
--end
|
|
output('#endif\n\n')
|
|
end
|
|
|
|
end
|
|
|
|
function classVirtualClass:register(pre)
|
|
|
|
-- pure virtual classes can have no default constructors on gcc 4
|
|
if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
|
|
output('#if (__GNUC__ == 4) || (__GNUC__ > 4 )\n')
|
|
end
|
|
|
|
classClass.register(self, pre)
|
|
|
|
if self.flags.parent_object.flags.pure_virtual and not enable_pure_virtual then
|
|
output('#endif // __GNUC__ >= 4\n')
|
|
end
|
|
end
|
|
|
|
|
|
--function classVirtualClass:requirecollection(_c)
|
|
-- if self.flags.parent_object.flags.pure_virtual then
|
|
-- return false
|
|
-- end
|
|
-- return classClass.requirecollection(self, _c)
|
|
--end
|
|
|
|
function classVirtualClass:output_parent_methods()
|
|
|
|
for k,v in ipairs(self.methods) do
|
|
|
|
if v.f.access ~= 2 and (not v.f.pure_virtual) and v.f.name ~= 'new' and v.f.name ~= 'delete' then
|
|
|
|
local rettype = v.f.mod.." "..v.f.type..v.f.ptr.." "
|
|
local parent_name = rettype..self.btype.."__"..v.f.name
|
|
|
|
local par_list = self:get_arg_list(v.f, true)
|
|
local var_list = self:get_arg_list(v.f, false)
|
|
|
|
-- the parent's virtual function
|
|
output("\t"..parent_name..par_list.." {")
|
|
|
|
output("\t\treturn (",rettype,")"..self.btype.."::"..v.f.name..var_list..";")
|
|
output("\t};")
|
|
end
|
|
end
|
|
end
|
|
|
|
function classVirtualClass:output_methods(btype)
|
|
|
|
for k,v in ipairs(self.methods) do
|
|
|
|
if v.f.name ~= 'new' and v.f.name ~= 'delete' then
|
|
|
|
self:output_method(v.f, btype)
|
|
end
|
|
end
|
|
output("\n")
|
|
end
|
|
|
|
function classVirtualClass:output_constructors()
|
|
|
|
for k,v in ipairs(self.methods) do
|
|
|
|
if v.f.name == 'new' then
|
|
|
|
local par_list = self:get_arg_list(v.f, true)
|
|
local var_list = self:get_arg_list(v.f, false)
|
|
|
|
output("\t",self.original_name,par_list,":",self.btype,var_list,"{};")
|
|
end
|
|
end
|
|
end
|
|
|
|
function classVirtualClass:output_method(f, btype)
|
|
|
|
if f.access == 2 then -- private
|
|
return
|
|
end
|
|
|
|
local ptr
|
|
if f.ret ~= '' then
|
|
ptr = f.ret
|
|
else
|
|
ptr = f.ptr
|
|
end
|
|
|
|
local rettype = f.mod.." "..f.type..f.ptr.." "
|
|
local par_list = self:get_arg_list(f, true)
|
|
local var_list = self:get_arg_list(f, false)
|
|
|
|
if string.find(rettype, "%s*LuaQtGenericFlags%s*") then
|
|
|
|
_,_,rettype = string.find(f.original_sig, "^%s*([^%s]+)%s+")
|
|
end
|
|
|
|
-- the caller of the lua method
|
|
output("\t"..rettype.." "..f.name..par_list..f.const.." {")
|
|
local fn = f.cname
|
|
if f.access == 1 then
|
|
fn = "NULL"
|
|
end
|
|
output('\t\tif (push_method("',f.lname,'", ',fn,')) {')
|
|
|
|
--if f.type ~= 'void' then
|
|
-- output("\t\t\tint top = lua_gettop(lua_state)-1;")
|
|
--end
|
|
|
|
-- push the parameters
|
|
local argn = 0
|
|
for i,arg in ipairs(f.args) do
|
|
if arg.type ~= 'void' then
|
|
local t,ct = isbasic(arg.type)
|
|
if t and t ~= '' then
|
|
if arg.ret == "*" then
|
|
t = 'userdata'
|
|
ct = 'void*'
|
|
end
|
|
output("\t\t\ttolua_push"..t.."(lua_state, ("..ct..")"..arg.name..");");
|
|
else
|
|
local m = arg.ptr
|
|
if m and m~= "" then
|
|
if m == "*" then m = "" end
|
|
output("\t\t\ttolua_pushusertype(lua_state, (void*)"..m..arg.name..", \""..arg.type.."\");")
|
|
else
|
|
output("\t\t\tvoid* tolua_obj = (void*)new "..arg.type.."("..arg.name..");\n")
|
|
output('\t\t\ttolua_pushusertype_and_takeownership(lua_state, tolua_obj, "'..arg.type..'");\n')
|
|
end
|
|
end
|
|
argn = argn+1
|
|
end
|
|
end
|
|
|
|
-- call the function
|
|
output("\t\t\tToluaBase::dbcall(lua_state, ",argn+1,", ")
|
|
|
|
-- return value
|
|
if f.type ~= 'void' then
|
|
output("1);")
|
|
|
|
local t,ct = isbasic(f.type)
|
|
if t and t ~= '' then
|
|
--output("\t\t\treturn ("..rettype..")tolua_to"..t.."(lua_state, top, 0);")
|
|
output("\t\t\t",rettype,"tolua_ret = ("..rettype..")tolua_to"..t.."(lua_state, -1, 0);")
|
|
else
|
|
|
|
local mod = ""
|
|
if f.ptr ~= "*" then
|
|
mod = "*("..f.type.."*)"
|
|
end
|
|
|
|
--output("\t\t\treturn ("..rettype..")"..mod.."tolua_tousertype(lua_state, top, 0);")
|
|
output("\t\t\t",rettype,"tolua_ret = ("..rettype..")"..mod.."tolua_tousertype(lua_state, -1, 0);")
|
|
end
|
|
output("\t\t\tlua_pop(lua_state, 1);")
|
|
output("\t\t\treturn tolua_ret;")
|
|
else
|
|
output("0);")
|
|
end
|
|
|
|
-- handle non-implemeted function
|
|
output("\t\t} else {")
|
|
|
|
if f.pure_virtual then
|
|
|
|
output('\t\t\tif (lua_state)')
|
|
--output('\t\t\t\ttolua_error(lua_state, "pure-virtual method '..btype.."::"..f.name..' not implemented.", NULL);')
|
|
output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' not implemented.");')
|
|
output('\t\t\telse {')
|
|
output('\t\t\t\tLOG("pure-virtual method '..btype.."::"..f.name..' called with no lua_state. Aborting");')
|
|
output('\t\t\t\t::abort();')
|
|
output('\t\t\t};')
|
|
if( rettype == " std::string " ) then
|
|
output('\t\t\treturn "";')
|
|
else
|
|
output('\t\t\treturn (',rettype,')0;')
|
|
end
|
|
else
|
|
|
|
output('\t\t\treturn (',rettype,')',btype,'::',f.name,var_list,';')
|
|
end
|
|
|
|
output("\t\t};")
|
|
|
|
output("\t};")
|
|
end
|
|
|
|
function VirtualClass()
|
|
|
|
local parent = classContainer.curr
|
|
pop()
|
|
|
|
local name = "Lua__"..parent.original_name
|
|
|
|
local c = _Class(_Container{name=name, base=parent.name, extra_bases=nil})
|
|
setmetatable(c, classVirtualClass)
|
|
|
|
local ft = getnamespace(c.parent)..c.original_name
|
|
append_global_type(ft, c)
|
|
|
|
push(parent)
|
|
|
|
c.flags.parent_object = parent
|
|
c.methods = {}
|
|
|
|
push(c)
|
|
c:parse("\nvoid tolua__set_instance(_lstate L, lua_Object lo);\n")
|
|
pop()
|
|
|
|
return c
|
|
end
|
|
|
|
|
|
|
|
|
|
|