-- tolua: container abstract class -- Written by Waldemar Celes -- TeCGraf/PUC-Rio -- Jul 1998 -- $Id: $ -- This code is free software; you can redistribute it and/or modify it. -- The software provided hereunder is on an "as is" basis, and -- the author has no obligation to provide maintenance, support, updates, -- enhancements, or modifications. -- table to store namespaced typedefs/enums in global scope global_typedefs = {} global_enums = {} -- Container class -- Represents a container of features to be bound -- to lua. classContainer = { curr = nil, } classContainer.__index = classContainer setmetatable(classContainer,classFeature) -- output tags function classContainer:decltype () push(self) local i=1 while self[i] do self[i]:decltype() i = i+1 end pop() end -- write support code function classContainer:supcode () if not self:check_public_access() then return end push(self) local i=1 while self[i] do if self[i]:check_public_access() then self[i]:supcode() end i = i+1 end pop() end function classContainer:hasvar () local i=1 while self[i] do if self[i]:isvariable() then return 1 end i = i+1 end return 0 end -- Internal container constructor function _Container (self) setmetatable(self,classContainer) self.n = 0 self.typedefs = {tolua_n=0} self.usertypes = {} self.enums = {tolua_n=0} self.lnames = {} return self end -- push container function push (t) t.prox = classContainer.curr classContainer.curr = t end -- pop container function pop () --print("name",classContainer.curr.name) --foreach(classContainer.curr.usertypes,print) --print("______________") classContainer.curr = classContainer.curr.prox end -- get current namespace function getcurrnamespace () return getnamespace(classContainer.curr) end -- append to current container function append (t) return classContainer.curr:append(t) end -- append typedef to current container function appendtypedef (t) return classContainer.curr:appendtypedef(t) end -- append usertype to current container function appendusertype (t) return classContainer.curr:appendusertype(t) end -- append enum to current container function appendenum (t) return classContainer.curr:appendenum(t) end -- substitute typedef function applytypedef (mod,type) return classContainer.curr:applytypedef(mod,type) end -- check if is type function findtype (type) local t = classContainer.curr:findtype(type) return t end -- check if is typedef function istypedef (type) return classContainer.curr:istypedef(type) end -- get fulltype (with namespace) function fulltype (t) local curr = classContainer.curr while curr do if curr then if curr.typedefs and curr.typedefs[t] then return curr.typedefs[t] elseif curr.usertypes and curr.usertypes[t] then return curr.usertypes[t] end end curr = curr.prox end return t end -- checks if it requires collection function classContainer:requirecollection (t) push(self) local i=1 local r = false while self[i] do r = self[i]:requirecollection(t) or r i = i+1 end pop() return r end -- get namesapce function getnamespace (curr) local namespace = '' while curr do if curr and ( curr.classtype == 'class' or curr.classtype == 'namespace') then namespace = (curr.original_name or curr.name) .. '::' .. namespace --namespace = curr.name .. '::' .. namespace end curr = curr.prox end return namespace end -- get namespace (only namespace) function getonlynamespace () local curr = classContainer.curr local namespace = '' while curr do if curr.classtype == 'class' then return namespace elseif curr.classtype == 'namespace' then namespace = curr.name .. '::' .. namespace end curr = curr.prox end return namespace end -- check if is enum function isenum (type) return classContainer.curr:isenum(type) end -- append feature to container function classContainer:append (t) self.n = self.n + 1 self[self.n] = t t.parent = self end -- append typedef function classContainer:appendtypedef (t) local namespace = getnamespace(classContainer.curr) self.typedefs.tolua_n = self.typedefs.tolua_n + 1 self.typedefs[self.typedefs.tolua_n] = t self.typedefs[t.utype] = namespace .. t.utype global_typedefs[namespace..t.utype] = t t.ftype = findtype(t.type) or t.type --print("appending typedef "..t.utype.." as "..namespace..t.utype.." with ftype "..t.ftype) append_global_type(namespace..t.utype) if t.ftype and isenum(t.ftype) then global_enums[namespace..t.utype] = true end end -- append usertype: return full type function classContainer:appendusertype (t) local container if t == (self.original_name or self.name) then container = self.prox else container = self end local ft = getnamespace(container) .. t container.usertypes[t] = ft _usertype[ft] = ft return ft end -- append enum function classContainer:appendenum (t) local namespace = getnamespace(classContainer.curr) self.enums.tolua_n = self.enums.tolua_n + 1 self.enums[self.enums.tolua_n] = t global_enums[namespace..t.name] = t end -- determine lua function name overload function classContainer:overload (lname) if not self.lnames[lname] then self.lnames[lname] = 0 else self.lnames[lname] = self.lnames[lname] + 1 end return format("%02d",self.lnames[lname]) end -- applies typedef: returns the 'the facto' modifier and type function classContainer:applytypedef (mod,type) if global_typedefs[type] then --print("found typedef "..global_typedefs[type].type) local mod1, type1 = global_typedefs[type].mod, global_typedefs[type].ftype local mod2, type2 = applytypedef(mod.." "..mod1, type1) --return mod2 .. ' ' .. mod1, type2 return mod2, type2 end do return mod,type end end -- check if it is a typedef function classContainer:istypedef (type) local env = self while env do if env.typedefs then local i=1 while env.typedefs[i] do if env.typedefs[i].utype == type then return type end i = i+1 end end env = env.parent end return nil end function find_enum_var(var) if tonumber(var) then return var end local c = classContainer.curr while c do local ns = getnamespace(c) for k,v in pairs(_global_enums) do if match_type(var, v, ns) then return v end end if c.base and c.base ~= '' then c = _global_classes[c:findtype(c.base)] else c = nil end end return var end -- check if is a registered type: return full type or nil function classContainer:findtype (t) t = string.gsub(t, "=.*", "") if _basic[t] then return t end local _,_,em = string.find(t, "([&%*])%s*$") t = string.gsub(t, "%s*([&%*])%s*$", "") p = self while p and type(p)=='table' do local st = getnamespace(p) for i=_global_types.n,1,-1 do -- in reverse order if match_type(t, _global_types[i], st) then return _global_types[i]..(em or "") end end if p.base and p.base ~= '' and p.base ~= t then --print("type is "..t..", p is "..p.base.." self.type is "..self.type.." self.name is "..self.name) p = _global_classes[p:findtype(p.base)] else p = nil end end return nil end function append_global_type(t, class) _global_types.n = _global_types.n +1 _global_types[_global_types.n] = t _global_types_hash[t] = 1 if class then append_class_type(t, class) end end function append_class_type(t,class) if _global_classes[t] then class.flags = _global_classes[t].flags class.lnames = _global_classes[t].lnames if _global_classes[t].base and (_global_classes[t].base ~= '') then class.base = _global_classes[t].base or class.base end end _global_classes[t] = class class.flags = class.flags or {} end function match_type(childtype, regtype, st) --print("findtype "..childtype..", "..regtype..", "..st) local b,e = string.find(regtype, childtype, -string.len(childtype), true) if b then if e == string.len(regtype) and (b == 1 or (string.sub(regtype, b-1, b-1) == ':' and string.sub(regtype, 1, b-1) == string.sub(st, 1, b-1))) then return true end end return false end function findtype_on_childs(self, t) local tchild if self.classtype == 'class' or self.classtype == 'namespace' then for k,v in ipairs(self) do if v.classtype == 'class' or v.classtype == 'namespace' then if v.typedefs and v.typedefs[t] then return v.typedefs[t] elseif v.usertypes and v.usertypes[t] then return v.usertypes[t] end tchild = findtype_on_childs(v, t) if tchild then return tchild end end end end return nil end function classContainer:isenum (type) if global_enums[type] then return type else return false end local basetype = gsub(type,"^.*::","") local env = self while env do if env.enums then local i=1 while env.enums[i] do if env.enums[i].name == basetype then return true end i = i+1 end end env = env.parent end return false end methodisvirtual = false -- a global -- parse chunk function classContainer:doparse (s) --print ("parse "..s) -- try the parser hook do local sub = parser_hook(s) if sub then return sub end end -- try the null statement do local b,e,code = string.find(s, "^%s*;") if b then return strsub(s,e+1) end end -- try empty verbatim line do local b,e,code = string.find(s, "^%s*$\n") if b then return strsub(s,e+1) end end -- try Lua code do local b,e,code = strfind(s,"^%s*(%b\1\2)") if b then Code(strsub(code,2,-2)) return strsub(s,e+1) end end -- try C code do local b,e,code = strfind(s,"^%s*(%b\3\4)") if b then code = '{'..strsub(code,2,-2)..'\n}\n' Verbatim(code,'r') -- verbatim code for 'r'egister fragment return strsub(s,e+1) end end -- try C code for preamble section do local b,e,code = string.find(s, "^%s*(%b\5\6)") if b then code = string.sub(code, 2, -2).."\n" Verbatim(code, '') return string.sub(s, e+1) end end -- try default_property directive do local b,e,ptype = strfind(s, "^%s*TOLUA_PROPERTY_TYPE%s*%(+%s*([^%)%s]*)%s*%)+%s*;?") if b then if not ptype or ptype == "" then ptype = "default" end self:set_property_type(ptype) return strsub(s, e+1) end end -- try protected_destructor directive do local b,e = string.find(s, "^%s*TOLUA_PROTECTED_DESTRUCTOR%s*;?") if b then if self.set_protected_destructor then self:set_protected_destructor(true) end return strsub(s, e+1) end end -- try 'extern' keyword do local b,e = string.find(s, "^%s*extern%s+") if b then -- do nothing return strsub(s, e+1) end end -- try 'virtual' keyworkd do local b,e = string.find(s, "^%s*virtual%s+") if b then methodisvirtual = true return strsub(s, e+1) end end -- try labels (public, private, etc) do local b,e = string.find(s, "^%s*%w*%s*:[^:]") if b then return strsub(s, e) -- preserve the [^:] end end -- try module do local b,e,name,body = strfind(s,"^%s*module%s%s*([_%w][_%w]*)%s*(%b{})%s*") if b then _curr_code = strsub(s,b,e) Module(name,body) return strsub(s,e+1) end end -- try namesapce do local b,e,name,body = strfind(s,"^%s*namespace%s%s*([_%w][_%w]*)%s*(%b{})%s*;?") if b then _curr_code = strsub(s,b,e) Namespace(name,body) return strsub(s,e+1) end end -- try define do local b,e,name = strfind(s,"^%s*#define%s%s*([^%s]*)[^\n]*\n%s*") if b then _curr_code = strsub(s,b,e) Define(name) return strsub(s,e+1) end end -- try enumerates do local b,e,name,body,varname = strfind(s,"^%s*enum%s+(%S*)%s*(%b{})%s*([^%s;]*)%s*;?%s*") if b then --error("#Sorry, declaration of enums and variables on the same statement is not supported.\nDeclare your variable separately (example: '"..name.." "..varname..";')") _curr_code = strsub(s,b,e) Enumerate(name,body,varname) return strsub(s,e+1) end end -- do -- local b,e,name,body = strfind(s,"^%s*enum%s+(%S*)%s*(%b{})%s*;?%s*") -- if b then -- _curr_code = strsub(s,b,e) -- Enumerate(name,body) -- return strsub(s,e+1) -- end -- end do local b,e,body,name = strfind(s,"^%s*typedef%s+enum[^{]*(%b{})%s*([%w_][^%s]*)%s*;%s*") if b then _curr_code = strsub(s,b,e) Enumerate(name,body) return strsub(s,e+1) end end -- try operator do local b,e,decl,kind,arg,const = strfind(s,"^%s*([_%w][_%w%s%*&:<>,]-%s+operator)%s*([^%s][^%s]*)%s*(%b())%s*(c?o?n?s?t?)%s*;%s*") if not b then -- try inline b,e,decl,kind,arg,const = strfind(s,"^%s*([_%w][_%w%s%*&:<>,]-%s+operator)%s*([^%s][^%s]*)%s*(%b())%s*(c?o?n?s?t?)[%s\n]*%b{}%s*;?%s*") end if not b then -- try cast operator b,e,decl,kind,arg,const = strfind(s, "^%s*(operator)%s+([%w_:%d<>%*%&%s]+)%s*(%b())%s*(c?o?n?s?t?)"); if b then local _,ie = string.find(s, "^%s*%b{}", e+1) if ie then e = ie end end end if b then _curr_code = strsub(s,b,e) Operator(decl,kind,arg,const) return strsub(s,e+1) end end -- try function do --local b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&:<>]*[_%w])%s*(%b())%s*(c?o?n?s?t?)%s*=?%s*0?%s*;%s*") local b,e,decl,arg,const,virt = strfind(s,"^%s*([^%(\n]+)%s*(%b())%s*(c?o?n?s?t?)%s*(=?%s*0?)%s*;%s*") if not b then -- try function with template b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&:<>]*[_%w]%b<>)%s*(%b())%s*(c?o?n?s?t?)%s*=?%s*0?%s*;%s*") end if not b then -- try a single letter function name b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?)%s*;%s*") end if b then if virt and string.find(virt, "[=0]") then if self.flags then self.flags.pure_virtual = true end end _curr_code = strsub(s,b,e) Function(decl,arg,const) return strsub(s,e+1) end end -- try inline function do local b,e,decl,arg,const = strfind(s,"^%s*([^%(\n]+)%s*(%b())%s*(c?o?n?s?t?)[^;{]*%b{}%s*;?%s*") --local b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&:<>]*[_%w>])%s*(%b())%s*(c?o?n?s?t?)[^;]*%b{}%s*;?%s*") if not b then -- try a single letter function name b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?).-%b{}%s*;?%s*") end if b then _curr_code = strsub(s,b,e) Function(decl,arg,const) return strsub(s,e+1) end end -- try class do local b,e,name,base,body base = '' body = '' b,e,name = strfind(s,"^%s*class%s*([_%w][_%w@]*)%s*;") -- dummy class if not b then b,e,name = strfind(s,"^%s*struct%s*([_%w][_%w@]*)%s*;") -- dummy struct if not b then b,e,name,base,body = strfind(s,"^%s*class%s*([_%w][_%w@]*)%s*(.-)%s*(%b{})%s*;%s*") if not b then b,e,name,base,body = strfind(s,"^%s*struct%s*([_%w][_%w@]*)%s*(.-)%s*(%b{})%s*;%s*") if not b then b,e,name,base,body = strfind(s,"^%s*union%s*([_%w][_%w@]*)%s*(.-)%s*(%b{})%s*;%s*") if not b then base = '' b,e,body,name = strfind(s,"^%s*typedef%s%s*struct%s%s*[_%w]*%s*(%b{})%s*([_%w][_%w@]*)%s*;%s*") end end end end end if b then if base ~= '' then base = string.gsub(base, "^%s*:%s*", "") base = string.gsub(base, "%s*public%s*", "") base = split(base, ",") --local b,e --b,e,base = strfind(base,".-([_%w][_%w<>,:]*)$") else base = {} end _curr_code = strsub(s,b,e) Class(name,base,body) return strsub(s,e+1) end end -- try typedef do local b,e,types = strfind(s,"^%s*typedef%s%s*(.-)%s*;%s*") if b then _curr_code = strsub(s,b,e) Typedef(types) return strsub(s,e+1) end end -- try variable do local b,e,decl = strfind(s,"^%s*([_%w][_@%s%w%d%*&:<>,]*[_%w%d])%s*;%s*") if b then _curr_code = strsub(s,b,e) local list = split_c_tokens(decl, ",") Variable(list[1]) if list.n > 1 then local _,_,type = strfind(list[1], "(.-)%s+([^%s]*)$"); local i =2; while list[i] do Variable(type.." "..list[i]) i=i+1 end end --Variable(decl) return strsub(s,e+1) end end -- try string do local b,e,decl = strfind(s,"^%s*([_%w]?[_%s%w%d]-char%s+[_@%w%d]*%s*%[%s*%S+%s*%])%s*;%s*") if b then _curr_code = strsub(s,b,e) Variable(decl) return strsub(s,e+1) end end -- try array do local b,e,decl = strfind(s,"^%s*([_%w][][_@%s%w%d%*&:]*[]_%w%d])%s*;%s*") if b then _curr_code = strsub(s,b,e) Array(decl) return strsub(s,e+1) end end -- no matching if gsub(s,"%s%s*","") ~= "" then _curr_code = s error("#parse error") else return "" end end function classContainer:parse (s) self.curr_member_access = nil while s ~= '' do s = self:doparse(s) methodisvirtual = false end end -- property types function get_property_type() return classContainer.curr:get_property_type() end function classContainer:set_property_type(ptype) ptype = string.gsub(ptype, "^%s*", "") ptype = string.gsub(ptype, "%s*$", "") self.property_type = ptype end function classContainer:get_property_type() return self.property_type or (self.parent and self.parent:get_property_type()) or "default" end