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