1-- tolua: declaration 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-- 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 = find_enum_var(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 = find_enum_var(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
83
84	if self.kind and self.kind == 'var' then
85		self.name = string.gsub(self.name, ":.*$", "") -- ???
86	end
87end
88
89-- Check declaration type
90-- Substitutes typedef's.
91function classDeclaration:checktype ()
92
93 -- check if there is a pointer to basic type
94 local basic = isbasic(self.type)
95 if self.kind == 'func' and basic=='number' and string.find(self.ptr, "%*") then
96 	self.type = '_userdata'
97 	self.ptr = ""
98 end
99 if basic and self.ptr~='' then
100  self.ret = self.ptr
101  self.ptr = nil
102  if isbasic(self.type) == 'number' then
103  	self.return_userdata = true
104  end
105 end
106
107 -- check if there is array to be returned
108 if self.dim~='' and self.ret~='' then
109   error('#invalid parameter: cannot return an array of values')
110 end
111 -- restore 'void*' and 'string*'
112 if self.type == '_userdata' then self.type = 'void*'
113 elseif self.type == '_cstring' then self.type = 'char*'
114 elseif self.type == '_lstate' then self.type = 'lua_State*'
115 end
116
117 -- resolve types inside the templates
118 if self.type then
119	 self.type = resolve_template_types(self.type)
120 end
121
122--
123-- -- if returning value, automatically set default value
124-- if self.ret ~= '' and self.def == '' then
125--  self.def = '0'
126-- end
127--
128
129end
130
131function resolve_template_types(type)
132
133	if isbasic(type) then
134		return type
135	end
136	local b,_,m = string.find(type, "(%b<>)")
137	if b then
138
139		m = split_c_tokens(string.sub(m, 2, -2), ",")
140		for i=1, #m do
141			m[i] = string.gsub(m[i],"%s*([%*&])", "%1")
142			if not isbasic(m[i]) then
143				if not isenum(m[i]) then _, m[i] = applytypedef("", m[i]) end
144				m[i] = findtype(m[i]) or m[i]
145				m[i] = resolve_template_types(m[i])
146			end
147		end
148
149		local b,i
150		type,b,i = break_template(type)
151--print("concat is ",concat(m, 1, m.n))
152		local template_part = "<"..concat(m, 1, m.n, ",")..">"
153		type = rebuild_template(type, b, template_part)
154		type = string.gsub(type, ">>", "> >")
155	end
156	return type
157end
158
159function break_template(s)
160	local b,e,timpl = string.find(s, "(%b<>)")
161	if timpl then
162		s = string.gsub(s, "%b<>", "")
163		return s, b, timpl
164	else
165		return s, 0, nil
166	end
167end
168
169function rebuild_template(s, b, timpl)
170
171	if b == 0 then
172		return s
173	end
174
175	return string.sub(s, 1, b-1)..timpl..string.sub(s, b, -1)
176end
177
178-- Print method
179function classDeclaration:print (ident,close)
180 print(ident.."Declaration{")
181 print(ident.." mod  = '"..self.mod.."',")
182 print(ident.." type = '"..self.type.."',")
183 print(ident.." ptr  = '"..self.ptr.."',")
184 print(ident.." name = '"..self.name.."',")
185 print(ident.." dim  = '"..self.dim.."',")
186 print(ident.." def  = '"..self.def.."',")
187 print(ident.." ret  = '"..self.ret.."',")
188 print(ident.."}"..close)
189end
190
191-- check if array of values are returned to Lua
192function classDeclaration:requirecollection (t)
193 if self.mod ~= 'const' and
194	    self.dim and self.dim ~= '' and
195				 not isbasic(self.type) and
196				 self.ptr == '' and self:check_public_access() then
197		local type = gsub(self.type,"%s*const%s+","")
198		t[type] = "tolua_collect_" .. clean_template(type)
199		return true
200	end
201	return false
202end
203
204-- declare tag
205function classDeclaration:decltype ()
206
207	self.type = typevar(self.type)
208	if strfind(self.mod,'const') then
209		self.type = 'const '..self.type
210		self.mod = gsub(self.mod,'const%s*','')
211	end
212end
213
214
215-- output type checking
216function classDeclaration:outchecktype (narg)
217 local def
218 local t = isbasic(self.type)
219 if self.def~='' then
220  def = 1
221 else
222  def = 0
223 end
224 if self.dim ~= '' then
225	--if t=='string' then
226	--	return 'tolua_isstringarray(tolua_S,'..narg..','..def..',&tolua_err)'
227	--else
228	return '!tolua_istable(tolua_S,'..narg..',0,&tolua_err)'
229 	--end
230 elseif t then
231	return '!tolua_is'..t..'(tolua_S,'..narg..','..def..',&tolua_err)'
232 else
233  local is_func = get_is_function(self.type)
234  if self.ptr == '&' or self.ptr == '' then
235  	return '(tolua_isvaluenil(tolua_S,'..narg..',&tolua_err) || !'..is_func..'(tolua_S,'..narg..',"'..self.type..'",'..def..',&tolua_err))'
236  else
237	return '!'..is_func..'(tolua_S,'..narg..',"'..self.type..'",'..def..',&tolua_err)'
238  end
239 end
240end
241
242function classDeclaration:builddeclaration (narg, cplusplus)
243 local array = self.dim ~= '' and tonumber(self.dim)==nil
244	local line = ""
245 local ptr = ''
246 local mod
247 local type = self.type
248 local nctype = gsub(self.type,'const%s+','')
249 if self.dim ~= '' then
250	 type = gsub(self.type,'const%s+','')  -- eliminates const modifier for arrays
251 end
252 if self.ptr~='' and not isbasic(type) then ptr = '*' end
253 line = concatparam(line," ",self.mod,type,ptr)
254 if array then
255  line = concatparam(line,'*')
256 end
257 line = concatparam(line,self.name)
258 if self.dim ~= '' then
259  if tonumber(self.dim)~=nil then
260   line = concatparam(line,'[',self.dim,'];')
261  else
262	if cplusplus then
263		line = concatparam(line,' = Mtolua_new_dim(',type,ptr,', '..self.dim..');')
264	else
265		line = concatparam(line,' = (',type,ptr,'*)',
266		'malloc((',self.dim,')*sizeof(',type,ptr,'));')
267	end
268  end
269 else
270  local t = isbasic(type)
271  line = concatparam(line,' = ')
272  if t == 'state' then
273  	line = concatparam(line, 'tolua_S;')
274  else
275  	--print("t is "..tostring(t)..", ptr is "..tostring(self.ptr))
276  	if t == 'number' and string.find(self.ptr, "%*") then
277  		t = 'userdata'
278  	end
279	if not t and ptr=='' then line = concatparam(line,'*') end
280	line = concatparam(line,'((',self.mod,type)
281	if not t then
282		line = concatparam(line,'*')
283	end
284	line = concatparam(line,') ')
285	if isenum(nctype) then
286		line = concatparam(line,'(int) ')
287	end
288	local def = 0
289	if self.def ~= '' then
290		def = self.def
291		if (ptr == '' or self.ptr == '&') and not t then
292			def = "(void*)&(const "..type..")"..def
293		end
294	end
295	if t then
296		line = concatparam(line,'tolua_to'..t,'(tolua_S,',narg,',',def,'));')
297	else
298		local to_func = get_to_function(type)
299		line = concatparam(line,to_func..'(tolua_S,',narg,',',def,'));')
300	end
301  end
302 end
303	return line
304end
305
306-- Declare variable
307function classDeclaration:declare (narg)
308 if self.dim ~= '' and tonumber(self.dim)==nil then
309	 output('#ifdef __cplusplus\n')
310		output(self:builddeclaration(narg,true))
311		output('#else\n')
312		output(self:builddeclaration(narg,false))
313	 output('#endif\n')
314	else
315		output(self:builddeclaration(narg,false))
316	end
317end
318
319-- Get parameter value
320function classDeclaration:getarray (narg)
321 if self.dim ~= '' then
322	 local type = gsub(self.type,'const ','')
323  output('  {')
324	 output('#ifndef TOLUA_RELEASE\n')
325  local def; if self.def~='' then def=1 else def=0 end
326		local t = isbasic(type)
327		if (t) then
328		   output('   if (!tolua_is'..t..'array(tolua_S,',narg,',',self.dim,',',def,',&tolua_err))')
329		else
330		   output('   if (!tolua_isusertypearray(tolua_S,',narg,',"',type,'",',self.dim,',',def,',&tolua_err))')
331		end
332  output('    goto tolua_lerror;')
333  output('   else\n')
334	 output('#endif\n')
335  output('   {')
336  output('    int i;')
337  output('    for(i=0; i<'..self.dim..';i++)')
338  local t = isbasic(type)
339  local ptr = ''
340  if self.ptr~='' then ptr = '*' end
341  output('   ',self.name..'[i] = ')
342  if not t and ptr=='' then output('*') end
343  output('((',type)
344  if not t then
345   output('*')
346  end
347  output(') ')
348  local def = 0
349  if self.def ~= '' then def = self.def end
350  if t then
351   output('tolua_tofield'..t..'(tolua_S,',narg,',i+1,',def,'));')
352  else
353   output('tolua_tofieldusertype(tolua_S,',narg,',i+1,',def,'));')
354  end
355  output('   }')
356  output('  }')
357 end
358end
359
360-- Get parameter value
361function classDeclaration:setarray (narg)
362 if not strfind(self.type,'const%s+') and self.dim ~= '' then
363	 local type = gsub(self.type,'const ','')
364  output('  {')
365  output('   int i;')
366  output('   for(i=0; i<'..self.dim..';i++)')
367  local t,ct = isbasic(type)
368  if t then
369   output('    tolua_pushfield'..t..'(tolua_S,',narg,',i+1,(',ct,')',self.name,'[i]);')
370  else
371   if self.ptr == '' then
372     output('   {')
373     output('#ifdef __cplusplus\n')
374     output('    void* tolua_obj = Mtolua_new((',type,')(',self.name,'[i]));')
375     output('    tolua_pushfieldusertype_and_takeownership(tolua_S,',narg,',i+1,tolua_obj,"',type,'");')
376     output('#else\n')
377     output('    void* tolua_obj = tolua_copy(tolua_S,(void*)&',self.name,'[i],sizeof(',type,'));')
378     output('    tolua_pushfieldusertype(tolua_S,',narg,',i+1,tolua_obj,"',type,'");')
379     output('#endif\n')
380     output('   }')
381   else
382    output('   tolua_pushfieldusertype(tolua_S,',narg,',i+1,(void*)',self.name,'[i],"',type,'");')
383   end
384  end
385  output('  }')
386 end
387end
388
389-- Free dynamically allocated array
390function classDeclaration:freearray ()
391 if self.dim ~= '' and tonumber(self.dim)==nil then
392	 output('#ifdef __cplusplus\n')
393		output('  Mtolua_delete_dim(',self.name,');')
394	 output('#else\n')
395  output('  free(',self.name,');')
396	 output('#endif\n')
397 end
398end
399
400-- Pass parameter
401function classDeclaration:passpar ()
402 if self.ptr=='&' and not isbasic(self.type) then
403  output('*'..self.name)
404 elseif self.ret=='*' then
405  output('&'..self.name)
406 else
407  output(self.name)
408 end
409end
410
411-- Return parameter value
412function classDeclaration:retvalue ()
413 if self.ret ~= '' then
414  local t,ct = isbasic(self.type)
415  if t and t~='' then
416   output('   tolua_push'..t..'(tolua_S,(',ct,')'..self.name..');')
417  else
418   local push_func = get_push_function(self.type)
419   output('   ',push_func,'(tolua_S,(void*)'..self.name..',"',self.type,'");')
420  end
421  return 1
422 end
423 return 0
424end
425
426-- Internal constructor
427function _Declaration (t)
428
429 setmetatable(t,classDeclaration)
430 t:buildnames()
431 t:checkname()
432 t:checktype()
433 local ft = findtype(t.type) or t.type
434 if not isenum(ft) then
435	t.mod, t.type = applytypedef(t.mod, ft)
436 end
437
438 if t.kind=="var" and (string.find(t.mod, "tolua_property%s") or string.find(t.mod, "tolua_property$")) then
439 	t.mod = string.gsub(t.mod, "tolua_property", "tolua_property__"..get_property_type())
440 end
441
442 return t
443end
444
445-- Constructor
446-- Expects the string declaration.
447-- The kind of declaration can be "var" or "func".
448function Declaration (s,kind,is_parameter)
449
450 -- eliminate spaces if default value is provided
451 s = gsub(s,"%s*=%s*","=")
452 s = gsub(s, "%s*<", "<")
453
454 local defb,tmpdef
455 defb,_,tmpdef = string.find(s, "(=.*)$")
456 if defb then
457 	s = string.gsub(s, "=.*$", "")
458 else
459 	tmpdef = ''
460 end
461 if kind == "var" then
462  -- check the form: void
463  if s == '' or s == 'void' then
464   return _Declaration{type = 'void', kind = kind, is_parameter = is_parameter}
465  end
466 end
467
468 -- check the form: mod type*& name
469 local t = split_c_tokens(s,'%*%s*&')
470 if t.n == 2 then
471  if kind == 'func' then
472   error("#invalid function return type: "..s)
473  end
474  --local m = split(t[1],'%s%s*')
475  local m = split_c_tokens(t[1],'%s+')
476  return _Declaration{
477   name = t[2]..tmpdef,
478   ptr = '*',
479   ret = '&',
480   --type = rebuild_template(m[m.n], tb, timpl),
481   type = m[m.n],
482   mod = concat(m,1,m.n-1),
483   is_parameter = is_parameter,
484   kind = kind
485  }
486 end
487
488 -- check the form: mod type** name
489 t = split_c_tokens(s,'%*%s*%*')
490 if t.n == 2 then
491  if kind == 'func' then
492   error("#invalid function return type: "..s)
493  end
494  --local m = split(t[1],'%s%s*')
495  local m = split_c_tokens(t[1],'%s+')
496  return _Declaration{
497   name = t[2]..tmpdef,
498   ptr = '*',
499   ret = '*',
500   --type = rebuild_template(m[m.n], tb, timpl),
501   type = m[m.n],
502   mod = concat(m,1,m.n-1),
503   is_parameter = is_parameter,
504   kind = kind
505  }
506 end
507
508 -- check the form: mod type& name
509 t = split_c_tokens(s,'&')
510 if t.n == 2 then
511  --local m = split(t[1],'%s%s*')
512  local m = split_c_tokens(t[1],'%s+')
513  return _Declaration{
514   name = t[2]..tmpdef,
515   ptr = '&',
516   --type = rebuild_template(m[m.n], tb, timpl),
517   type = m[m.n],
518   mod = concat(m,1,m.n-1),
519   is_parameter = is_parameter,
520   kind = kind
521  }
522 end
523
524 -- check the form: mod type* name
525 local s1 = gsub(s,"(%b%[%])",function (n) return gsub(n,'%*','\1') end)
526 t = split_c_tokens(s1,'%*')
527 if t.n == 2 then
528  t[2] = gsub(t[2],'\1','%*') -- restore * in dimension expression
529  --local m = split(t[1],'%s%s*')
530  local m = split_c_tokens(t[1],'%s+')
531  return _Declaration{
532   name = t[2]..tmpdef,
533   ptr = '*',
534   type = m[m.n],
535   --type = rebuild_template(m[m.n], tb, timpl),
536   mod = concat(m,1,m.n-1)   ,
537   is_parameter = is_parameter,
538   kind = kind
539  }
540 end
541
542 if kind == 'var' then
543  -- check the form: mod type name
544  --t = split(s,'%s%s*')
545  t = split_c_tokens(s,'%s+')
546  local v
547  if findtype(t[t.n]) then v = create_varname() else v = t[t.n]; t.n = t.n-1 end
548  return _Declaration{
549   name = v..tmpdef,
550   --type = rebuild_template(t[t.n], tb, timpl),
551   type = t[t.n],
552   mod = concat(t,1,t.n-1),
553   is_parameter = is_parameter,
554   kind = kind
555  }
556
557 else -- kind == "func"
558
559  -- check the form: mod type name
560  --t = split(s,'%s%s*')
561  t = split_c_tokens(s,'%s+')
562  local v = t[t.n]  -- last word is the function name
563  local tp,md
564  if t.n>1 then
565   tp = t[t.n-1]
566   md = concat(t,1,t.n-2)
567  end
568  --if tp then tp = rebuild_template(tp, tb, timpl) end
569  return _Declaration{
570   name = v,
571   type = tp,
572   mod = md,
573   is_parameter = is_parameter,
574   kind = kind
575  }
576 end
577
578end
579
580