572 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			572 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| -- tolua: declaration class
 | |
| -- Written by Waldemar Celes
 | |
| -- TeCGraf/PUC-Rio
 | |
| -- Jul 1998
 | |
| -- $Id: declaration.lua 1141 2006-05-18 23:58:45Z lindquist $
 | |
| 
 | |
| -- This code is free software; you can redistribute it and/or modify it.
 | |
| -- The software provided hereunder is on an "as is" basis, and
 | |
| -- the author has no obligation to provide maintenance, support, updates,
 | |
| -- enhancements, or modifications.
 | |
| 
 | |
| 
 | |
| -- Declaration class
 | |
| -- Represents variable, function, or argument declaration.
 | |
| -- Stores the following fields:
 | |
| --  mod  = type modifiers
 | |
| --  type = type
 | |
| --  ptr  = "*" or "&", if representing a pointer or a reference
 | |
| --  name = name
 | |
| --  dim  = dimension, if a vector
 | |
| --  def  = default value, if any (only for arguments)
 | |
| --  ret  = "*" or "&", if value is to be returned (only for arguments)
 | |
| classDeclaration = {
 | |
|  mod = '',
 | |
|  type = '',
 | |
|  ptr = '',
 | |
|  name = '',
 | |
|  dim = '',
 | |
|  ret = '',
 | |
|  def = ''
 | |
| }
 | |
| classDeclaration.__index = classDeclaration
 | |
| setmetatable(classDeclaration,classFeature)
 | |
| 
 | |
| -- Create an unique variable name
 | |
| function create_varname ()
 | |
|  if not _varnumber then _varnumber = 0 end
 | |
|  _varnumber = _varnumber + 1
 | |
|  return "tolua_var_".._varnumber
 | |
| end
 | |
| 
 | |
| -- Check declaration name
 | |
| -- It also identifies default values
 | |
| function classDeclaration:checkname ()
 | |
| 
 | |
|  if strsub(self.name,1,1) == '[' and not findtype(self.type) then
 | |
|   self.name = self.type..self.name
 | |
|   local m = split(self.mod,'%s%s*')
 | |
|   self.type = m[m.n]
 | |
|   self.mod = concat(m,1,m.n-1)
 | |
|  end
 | |
| 
 | |
|  local t = split(self.name,'=')
 | |
|  if t.n==2 then
 | |
|   self.name = t[1]
 | |
|   self.def = find_enum_var(t[t.n])
 | |
|  end
 | |
| 
 | |
|  local b,e,d = strfind(self.name,"%[(.-)%]")
 | |
|  if b then
 | |
|   self.name = strsub(self.name,1,b-1)
 | |
|   self.dim = find_enum_var(d)
 | |
|  end
 | |
| 
 | |
| 
 | |
|  if self.type ~= '' and self.type ~= 'void' and self.name == '' then
 | |
|   self.name = create_varname()
 | |
|  elseif self.kind=='var' then
 | |
|   if self.type=='' and self.name~='' then
 | |
|    self.type = self.type..self.name
 | |
|    self.name = create_varname()
 | |
|   elseif findtype(self.name) then
 | |
|    if self.type=='' then self.type = self.name
 | |
|    else self.type = self.type..' '..self.name end
 | |
|    self.name = create_varname()
 | |
|   end
 | |
|  end
 | |
| 
 | |
|  -- adjust type of string
 | |
|  if self.type == 'char' and self.dim ~= '' then
 | |
| 	 self.type = 'char*'
 | |
|  end
 | |
| 
 | |
| 	if self.kind and self.kind == 'var' then
 | |
| 		self.name = string.gsub(self.name, ":.*$", "") -- ???
 | |
| 	end
 | |
| end
 | |
| 
 | |
| -- Check declaration type
 | |
| -- Substitutes typedef's.
 | |
| function classDeclaration:checktype ()
 | |
| 
 | |
|  -- check if there is a pointer to basic type
 | |
|  local basic = isbasic(self.type)
 | |
|  if self.kind == 'func' and basic=='number' and string.find(self.ptr, "%*") then
 | |
|  	self.type = '_userdata'
 | |
|  	self.ptr = ""
 | |
|  end
 | |
|  if basic and self.ptr~='' then
 | |
