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