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