|   self.ret = self.ptr
 | |
|   self.ptr = nil
 | |
|   if isbasic(self.type) == 'number' then
 | |
|   	self.return_userdata = true
 | |
|   end
 | |
|  end
 | |
| 
 | |
|  -- check if there is array to be returned
 | |
|  if self.dim~='' and self.ret~='' then
 | |
|    error('#invalid parameter: cannot return an array of values')
 | |
|  end
 | |
|  -- restore 'void*' and 'string*'
 | |
|  if self.type == '_userdata' then self.type = 'void*'
 | |
|  elseif self.type == '_cstring' then self.type = 'char*'
 | |
|  elseif self.type == '_lstate' then self.type = 'lua_State*'
 | |
|  end
 | |
| 
 | |
|  -- resolve types inside the templates
 | |
|  if self.type then
 | |
| 	 self.type = resolve_template_types(self.type)
 | |
|  end
 | |
| 
 | |
| --
 | |
| -- -- if returning value, automatically set default value
 | |
| -- if self.ret ~= '' and self.def == '' then
 | |
| --  self.def = '0'
 | |
| -- end
 | |
| --
 | |
| 
 | |
| end
 | |
| 
 | |
| function resolve_template_types(type)
 | |
| 
 | |
| 	if isbasic(type) then
 | |
| 		return type
 | |
| 	end
 | |
| 	local b,_,m = string.find(type, "(%b<>)")
 | |
| 	if b then
 | |
| 
 | |
| 		m = split_c_tokens(string.sub(m, 2, -2), ",")
 | |
| 		for i=1, table.getn(m) do
 | |
| 			m[i] = string.gsub(m[i],"%s*([%*&])", "%1")
 | |
| 			m[i] = findtype(m[i]) or m[i]
 | |
| 			m[i] = resolve_template_types(m[i])
 | |
| 		end
 | |
| 
 | |
| 		local b,i
 | |
| 		type,b,i = break_template(type)
 | |
| 		local template_part = "<"..string.gsub(concat(m, 1, m.n), " ", ",")..">"
 | |
| 		type = rebuild_template(type, b, template_part)
 | |
| 		type = string.gsub(type, ">>", "> >")
 | |
| 	end
 | |
| 	return type
 | |
| end
 | |
| 
 | |
| function break_template(s)
 | |
| 	local b,e,timpl = string.find(s, "(%b<>)")
 | |
| 	if timpl then
 | |
| 		s = string.gsub(s, "%b<>", "")
 | |
| 		return s, b, timpl
 | |
| 	else
 | |
| 		return s, 0, nil
 | |
| 	end
 | |
| end
 | |
| 
 | |
| function rebuild_template(s, b, timpl)
 | |
| 
 | |
| 	if b == 0 then
 | |
| 		return s
 | |
| 	end
 | |
| 
 | |
| 	return string.sub(s, 1, b-1)..timpl..string.sub(s, b, -1)
 | |
| end
 | |
| 
 | |
| -- Print method
 | |
| function classDeclaration:print (ident,close)
 | |
|  print(ident.."Declaration{")
 | |
|  print(ident.." mod  = '"..self.mod.."',")
 | |
|  print(ident.." type = '"..self.type.."',")
 | |
|  print(ident.." ptr  = '"..self.ptr.."',")
 | |
|  print(ident.." name = '"..self.name.."',")
 | |
|  print(ident.." dim  = '"..self.dim.."',")
 | |
|  print(ident.." def  = '"..self.def.."',")
 | |
|  print(ident.." ret  = '"..self.ret.."',")
 | |
|  print(ident.."}"..close)
 | |
| end
 | |
| 
 | |
| -- check if array of values are returned to Lua
 | |
| function classDeclaration:requirecollection (t)
 | |
|  if self.mod ~= 'const' and
 | |
| 	    self.dim and self.dim ~= '' and
 | |
| 				 not isbasic(self.type) and
 | |
| 				 self.ptr == '' then
 | |
| 		local type = gsub(self.type,"%s*const%s+","")
 | |
| 		t[type] = "tolua_collect_" .. clean_template(type)
 | |
| 		return true
 | |
| 	end
 | |
| 	return false
 | |
| end
 | |
