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