1-- tolua: package class 2-- Written by Waldemar Celes 3-- TeCGraf/PUC-Rio 4-- Jul 1998 5-- $Id: $ 6 7-- This code is free software; you can redistribute it and/or modify it. 8-- The software provided hereunder is on an "as is" basis, and 9-- the author has no obligation to provide maintenance, support, updates, 10-- enhancements, or modifications. 11 12 13 14-- Package class 15-- Represents the whole package being bound. 16-- The following fields are stored: 17-- {i} = list of objects in the package. 18classPackage = { 19 classtype = 'package' 20} 21classPackage.__index = classPackage 22setmetatable(classPackage,classContainer) 23 24-- Print method 25function classPackage:print () 26 print("Package: "..self.name) 27 local i=1 28 while self[i] do 29 self[i]:print("","") 30 i = i+1 31 end 32end 33 34function classPackage:preprocess () 35 36 -- avoid preprocessing embedded Lua code 37 local L = {} 38 self.code = gsub(self.code,"\n%s*%$%[","\1") -- deal with embedded lua code 39 self.code = gsub(self.code,"\n%s*%$%]","\2") 40 self.code = gsub(self.code,"(%b\1\2)", function (c) 41 tinsert(L,c) 42 return "\n#["..getn(L).."]#" 43 end) 44 -- avoid preprocessing embedded C code 45 local C = {} 46 self.code = gsub(self.code,"\n%s*%$%<","\3") -- deal with embedded C code 47 self.code = gsub(self.code,"\n%s*%$%>","\4") 48 self.code = gsub(self.code,"(%b\3\4)", function (c) 49 tinsert(C,c) 50 return "\n#<"..getn(C)..">#" 51 end) 52 -- avoid preprocessing embedded C code 53 self.code = gsub(self.code,"\n%s*%$%{","\5") -- deal with embedded C code 54 self.code = gsub(self.code,"\n%s*%$%}","\6") 55 self.code = gsub(self.code,"(%b\5\6)", function (c) 56 tinsert(C,c) 57 return "\n#<"..getn(C)..">#" 58 end) 59 60 --self.code = gsub(self.code,"\n%s*#[^d][^\n]*\n", "\n\n") -- eliminate preprocessor directives that don't start with 'd' 61 self.code = gsub(self.code,"\n[ \t]*#[ \t]*[^d%<%[]", "\n//") -- eliminate preprocessor directives that don't start with 'd' 62 63 -- avoid preprocessing verbatim lines 64 local V = {} 65 self.code = gsub(self.code,"\n(%s*%$[^%[%]][^\n]*)",function (v) 66 tinsert(V,v) 67 return "\n#"..getn(V).."#" 68 end) 69 70 -- perform global substitution 71 72 self.code = gsub(self.code,"(//[^\n]*)","") -- eliminate C++ comments 73 self.code = gsub(self.code,"/%*","\1") 74 self.code = gsub(self.code,"%*/","\2") 75 self.code = gsub(self.code,"%b\1\2","") 76 self.code = gsub(self.code,"\1","/%*") 77 self.code = gsub(self.code,"\2","%*/") 78 self.code = gsub(self.code,"%s*@%s*","@") -- eliminate spaces beside @ 79 self.code = gsub(self.code,"%s?inline(%s)","%1") -- eliminate 'inline' keyword 80 --self.code = gsub(self.code,"%s?extern(%s)","%1") -- eliminate 'extern' keyword 81 --self.code = gsub(self.code,"%s?virtual(%s)","%1") -- eliminate 'virtual' keyword 82 --self.code = gsub(self.code,"public:","") -- eliminate 'public:' keyword 83 self.code = gsub(self.code,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' 84 self.code = gsub(self.code,"([^%w_])void%s*%*","%1_userdata ") -- substitute 'void*' 85 self.code = gsub(self.code,"([^%w_])char%s*%*","%1_cstring ") -- substitute 'char*' 86 self.code = gsub(self.code,"([^%w_])lua_State%s*%*","%1_lstate ") -- substitute 'lua_State*' 87 88 -- restore embedded Lua code 89 self.code = gsub(self.code,"%#%[(%d+)%]%#",function (n) 90 return L[tonumber(n)] 91 end) 92 -- restore embedded C code 93 self.code = gsub(self.code,"%#%<(%d+)%>%#",function (n) 94 return C[tonumber(n)] 95 end) 96 -- restore verbatim lines 97 self.code = gsub(self.code,"%#(%d+)%#",function (n) 98 return V[tonumber(n)] 99 end) 100 101 self.code = string.gsub(self.code, "\n%s*%$([^\n]+)", function (l) 102 Verbatim(l.."\n") 103 return "\n" 104 end) 105end 106 107-- translate verbatim 108function classPackage:preamble () 109 output('/*\n') 110 output('** Lua binding: '..self.name..'\n') 111 output('** Generated automatically by '..TOLUA_VERSION..' on '..date()..'.\n') 112 output('*/\n\n') 113 114 output('#ifndef __cplusplus\n') 115 output('#include "stdlib.h"\n') 116 output('#endif\n') 117 output('#include "string.h"\n\n') 118 output('#include "tolua++.h"\n\n') 119 120 if not flags.h then 121 output('/* Exported function */') 122 output('TOLUA_API int tolua_'..self.name..'_open (lua_State* tolua_S);') 123 output('\n') 124 end 125 126 local i=1 127 while self[i] do 128 self[i]:preamble() 129 i = i+1 130 end 131 132 if self:requirecollection(_collect) then 133 output('\n') 134 output('/* function to release collected object via destructor */') 135 output('#ifdef __cplusplus\n') 136 for i,v in pairs(_collect) do 137 output('\nstatic int '..v..' (lua_State* tolua_S)') 138 output('{') 139 output(' '..i..'* self = ('..i..'*) tolua_tousertype(tolua_S,1,0);') 140 output(' Mtolua_delete(self);') 141 output(' return 0;') 142 output('}') 143 end 144 output('#endif\n\n') 145 end 146 147 output('\n') 148 output('/* function to register type */') 149 output('static void tolua_reg_types (lua_State* tolua_S)') 150 output('{') 151 152 if flags.t then 153 output("#ifndef Mtolua_typeid\n#define Mtolua_typeid(L,TI,T)\n#endif\n") 154 end 155 foreach(_usertype,function(n,v) 156 if (not _global_classes[v]) or _global_classes[v]:check_public_access() then 157 output(' tolua_usertype(tolua_S,"',v,'");') 158 if flags.t then 159 output(' Mtolua_typeid(tolua_S,typeid(',v,'), "',v,'");') 160 end 161 end 162 end) 163 output('}') 164 output('\n') 165end 166 167-- register package 168-- write package open function 169function classPackage:register (pre) 170 pre = pre or '' 171 push(self) 172 output(pre.."/* Open function */") 173 output(pre.."TOLUA_API int tolua_"..self.name.."_open (lua_State* tolua_S)") 174 output(pre.."{") 175 output(pre.." tolua_open(tolua_S);") 176 output(pre.." tolua_reg_types(tolua_S);") 177 output(pre.." tolua_module(tolua_S,NULL,",self:hasvar(),");") 178 output(pre.." tolua_beginmodule(tolua_S,NULL);") 179 local i=1 180 while self[i] do 181 self[i]:register(pre.." ") 182 i = i+1 183 end 184 output(pre.." tolua_endmodule(tolua_S);") 185 output(pre.." return 1;") 186 output(pre.."}") 187 188 output("\n\n") 189 output("#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM >= 501\n"); 190 output(pre.."TOLUA_API int luaopen_"..self.name.." (lua_State* tolua_S) {") 191 output(pre.." return tolua_"..self.name.."_open(tolua_S);") 192 output(pre.."};") 193 output("#endif\n\n") 194 195 pop() 196end 197 198-- write header file 199function classPackage:header () 200 output('/*\n') output('** Lua binding: '..self.name..'\n') 201 output('** Generated automatically by '..TOLUA_VERSION..' on '..date()..'.\n') 202 output('*/\n\n') 203 204 if not flags.h then 205 output('/* Exported function */') 206 output('TOLUA_API int tolua_'..self.name..'_open (lua_State* tolua_S);') 207 output('\n') 208 end 209end 210 211-- Internal constructor 212function _Package (self) 213 setmetatable(self,classPackage) 214 return self 215end 216 217-- Parse C header file with tolua directives 218-- *** Thanks to Ariel Manzur for fixing bugs in nested directives *** 219function extract_code(fn,s) 220 local code = '\n$#include "'..fn..'"\n' 221 s= "\n" .. s .. "\n" -- add blank lines as sentinels 222 local _,e,c,t = strfind(s, "\n([^\n]-)[Tt][Oo][Ll][Uu][Aa]_([^%s]*)[^\n]*\n") 223 while e do 224 t = strlower(t) 225 if t == "begin" then 226 _,e,c = strfind(s,"(.-)\n[^\n]*[Tt][Oo][Ll][Uu][Aa]_[Ee][Nn][Dd][^\n]*\n",e) 227 if not e then 228 tolua_error("Unbalanced 'tolua_begin' directive in header file") 229 end 230 end 231 code = code .. c .. "\n" 232 _,e,c,t = strfind(s, "\n([^\n]-)[Tt][Oo][Ll][Uu][Aa]_([^%s]*)[^\n]*\n",e) 233 end 234 return code 235end 236 237-- Constructor 238-- Expects the package name, the file extension, and the file text. 239function Package (name,fn) 240 local ext = "pkg" 241 242 -- open input file, if any 243 local st,msg 244 if fn then 245 st, msg = readfrom(flags.f) 246 if not st then 247 error('#'..msg) 248 end 249 local _; _, _, ext = strfind(fn,".*%.(.*)$") 250 end 251 local code 252 if ext == 'pkg' then 253 code = prep(st) 254 else 255 code = "\n" .. read('*a') 256 if ext == 'h' or ext == 'hpp' then 257 code = extract_code(fn,code) 258 end 259 end 260 261 -- close file 262 if fn then 263 readfrom() 264 end 265 266 -- deal with include directive 267 local nsubst 268 repeat 269 code,nsubst = gsub(code,'\n%s*%$(.)file%s*"(.-)"([^\n]*)\n', 270 function (kind,fn,extra) 271 local _, _, ext = strfind(fn,".*%.(.*)$") 272 local fp,msg = openfile(fn,'r') 273 if not fp then 274 error('#'..msg..': '..fn) 275 end 276 if kind == 'p' then 277 local s = prep(fp) 278 closefile(fp) 279 return s 280 end 281 local s = read(fp,'*a') 282 closefile(fp) 283 if kind == 'c' or kind == 'h' then 284 return extract_code(fn,s) 285 elseif kind == 'l' then 286 return "\n$[--##"..fn.."\n" .. s .. "\n$]\n" 287 elseif kind == 'i' then 288 local t = {code=s} 289 extra = string.gsub(extra, "^%s*,%s*", "") 290 local pars = split_c_tokens(extra, ",") 291 include_file_hook(t, fn, unpack(pars)) 292 return "\n\n" .. t.code 293 else 294 error('#Invalid include directive (use $cfile, $pfile, $lfile or $ifile)') 295 end 296 end) 297 until nsubst==0 298 299 -- deal with renaming directive 300 repeat -- I don't know why this is necesary 301 code,nsubst = gsub(code,'\n%s*%$renaming%s*(.-)%s*\n', function (r) appendrenaming(r) return "\n" end) 302 until nsubst == 0 303 304 local t = _Package(_Container{name=name, code=code}) 305 push(t) 306 preprocess_hook(t) 307 t:preprocess() 308 preparse_hook(t) 309 t:parse(t.code) 310 pop() 311 return t 312end 313 314 315setmetatable(_extra_parameters, { __index = _G }) 316 317function prep(file) 318 319 local chunk = {'local __ret = {"\\n"}\n'} 320 for line in file:lines() do 321 if string.find(line, "^##") then 322 table.insert(chunk, string.sub(line, 3) .. "\n") 323 else 324 local last = 1 325 for text, expr, index in string.gfind(line, "(.-)$(%b())()") do 326 last = index 327 if text ~= "" then 328 table.insert(chunk, string.format('table.insert(__ret, %q )', text)) 329 end 330 table.insert(chunk, string.format('table.insert(__ret, %s )', expr)) 331 end 332 table.insert(chunk, string.format('table.insert(__ret, %q)\n', 333 string.sub(line, last).."\n")) 334 end 335 end 336 table.insert(chunk, '\nreturn table.concat(__ret)\n') 337 local f,e = loadstring(table.concat(chunk)) 338 if e then 339 error("#"..e) 340 end 341 setfenv(f, _extra_parameters) 342 return f() 343end 344