| 
 | |
| -- declare tag
 | |
| function classDeclaration:decltype ()
 | |
| 
 | |
| 	self.type = typevar(self.type)
 | |
| 	if strfind(self.mod,'const') then
 | |
| 		self.type = 'const '..self.type
 | |
| 		self.mod = gsub(self.mod,'const%s*','')
 | |
| 	end
 | |
| end
 | |
| 
 | |
| 
 | |
| -- output type checking
 | |
| function classDeclaration:outchecktype (narg)
 | |
|  local def
 | |
|  local t = isbasic(self.type)
 | |
|  if self.def~='' then
 | |
|   def = 1
 | |
|  else
 | |
|   def = 0
 | |
|  end
 | |
|  if self.dim ~= '' then
 | |
| 	--if t=='string' then
 | |
| 	--	return 'tolua_isstringarray(tolua_S,'..narg..','..def..',&tolua_err)'
 | |
| 	--else
 | |
| 	return 'tolua_istable(tolua_S,'..narg..',0,&tolua_err)'
 | |
|  	--end
 | |
|  elseif t then
 | |
| 	return 'tolua_is'..t..'(tolua_S,'..narg..','..def..',&tolua_err)'
 | |
|  else
 | |
|   return 'tolua_isusertype(tolua_S,'..narg..',"'..self.type..'",'..def..',&tolua_err)'
 | |
|  end
 | |
| end
 | |
| 
 | |
| function classDeclaration:builddeclaration (narg, cplusplus)
 | |
|  local array = self.dim ~= '' and tonumber(self.dim)==nil
 | |
| 	local line = ""
 | |
|  local ptr = ''
 | |
| 	local mod
 | |
| 	local type = self.type
 | |
|  if self.dim ~= '' then
 | |
| 	 type = gsub(self.type,'const%s+','')  -- eliminates const modifier for arrays
 | |
|  end
 | |
|  if self.ptr~='' and not isbasic(type) then ptr = '*' end
 | |
|  line = concatparam(line," ",self.mod,type,ptr)
 | |
|  if array then
 | |
|   line = concatparam(line,'*')
 | |
|  end
 | |
|  line = concatparam(line,self.name)
 | |
|  if self.dim ~= '' then
 | |
|   if tonumber(self.dim)~=nil then
 | |
|    line = concatparam(line,'[',self.dim,'];')
 | |
|   else
 | |
| 	if cplusplus then
 | |
| 		line = concatparam(line,' = new',type,ptr,'['..self.dim..'];')
 | |
| 	else
 | |
| 		line = concatparam(line,' = (',type,ptr,'*)',
 | |
| 		'malloc((',self.dim,')*sizeof(',type,ptr,'));')
 | |
| 	end
 | |
|   end
 | |
|  else
 | |
|   local t = isbasic(type)
 | |
|   line = concatparam(line,' = ')
 | |
|   if t == 'state' then
 | |
|   	line = concatparam(line, 'tolua_S;')
 | |
|   else
 | |
|   	--print("t is "..tostring(t)..", ptr is "..tostring(self.ptr))
 | |
|   	if t == 'number' and string.find(self.ptr, "%*") then
 | |
|   		t = 'userdata'
 | |
|   	end
 | |
| 	if not t and ptr=='' then line = concatparam(line,'*') end
 | |
| 	line = concatparam(line,'((',self.mod,type)
 | |
| 	if not t then
 | |
| 		line = concatparam(line,'*')
 | |
| 	end
 | |
| 	line = concatparam(line,') ')
 | |
| 			if isenum(type) then
 | |
| 			line = concatparam(line,'(int) ')
 | |
| 			end
 | |
| 	local def = 0
 | |
| 	if self.def ~= '' then
 | |
| 		def = self.def
 | |
| 		if (ptr == '' or self.ptr == '&') and not t then
 | |
| 			def = "(void*)&(const "..type..")"..def
 | |
| 		end
 | |
| 	end
 | |
| 	if t then
 | |
| 	  if t == "boolean" then
 | |
| 	    line = concatparam(line,'(tolua_toboolean','(tolua_S,',narg,',',def,')!=0));')
 | |
| 	  else
 | |
