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