1-- tolua: declaration class
2-- Written by Waldemar Celes
3-- TeCGraf/PUC-Rio
4-- Jul 1998
5-- $Id: declaration.lua,v 1.1 2009-07-09 13:43:54 fabraham Exp $
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-- Declaration class
14-- Represents variable, function, or argument declaration.
15-- Stores the following fields:
16--  mod  = type modifiers
17--  type = type
18--  ptr  = "*" or "&", if representing a pointer or a reference
19--  name = name
20--  dim  = dimension, if a vector
21--  def  = default value, if any (only for arguments)
22--  ret  = "*" or "&", if value is to be returned (only for arguments)
23classDeclaration = {
24 mod = '',
25 type = '',
26 ptr = '',
27 name = '',
28 dim = '',
29 ret = '',
30 def = ''
31}
32classDeclaration.__index = classDeclaration
33setmetatable(classDeclaration,classFeature)
34
35-- Create an unique variable name
36function create_varname ()
37 if not _varnumber then _varnumber = 0 end
38 _varnumber = _varnumber + 1
39 return "tolua_var_".._varnumber
40end
41
42-- Check declaration name
43-- It also identifies default values
44function classDeclaration:checkname ()
45
46 if strsub(self.name,1,1) == '[' and not findtype(self.type) then
47  self.name = self.type..self.name
48  local m = split(self.mod,'%s%s*')
49  self.type = m[m.n]
50  self.mod = concat(m,1,m.n-1)
51 end
52
53 local t = split(self.name,'=')
54 if t.n==2 then
55  self.name = t[1]
56  self.def = t[t.n]
57 end
58
59 local b,e,d = strfind(self.name,"%[(.-)%]")
60 if b then
61  self.name = strsub(self.name,1,b-1)
62  self.dim = d
63 end
64
65
66 if self.type ~= '' and self.type ~= 'void' and self.name == '' then
67  self.name = create_varname()
68 elseif self.kind=='var' then
69  if self.type=='' and self.name~='' then
70   self.type = self.type..self.name
71   self.name = create_varname()
72  elseif findtype(self.name) then
73   if self.type=='' then self.type = self.name
74   else self.type = self.type..' '..self.name end
75   self.name = create_varname()
76  end
77 end
78
79 -- adjust type of string
80-- if self.type == 'char' and self.dim ~= '' then
81--	 self.type = 'char*'
82--	end
83end
84
85-- Check declaration type
86-- Substitutes typedef's.
87function classDeclaration:checktype ()
88
89 -- check if there is a pointer to basic type
90 if isbasic(self.type) and self.ptr~='' then
91  self.ret = self.ptr
92  self.ptr = nil
93 end
94
95 -- check if there is array to be returned
96 if self.dim~='' and self.ret~='' then
97   error('#invalid parameter: cannot return an array of values')
98 end
99
100 -- restore 'void*' and 'string*'
101 if self.type == '_userdata' then self.type = 'void*'
102 elseif self.type == '_cstring' then self.type = 'char*'
103 elseif self.type == '_lstate' then self.type = 'lua_State*'
104 end
105
106--
107-- -- if returning value, automatically set default value
108-- if self.ret ~= '' and self.def == '' then
109--  self.def = '0'
110-- end
111--
112
113end
114
115-- Print method
116function classDeclaration:print (ident,close)
117 print(ident.."Declaration{")
118 print(ident.." mod  = '"..self.mod.."',")
119 print(ident.." type = '"..self.type.."',")
120 print(ident.." ptr  = '"..self.ptr.."',")
121 print(ident.." name = '"..self.name.."',")
122 print(ident.." dim  = '"..self.dim.."',")
123 print(ident.." def  = '"..self.def.."',")
124 print(ident.." ret  = '"..self.ret.."',")
125 print(ident.."}"..close)
126end
127
128-- check if array of values are returned to Lua
129function classDeclaration:requirecollection (t)
130 if  self.mod ~= 'const' and
131	    self.dim and self.dim ~= '' and
132				 not isbasic(self.type) and
133				 self.ptr == '' then
134		local type = gsub(self.type,"%s*const%s*","")
135		t[type] = "tolua_collect_" .. gsub(type,"::","_")
136		return true
137	end
138	return false
139end
140
141-- declare tag
142function classDeclaration:decltype ()
143 self.type = typevar(self.type)
144 if strfind(self.mod,'const') then
145	 self.type = 'const '..self.type
146		self.mod = gsub(self.mod,'const%s*','')
147	end
148end
149
150
151-- output type checking
152function classDeclaration:outchecktype (narg,var)
153 local def
154 local t = isbasic(self.type)
155 if self.def~='' then
156  def = 1
157 else
158  def = 0
159 end
160 if self.dim ~= '' then
161	 if var and self.type=='char' then
162   return 'tolua_isstring(tolua_S,'..narg..','..def..',&tolua_err)'
163		else
164   return 'tolua_istable(tolua_S,'..narg..',0,&tolua_err)'
165		end
166	elseif t then
167  return 'tolua_is'..t..'(tolua_S,'..narg..','..def..',&tolua_err)'
168	else
169  return 'tolua_isusertype(tolua_S,'..narg..',"'..self.type..'",'..def..',&tolua_err)'
170	end
171end
172
173function classDeclaration:builddeclaration (narg, cplusplus)
174 local array = self.dim ~= '' and tonumber(self.dim)==nil
175	local line = ""
176 local ptr = ''
177	local mod
178	local type = self.type
179 if self.dim ~= '' then
180	 type = gsub(self.type,'const%s*','')  -- eliminates const modifier for arrays
181	end
182	local ctype = type
183	if ctype=="lua_Object" or ctype=="lua_Function" then
184	 ctype = "int"
185	end
186 if self.ptr~='' then ptr = '*' end
187 line = concatparam(line," ",self.mod,ctype,ptr)
188 if array then
189  line = concatparam(line,'*')
190 end
191 line = concatparam(line,self.name)
192 if self.dim~='' then
193  if tonumber(self.dim)~=nil then
194   line = concatparam(line,'[',self.dim,'];')
195  else
196		 if cplusplus then
197			 line = concatparam(line,' = new',type,ptr,'['..self.dim..'];')
198			else
199    line = concatparam(line,' = (',type,ptr,'*)',
200           'malloc((',self.dim,')*sizeof(',type,ptr,'));')
201			end
202  end
203 else
204  local t = isbasic(type)
205  line = concatparam(line,' = ')
206		if t == 'state' then
207		 line = concatparam(line, 'tolua_S;')
208		else
209			if not t and ptr=='' then line = concatparam(line,'*') end
210			local ct = type
211			if t == 'value' or t == 'function' then
212				ct = 'int'
213			end
214			line = concatparam(line,'((',self.mod,ct)
215			if not t then
216				line = concatparam(line,'*')
217			end
218			line = concatparam(line,') ')
219			if isenum(type) then
220			--if not t and isenum(type) then
221				line = concatparam(line,'(int) ')
222			end
223			local def = 0
224			if self.def ~= '' then def = self.def end
225			if t then
226		  if t=='function' then t='value' end
227				line = concatparam(line,'tolua_to'..t,'(tolua_S,',narg,',',def,'));')
228			else
229				line = concatparam(line,'tolua_tousertype(tolua_S,',narg,',',def,'));')
230			end
231		end
232 end
233	return line
234end
235
236-- Declare variable
237function classDeclaration:declare (narg)
238 if self.dim ~= '' and self.type~='char' and tonumber(self.dim)==nil then
239	 output('#ifdef __cplusplus\n')
240		output(self:builddeclaration(narg,true))
241		output('#else\n')
242		output(self:builddeclaration(narg,false))
243	 output('#endif\n')
244	else
245		output(self:builddeclaration(narg,false))
246	end
247end
248
249-- Get parameter value
250function classDeclaration:getarray (narg)
251 if self.dim ~= '' then
252	 local type = gsub(self.type,'const ','')
253  output('  {')
254	 output('#ifndef TOLUA_RELEASE\n')
255  local def; if self.def~='' then def=1 else def=0 end
256		local t = isbasic(type)
257		if (t) then
258   output('   if (!tolua_is'..t..'array(tolua_S,',narg,',',self.dim,',',def,',&tolua_err))')
259		else
260   output('   if (!tolua_isusertypearray(tolua_S,',narg,',"',type,'",',self.dim,',',def,',&tolua_err))')
261		end
262  output('    goto tolua_lerror;')
263  output('   else\n')
264	 output('#endif\n')
265  output('   {')
266  output('    int i;')
267  output('    for(i=0; i<'..self.dim..';i++)')
268  local t = isbasic(type)
269  local ptr = ''
270  if self.ptr~='' then ptr = '*' end
271  output('   ',self.name..'[i] = ')
272  if not t and ptr=='' then output('*') end
273  output('((',type)
274  if not t then
275   output('*')
276  end
277  output(') ')
278  local def = 0
279  if self.def ~= '' then def = self.def end
280  if t then
281		 if t=='function' then t='value' end
282   output('tolua_tofield'..t..'(tolua_S,',narg,',i+1,',def,'));')
283  else
284   output('tolua_tofieldusertype(tolua_S,',narg,',i+1,',def,'));')
285  end
286  output('   }')
287  output('  }')
288 end
289end
290
291-- Get parameter value
292function classDeclaration:setarray (narg)
293 if not strfind(self.type,'const') and self.dim ~= '' then
294	 local type = gsub(self.type,'const ','')
295  output('  {')
296  output('   int i;')
297  output('   for(i=0; i<'..self.dim..';i++)')
298  local t,ct = isbasic(type)
299  if t then
300		 if t=='function' then t='value' end
301   output('    tolua_pushfield'..t..'(tolua_S,',narg,',i+1,(',ct,')',self.name,'[i]);')
302  else
303   if self.ptr == '' then
304     output('   {')
305     output('#ifdef __cplusplus\n')
306     output('    void* tolua_obj = new',type,'(',self.name,'[i]);')
307					output('    tolua_pushfieldusertype(tolua_S,',narg,',i+1,tolua_clone(tolua_S,tolua_obj,'.. (_collect[type] or 'NULL') ..'),"',type,'");')
308     output('#else\n')
309     output('    void* tolua_obj = tolua_copy(tolua_S,(void*)&',self.name,'[i],sizeof(',type,'));')
310					output('    tolua_pushfieldusertype(tolua_S,',narg,',i+1,tolua_clone(tolua_S,tolua_obj,NULL),"',type,'");')
311     output('#endif\n')
312     output('   }')
313   else
314    output('   tolua_pushfieldusertype(tolua_S,',narg,',i+1,(void*)',self.name,'[i],"',type,'");')
315   end
316  end
317  output('  }')
318 end
319end
320
321-- Free dynamically allocated array
322function classDeclaration:freearray ()
323 if self.dim ~= '' and tonumber(self.dim)==nil then
324	 output('#ifdef __cplusplus\n')
325		output('  delete []',self.name,';')
326	 output('#else\n')
327  output('  free(',self.name,');')
328	 output('#endif\n')
329 end
330end
331
332-- Pass parameter
333function classDeclaration:passpar ()
334 if self.ptr=='&' then
335  output('*'..self.name)
336 elseif self.ret=='*' then
337  output('&'..self.name)
338 else
339  output(self.name)
340 end
341end
342
343-- Return parameter value
344function classDeclaration:retvalue ()
345 if self.ret ~= '' then
346  local t,ct = isbasic(self.type)
347  if t then
348		 if t=='function' then t='value' end
349   output('   tolua_push'..t..'(tolua_S,(',ct,')'..self.name..');')
350  else
351   output('   tolua_pushusertype(tolua_S,(void*)'..self.name..',"',self.type,'");')
352  end
353  return 1
354 end
355 return 0
356end
357
358-- Internal constructor
359function _Declaration (t)
360 setmetatable(t,classDeclaration)
361 t:buildnames()
362 t:checkname()
363 t:checktype()
364 return t
365end
366
367-- Constructor
368-- Expects the string declaration.
369-- The kind of declaration can be "var" or "func".
370function Declaration (s,kind)
371 -- eliminate spaces if default value is provided
372 s = gsub(s,"%s*=%s*","=")
373
374 if kind == "var" then
375  -- check the form: void
376  if s == '' or s == 'void' then
377   return _Declaration{type = 'void', kind = kind}
378  end
379 end
380
381 -- check the form: mod type*& name
382 local t = split(s,'%*%s*&')
383 if t.n == 2 then
384  if kind == 'func' then
385   error("#invalid function return type: "..s)
386  end
387  local m = split(t[1],'%s%s*')
388  return _Declaration{
389   name = t[2],
390   ptr = '*',
391   ret = '&',
392   type = m[m.n],
393   mod = concat(m,1,m.n-1),
394   kind = kind
395  }
396 end
397
398 -- check the form: mod type** name
399 t = split(s,'%*%s*%*')
400 if t.n == 2 then
401  if kind == 'func' then
402   error("#invalid function return type: "..s)
403  end
404  local m = split(t[1],'%s%s*')
405  return _Declaration{
406   name = t[2],
407   ptr = '*',
408   ret = '*',
409   type = m[m.n],
410   mod = concat(m,1,m.n-1),
411   kind = kind
412  }
413 end
414
415 -- check the form: mod type& name
416 t = split(s,'&')
417 if t.n == 2 then
418  local m = split(t[1],'%s%s*')
419  return _Declaration{
420   name = t[2],
421   ptr = '&',
422   type = m[m.n],
423   mod = concat(m,1,m.n-1)   ,
424   kind = kind
425  }
426 end
427
428 -- check the form: mod type* name
429 local s1 = gsub(s,"(%b\[\])",function (n) return gsub(n,'%*','\1') end)
430 t = split(s1,'%*')
431 if t.n == 2 then
432  t[2] = gsub(t[2],'\1','%*') -- restore * in dimension expression
433  local m = split(t[1],'%s%s*')
434  return _Declaration{
435   name = t[2],
436   ptr = '*',
437   type = m[m.n],
438   mod = concat(m,1,m.n-1)   ,
439   kind = kind
440  }
441 end
442
443 if kind == 'var' then
444  -- check the form: mod type name
445  t = split(s,'%s%s*')
446  local v
447  if findtype(t[t.n]) then v = '' else v = t[t.n]; t.n = t.n-1 end
448  return _Declaration{
449   name = v,
450   type = t[t.n],
451   mod = concat(t,1,t.n-1),
452   kind = kind
453  }
454
455 else -- kind == "func"
456
457  -- check the form: mod type name
458  t = split(s,'%s%s*')
459  local v = t[t.n]  -- last word is the function name
460  local tp,md
461  if t.n>1 then
462   tp = t[t.n-1]
463   md = concat(t,1,t.n-2)
464  end
465  return _Declaration{
466   name = v,
467   type = tp,
468   mod = md,
469   kind = kind
470  }
471 end
472
473end
474
475
476
477