| 	   line = concatparam(line,'tolua_to'..t,'(tolua_S,',narg,',',def,'));')
 | |
| 	  end
 | |
| 	else
 | |
| 	line = concatparam(line,'tolua_tousertype(tolua_S,',narg,',',def,'));')
 | |
| 	end
 | |
|   end
 | |
|  end
 | |
| 	return line
 | |
| end
 | |
| 
 | |
| -- Declare variable
 | |
| function classDeclaration:declare (narg)
 | |
|  if self.dim ~= '' and tonumber(self.dim)==nil then
 | |
| 	 output('#ifdef __cplusplus\n')
 | |
| 		output(self:builddeclaration(narg,true))
 | |
| 		output('#else\n')
 | |
| 		output(self:builddeclaration(narg,false))
 | |
| 	 output('#endif\n')
 | |
| 	else
 | |
| 		output(self:builddeclaration(narg,false))
 | |
| 	end
 | |
| end
 | |
| 
 | |
| -- Get parameter value
 | |
| function classDeclaration:getarray (narg)
 | |
|  if self.dim ~= '' then
 | |
| 	 local type = gsub(self.type,'const ','')
 | |
|   output('  {')
 | |
| 	 output('#ifndef TOLUA_RELEASE\n')
 | |
|   local def; if self.def~='' then def=1 else def=0 end
 | |
| 		local t = isbasic(type)
 | |
| 		if (t) then
 | |
|    output('   if (!tolua_is'..t..'array(tolua_S,',narg,',',self.dim,',',def,',&tolua_err))')
 | |
| 		else
 | |
|    output('   if (!tolua_isusertypearray(tolua_S,',narg,',"',type,'",',self.dim,',',def,',&tolua_err))')
 | |
| 		end
 | |
|   output('    goto tolua_lerror;')
 | |
|   output('   else\n')
 | |
| 	 output('#endif\n')
 | |
|   output('   {')
 | |
|   output('    int i;')
 | |
|   output('    for(i=0; i<'..self.dim..';i++)')
 | |
|   local t = isbasic(type)
 | |
|   local ptr = ''
 | |
|   if self.ptr~='' then ptr = '*' end
 | |
|   output('   ',self.name..'[i] = ')
 | |
|   if not t and ptr=='' then output('*') end
 | |
|   output('((',type)
 | |
|   if not t then
 | |
|    output('*')
 | |
|   end
 | |
|   output(') ')
 | |
|   local def = 0
 | |
|   if self.def ~= '' then def = self.def end
 | |
|   if t then
 | |
|    output('tolua_tofield'..t..'(tolua_S,',narg,',i+1,',def,'));')
 | |
|   else
 | |
|    output('tolua_tofieldusertype(tolua_S,',narg,',i+1,',def,'));')
 | |
|   end
 | |
|   output('   }')
 | |
|   output('  }')
 | |
|  end
 | |
| end
 | |
| 
 | |
| -- Get parameter value
 | |
| function classDeclaration:setarray (narg)
 | |
|  if not strfind(self.type,'const%s+') and self.dim ~= '' then
 | |
| 	 local type = gsub(self.type,'const ','')
 | |
|   output('  {')
 | |
|   output('   int i;')
 | |
|   output('   for(i=0; i<'..self.dim..';i++)')
 | |
|   local t,ct = isbasic(type)
 | |
|   if t then
 | |
|    output('    tolua_pushfield'..t..'(tolua_S,',narg,',i+1,(',ct,')',self.name,'[i]);')
 | |
|   else
 | |
|    if self.ptr == '' then
 | |
|      output('   {')
 | |
|      output('#ifdef __cplusplus\n')
 | |
|      output('    void* tolua_obj = new',type,'(',self.name,'[i]);')
 | |
|      output('    tolua_pushfieldusertype_and_takeownership(tolua_S,',narg,',i+1,tolua_obj,"',type,'");')
 | |
|      output('#else\n')
 | |
|      output('    void* tolua_obj = tolua_copy(tolua_S,(void*)&',self.name,'[i],sizeof(',type,'));')
 | |
|      output('    tolua_pushfieldusertype(tolua_S,',narg,',i+1,tolua_obj,"',type,'");')
 | |
|      output('#endif\n')
 | |
|      output('   }')
 | |
|    else
 | |
|     output('   tolua_pushfieldusertype(tolua_S,',narg,',i+1,(void*)',self.name,'[i],"',type,'");')
 | |
|    end
 | |
|   end
 | |
|   output('  }')
 | |
|  end
 | |
| end
 | |
| 
 | |
| -- Free dynamically allocated array
 | |
| function classDeclaration:freearray ()
 | |
|  if self.dim ~= '' and tonumber(self.dim)==nil then
 | |
| 	 output('#ifdef __cplusplus\n')
 | |
| 		output('  delete []',self.name,';')
 | |
| 	 output('#else\n')
 | |
|   output('  free(',self.name,');')
 | |
| 	 output('#endif\n')
 | |
|  end
 | |
| end
 | |
| 
 | |
| -- Pass parameter
 | |
| function classDeclaration:passpar ()
 | |
|  if self.ptr=='&' and not isbasic(self.type) then
 | |
|   output('*'..self.name)
 | |
|  elseif self.ret=='*' then
 | |
|   output('&'..self.name)
 | |
|  else
 | |
|   output(self.name)
 | |
|  end
 | |
| end
 | |
| 
 | |
| -- Return parameter value
 | |
| function classDeclaration:retvalue ()
 | |
|  if self.ret ~= '' then
 | |
|   local t,ct = isbasic(self.type)
 | |
|   if t and t~='' then
 | |
|    output('   tolua_push'..t..'(tolua_S,(',ct,')'..self.name..');')
 | |
|   else
 | |
|    output('   tolua_pushusertype(tolua_S,(void*)'..self.name..',"',self.type,'");')
 | |
|   end
 | |
|   return 1
 | |
|  end
 | |
|  return 0
 | |
| end
 | |
| 
 | |
| -- Internal constructor
 | |
| function _Declaration (t)
 | |
| 
 | |
|  setmetatable(t,classDeclaration)
 | |
|  t:buildnames()
 | |
|  t:checkname()
 | |
|  t:checktype()
 | |
|  local ft = findtype(t.type) or t.type
 | |
|  if not isenum(ft) then
 | |
| 	t.mod, t.type = applytypedef(t.mod, ft)
 | |
|  end
 | |
| 
 | |
|  if t.kind=="var" and (string.find(t.mod, "tolua_property%s") or string.find(t.mod, "tolua_property$")) then
 | |
|  	t.mod = string.gsub(t.mod, "tolua_property", "tolua_property__"..get_property_type())
 | |
|  end
 | |
| 
 | |
|  return t
 | |
| end
 | |
| 
 | |
| -- Constructor
 | |
| -- Expects the string declaration.
 | |
| -- The kind of declaration can be "var" or "func".
 | |
| function Declaration (s,kind,is_parameter)
 | |
| 
 | |
|  -- eliminate spaces if default value is provided
 | |
|  s = gsub(s,"%s*=%s*","=")
 | |
|  s = gsub(s, "%s*<", "<")
 | |
| 
 | |
|  local defb,tmpdef
 | |
|  defb,_,tmpdef = string.find(s, "(=.*)$")
 | |
|  if defb then
 | |
|  	s = string.gsub(s, "=.*$", "")
 | |
|  else
 | |
|  	tmpdef = ''
 | |
|  end
 | |
|  if kind == "var" then
 | |
|   -- check the form: void
 | |
|   if s == '' or s == 'void' then
 | |
|    return _Declaration{type = 'void', kind = kind, is_parameter = is_parameter}
 | |
|   end
 | |
|  end
 | |
| 
 | |
|  -- check the form: mod type*& name
 | |
|  local t = split_c_tokens(s,'%*%s*&')
 | |
|  if t.n == 2 then
 | |
|   if kind == 'func' then
 | |
|    error("#invalid function return type: "..s)
 | |
|   end
 | |
|   --local m = split(t[1],'%s%s*')
 | |
|   local m = split_c_tokens(t[1],'%s+')
 | |
|   return _Declaration{
 | |
|    name = t[2]..tmpdef,
 | |
|    ptr = '*',
 | |
|    ret = '&',
 | |
|    --type = rebuild_template(m[m.n], tb, timpl),
 | |
|    type = m[m.n],
 | |
|    mod = concat(m,1,m.n-1),
 | |
|    is_parameter = is_parameter,
 | |
|    kind = kind
 | |
|   }
 | |
|  end
 | |
| 
 | |
|  -- check the form: mod type** name
 | |
|  t = split_c_tokens(s,'%*%s*%*')
 | |
|  if t.n == 2 then
 | |
|   if kind == 'func' then
 | |
|    error("#invalid function return type: "..s)
 | |
|   end
 | |
|   --local m = split(t[1],'%s%s*')
 | |
|   local m = split_c_tokens(t[1],'%s+')
 | |
|   return _Declaration{
 | |
|    name = t[2]..tmpdef,
 | |
|    ptr = '*',
 | |
|    ret = '*',
 | |
|    --type = rebuild_template(m[m.n], tb, timpl),
 | |
|    type = m[m.n],
 | |
|    mod = concat(m,1,m.n-1),
 | |
|    is_parameter = is_parameter,
 | |
|    kind = kind
 | |
|   }
 | |
|  end
 | |
| 
 | |
|  -- check the form: mod type& name
 | |
|  t = split_c_tokens(s,'&')
 | |
|  if t.n == 2 then
 | |
|   --local m = split(t[1],'%s%s*')
 | |
|   local m = split_c_tokens(t[1],'%s+')
 | |
|   return _Declaration{
 | |
|    name = t[2]..tmpdef,
 | |
|    ptr = '&',
 | |
|    --type = rebuild_template(m[m.n], tb, timpl),
 | |
|    type = m[m.n],
 | |
|    mod = concat(m,1,m.n-1),
 | |
|    is_parameter = is_parameter,
 | |
|    kind = kind
 | |
|   }
 | |
|  end
 | |
| 
 | |
|  -- check the form: mod type* name
 | |
|  local s1 = gsub(s,"(%b\[\])",function (n) return gsub(n,'%*','\1') end)
 | |
|  t = split_c_tokens(s1,'%*')
 | |
|  if t.n == 2 then
 | |
|   t[2] = gsub(t[2],'\1','%*') -- restore * in dimension expression
 | |
|   --local m = split(t[1],'%s%s*')
 | |
|   local m = split_c_tokens(t[1],'%s+')
 | |
|   return _Declaration{
 | |
|    name = t[2]..tmpdef,
 | |
|    ptr = '*',
 | |
|    type = m[m.n],
 | |
|    --type = rebuild_template(m[m.n], tb, timpl),
 | |
|    mod = concat(m,1,m.n-1)   ,
 | |
|    is_parameter = is_parameter,
 | |
|    kind = kind
 | |
|   }
 | |
|  end
 | |
| 
 | |
|  if kind == 'var' then
 | |
|   -- check the form: mod type name
 | |
|   --t = split(s,'%s%s*')
 | |
|   t = split_c_tokens(s,'%s+')
 | |
|   local v
 | |
|   if findtype(t[t.n]) then v = create_varname() else v = t[t.n]; t.n = t.n-1 end
 | |
|   return _Declaration{
 | |
|    name = v..tmpdef,
 | |
|    --type = rebuild_template(t[t.n], tb, timpl),
 | |
|    type = t[t.n],
 | |
|    mod = concat(t,1,t.n-1),
 | |
|    is_parameter = is_parameter,
 | |
|    kind = kind
 | |
|   }
 | |
| 
 | |
|  else -- kind == "func"
 | |
| 
 | |
|   -- check the form: mod type name
 | |
|   --t = split(s,'%s%s*')
 | |
|   t = split_c_tokens(s,'%s+')
 | |
|   local v = t[t.n]  -- last word is the function name
 | |
|   local tp,md
 | |
|   if t.n>1 then
 | |
|    tp = t[t.n-1]
 | |
|    md = concat(t,1,t.n-2)
 | |
|   end
 | |
|   --if tp then tp = rebuild_template(tp, tb, timpl) end
 | |
|   return _Declaration{
 | |
|    name = v,
 | |
|    type = tp,
 | |
|    mod = md,
 | |
|    is_parameter = is_parameter,
 | |
|    kind = kind
 | |
|   }
 | |
|  end
 | |
| 
 | |
| end
 | |
| 
 |