1-- tolua: container abstract 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-- table to store namespaced typedefs/enums in global scope 13global_typedefs = {} 14global_enums = {} 15 16-- Container class 17-- Represents a container of features to be bound 18-- to lua. 19classContainer = 20{ 21 curr = nil, 22} 23classContainer.__index = classContainer 24setmetatable(classContainer,classFeature) 25 26-- output tags 27function classContainer:decltype () 28 push(self) 29 local i=1 30 while self[i] do 31 self[i]:decltype() 32 i = i+1 33 end 34 pop() 35end 36 37 38-- write support code 39function classContainer:supcode () 40 41 if not self:check_public_access() then 42 return 43 end 44 45 push(self) 46 local i=1 47 while self[i] do 48 if self[i]:check_public_access() then 49 self[i]:supcode() 50 end 51 i = i+1 52 end 53 pop() 54end 55 56function classContainer:hasvar () 57 local i=1 58 while self[i] do 59 if self[i]:isvariable() then 60 return 1 61 end 62 i = i+1 63 end 64 return 0 65end 66 67-- Internal container constructor 68function _Container (self) 69 setmetatable(self,classContainer) 70 self.n = 0 71 self.typedefs = {tolua_n=0} 72 self.usertypes = {} 73 self.enums = {tolua_n=0} 74 self.lnames = {} 75 return self 76end 77 78-- push container 79function push (t) 80 t.prox = classContainer.curr 81 classContainer.curr = t 82end 83 84-- pop container 85function pop () 86--print("name",classContainer.curr.name) 87--foreach(classContainer.curr.usertypes,print) 88--print("______________") 89 classContainer.curr = classContainer.curr.prox 90end 91 92-- get current namespace 93function getcurrnamespace () 94 return getnamespace(classContainer.curr) 95end 96 97-- append to current container 98function append (t) 99 return classContainer.curr:append(t) 100end 101 102-- append typedef to current container 103function appendtypedef (t) 104 return classContainer.curr:appendtypedef(t) 105end 106 107-- append usertype to current container 108function appendusertype (t) 109 return classContainer.curr:appendusertype(t) 110end 111 112-- append enum to current container 113function appendenum (t) 114 return classContainer.curr:appendenum(t) 115end 116 117-- substitute typedef 118function applytypedef (mod,type) 119 return classContainer.curr:applytypedef(mod,type) 120end 121 122-- check if is type 123function findtype (type) 124 local t = classContainer.curr:findtype(type) 125 return t 126end 127 128-- check if is typedef 129function istypedef (type) 130 return classContainer.curr:istypedef(type) 131end 132 133-- get fulltype (with namespace) 134function fulltype (t) 135 local curr = classContainer.curr 136 while curr do 137 if curr then 138 if curr.typedefs and curr.typedefs[t] then 139 return curr.typedefs[t] 140 elseif curr.usertypes and curr.usertypes[t] then 141 return curr.usertypes[t] 142 end 143 end 144 curr = curr.prox 145 end 146 return t 147end 148 149-- checks if it requires collection 150function classContainer:requirecollection (t) 151 push(self) 152 local i=1 153 local r = false 154 while self[i] do 155 r = self[i]:requirecollection(t) or r 156 i = i+1 157 end 158 pop() 159 return r 160end 161 162 163-- get namesapce 164function getnamespace (curr) 165 local namespace = '' 166 while curr do 167 if curr and 168 ( curr.classtype == 'class' or curr.classtype == 'namespace') 169 then 170 namespace = (curr.original_name or curr.name) .. '::' .. namespace 171 --namespace = curr.name .. '::' .. namespace 172 end 173 curr = curr.prox 174 end 175 return namespace 176end 177 178-- get namespace (only namespace) 179function getonlynamespace () 180 local curr = classContainer.curr 181 local namespace = '' 182 while curr do 183 if curr.classtype == 'class' then 184 return namespace 185 elseif curr.classtype == 'namespace' then 186 namespace = curr.name .. '::' .. namespace 187 end 188 curr = curr.prox 189 end 190 return namespace 191end 192 193-- check if is enum 194function isenum (type) 195 return classContainer.curr:isenum(type) 196end 197 198-- append feature to container 199function classContainer:append (t) 200 self.n = self.n + 1 201 self[self.n] = t 202 t.parent = self 203end 204 205-- append typedef 206function classContainer:appendtypedef (t) 207 local namespace = getnamespace(classContainer.curr) 208 self.typedefs.tolua_n = self.typedefs.tolua_n + 1 209 self.typedefs[self.typedefs.tolua_n] = t 210 self.typedefs[t.utype] = namespace .. t.utype 211 global_typedefs[namespace..t.utype] = t 212 t.ftype = findtype(t.type) or t.type 213 --print("appending typedef "..t.utype.." as "..namespace..t.utype.." with ftype "..t.ftype) 214 append_global_type(namespace..t.utype) 215 if t.ftype and isenum(t.ftype) then 216 217 global_enums[namespace..t.utype] = true 218 end 219end 220 221-- append usertype: return full type 222function classContainer:appendusertype (t) 223 local container 224 if t == (self.original_name or self.name) then 225 container = self.prox 226 else 227 container = self 228 end 229 local ft = getnamespace(container) .. t 230 container.usertypes[t] = ft 231 _usertype[ft] = ft 232 return ft 233end 234 235-- append enum 236function classContainer:appendenum (t) 237 local namespace = getnamespace(classContainer.curr) 238 self.enums.tolua_n = self.enums.tolua_n + 1 239 self.enums[self.enums.tolua_n] = t 240 global_enums[namespace..t.name] = t 241end 242 243-- determine lua function name overload 244function classContainer:overload (lname) 245 if not self.lnames[lname] then 246 self.lnames[lname] = 0 247 else 248 self.lnames[lname] = self.lnames[lname] + 1 249 end 250 return format("%02d",self.lnames[lname]) 251end 252 253-- applies typedef: returns the 'the facto' modifier and type 254function classContainer:applytypedef (mod,type) 255 if global_typedefs[type] then 256 --print("found typedef "..global_typedefs[type].type) 257 local mod1, type1 = global_typedefs[type].mod, global_typedefs[type].ftype 258 local mod2, type2 = applytypedef(mod.." "..mod1, type1) 259 --return mod2 .. ' ' .. mod1, type2 260 return mod2, type2 261 end 262 do return mod,type end 263end 264 265-- check if it is a typedef 266function classContainer:istypedef (type) 267 local env = self 268 while env do 269 if env.typedefs then 270 local i=1 271 while env.typedefs[i] do 272 if env.typedefs[i].utype == type then 273 return type 274 end 275 i = i+1 276 end 277 end 278 env = env.parent 279 end 280 return nil 281end 282 283function find_enum_var(var) 284 285 if tonumber(var) then return var end 286 287 local c = classContainer.curr 288 while c do 289 local ns = getnamespace(c) 290 for k,v in pairs(_global_enums) do 291 if match_type(var, v, ns) then 292 return v 293 end 294 end 295 if c.base and c.base ~= '' then 296 c = _global_classes[c:findtype(c.base)] 297 else 298 c = nil 299 end 300 end 301 302 return var 303end 304 305-- check if is a registered type: return full type or nil 306function classContainer:findtype (t) 307 308 t = string.gsub(t, "=.*", "") 309 if _basic[t] then 310 return t 311 end 312 313 local _,_,em = string.find(t, "([&%*])%s*$") 314 t = string.gsub(t, "%s*([&%*])%s*$", "") 315 p = self 316 while p and type(p)=='table' do 317 local st = getnamespace(p) 318 319 for i=_global_types.n,1,-1 do -- in reverse order 320 321 if match_type(t, _global_types[i], st) then 322 return _global_types[i]..(em or "") 323 end 324 end 325 if p.base and p.base ~= '' and p.base ~= t then 326 --print("type is "..t..", p is "..p.base.." self.type is "..self.type.." self.name is "..self.name) 327 p = _global_classes[p:findtype(p.base)] 328 else 329 p = nil 330 end 331 end 332 333 return nil 334end 335 336function append_global_type(t, class) 337 _global_types.n = _global_types.n +1 338 _global_types[_global_types.n] = t 339 _global_types_hash[t] = 1 340 if class then append_class_type(t, class) end 341end 342 343function append_class_type(t,class) 344 if _global_classes[t] then 345 class.flags = _global_classes[t].flags 346 class.lnames = _global_classes[t].lnames 347 if _global_classes[t].base and (_global_classes[t].base ~= '') then 348 class.base = _global_classes[t].base or class.base 349 end 350 end 351 _global_classes[t] = class 352 class.flags = class.flags or {} 353end 354 355function match_type(childtype, regtype, st) 356--print("findtype "..childtype..", "..regtype..", "..st) 357 local b,e = string.find(regtype, childtype, -string.len(childtype), true) 358 if b then 359 360 if e == string.len(regtype) and 361 (b == 1 or (string.sub(regtype, b-1, b-1) == ':' and 362 string.sub(regtype, 1, b-1) == string.sub(st, 1, b-1))) then 363 return true 364 end 365 end 366 367 return false 368end 369 370function findtype_on_childs(self, t) 371 372 local tchild 373 if self.classtype == 'class' or self.classtype == 'namespace' then 374 for k,v in ipairs(self) do 375 if v.classtype == 'class' or v.classtype == 'namespace' then 376 if v.typedefs and v.typedefs[t] then 377 return v.typedefs[t] 378 elseif v.usertypes and v.usertypes[t] then 379 return v.usertypes[t] 380 end 381 tchild = findtype_on_childs(v, t) 382 if tchild then return tchild end 383 end 384 end 385 end 386 return nil 387 388end 389 390function classContainer:isenum (type) 391 if global_enums[type] then 392 return type 393 else 394 return false 395 end 396 397 local basetype = gsub(type,"^.*::","") 398 local env = self 399 while env do 400 if env.enums then 401 local i=1 402 while env.enums[i] do 403 if env.enums[i].name == basetype then 404 return true 405 end 406 i = i+1 407 end 408 end 409 env = env.parent 410 end 411 return false 412end 413 414methodisvirtual = false -- a global 415 416-- parse chunk 417function classContainer:doparse (s) 418--print ("parse "..s) 419 420 -- try the parser hook 421 do 422 local sub = parser_hook(s) 423 if sub then 424 return sub 425 end 426 end 427 428 -- try the null statement 429 do 430 local b,e,code = string.find(s, "^%s*;") 431 if b then 432 return strsub(s,e+1) 433 end 434 end 435 436 -- try empty verbatim line 437 do 438 local b,e,code = string.find(s, "^%s*$\n") 439 if b then 440 return strsub(s,e+1) 441 end 442 end 443 444 -- try Lua code 445 do 446 local b,e,code = strfind(s,"^%s*(%b\1\2)") 447 if b then 448 Code(strsub(code,2,-2)) 449 return strsub(s,e+1) 450 end 451 end 452 453 -- try C code 454 do 455 local b,e,code = strfind(s,"^%s*(%b\3\4)") 456 if b then 457 code = '{'..strsub(code,2,-2)..'\n}\n' 458 Verbatim(code,'r') -- verbatim code for 'r'egister fragment 459 return strsub(s,e+1) 460 end 461 end 462 463 -- try C code for preamble section 464 do 465 local b,e,code = string.find(s, "^%s*(%b\5\6)") 466 if b then 467 code = string.sub(code, 2, -2).."\n" 468 Verbatim(code, '') 469 return string.sub(s, e+1) 470 end 471 end 472 473 -- try default_property directive 474 do 475 local b,e,ptype = strfind(s, "^%s*TOLUA_PROPERTY_TYPE%s*%(+%s*([^%)%s]*)%s*%)+%s*;?") 476 if b then 477 if not ptype or ptype == "" then 478 ptype = "default" 479 end 480 self:set_property_type(ptype) 481 return strsub(s, e+1) 482 end 483 end 484 485 -- try protected_destructor directive 486 do 487 local b,e = string.find(s, "^%s*TOLUA_PROTECTED_DESTRUCTOR%s*;?") 488 if b then 489 if self.set_protected_destructor then 490 self:set_protected_destructor(true) 491 end 492 return strsub(s, e+1) 493 end 494 end 495 496 -- try 'extern' keyword 497 do 498 local b,e = string.find(s, "^%s*extern%s+") 499 if b then 500 -- do nothing 501 return strsub(s, e+1) 502 end 503 end 504 505 -- try 'virtual' keyworkd 506 do 507 local b,e = string.find(s, "^%s*virtual%s+") 508 if b then 509 methodisvirtual = true 510 return strsub(s, e+1) 511 end 512 end 513 514 -- try labels (public, private, etc) 515 do 516 local b,e = string.find(s, "^%s*%w*%s*:[^:]") 517 if b then 518 return strsub(s, e) -- preserve the [^:] 519 end 520 end 521 522 -- try module 523 do 524 local b,e,name,body = strfind(s,"^%s*module%s%s*([_%w][_%w]*)%s*(%b{})%s*") 525 if b then 526 _curr_code = strsub(s,b,e) 527 Module(name,body) 528 return strsub(s,e+1) 529 end 530 end 531 532 -- try namesapce 533 do 534 local b,e,name,body = strfind(s,"^%s*namespace%s%s*([_%w][_%w]*)%s*(%b{})%s*;?") 535 if b then 536 _curr_code = strsub(s,b,e) 537 Namespace(name,body) 538 return strsub(s,e+1) 539 end 540 end 541 542 -- try define 543 do 544 local b,e,name = strfind(s,"^%s*#define%s%s*([^%s]*)[^\n]*\n%s*") 545 if b then 546 _curr_code = strsub(s,b,e) 547 Define(name) 548 return strsub(s,e+1) 549 end 550 end 551 552 -- try enumerates 553 554 do 555 local b,e,name,body,varname = strfind(s,"^%s*enum%s+(%S*)%s*(%b{})%s*([^%s;]*)%s*;?%s*") 556 if b then 557 --error("#Sorry, declaration of enums and variables on the same statement is not supported.\nDeclare your variable separately (example: '"..name.." "..varname..";')") 558 _curr_code = strsub(s,b,e) 559 Enumerate(name,body,varname) 560 return strsub(s,e+1) 561 end 562 end 563 564-- do 565-- local b,e,name,body = strfind(s,"^%s*enum%s+(%S*)%s*(%b{})%s*;?%s*") 566-- if b then 567-- _curr_code = strsub(s,b,e) 568-- Enumerate(name,body) 569-- return strsub(s,e+1) 570-- end 571-- end 572 573 do 574 local b,e,body,name = strfind(s,"^%s*typedef%s+enum[^{]*(%b{})%s*([%w_][^%s]*)%s*;%s*") 575 if b then 576 _curr_code = strsub(s,b,e) 577 Enumerate(name,body) 578 return strsub(s,e+1) 579 end 580 end 581 582 -- try operator 583 do 584 local b,e,decl,kind,arg,const = strfind(s,"^%s*([_%w][_%w%s%*&:<>,]-%s+operator)%s*([^%s][^%s]*)%s*(%b())%s*(c?o?n?s?t?)%s*;%s*") 585 if not b then 586 -- try inline 587 b,e,decl,kind,arg,const = strfind(s,"^%s*([_%w][_%w%s%*&:<>,]-%s+operator)%s*([^%s][^%s]*)%s*(%b())%s*(c?o?n?s?t?)[%s\n]*%b{}%s*;?%s*") 588 end 589 if not b then 590 -- try cast operator 591 b,e,decl,kind,arg,const = strfind(s, "^%s*(operator)%s+([%w_:%d<>%*%&%s]+)%s*(%b())%s*(c?o?n?s?t?)"); 592 if b then 593 local _,ie = string.find(s, "^%s*%b{}", e+1) 594 if ie then 595 e = ie 596 end 597 end 598 end 599 if b then 600 _curr_code = strsub(s,b,e) 601 Operator(decl,kind,arg,const) 602 return strsub(s,e+1) 603 end 604 end 605 606 -- try function 607 do 608 --local b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&:<>]*[_%w])%s*(%b())%s*(c?o?n?s?t?)%s*=?%s*0?%s*;%s*") 609 local b,e,decl,arg,const,virt = strfind(s,"^%s*([^%(\n]+)%s*(%b())%s*(c?o?n?s?t?)%s*(=?%s*0?)%s*;%s*") 610 if not b then 611 -- try function with template 612 b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&:<>]*[_%w]%b<>)%s*(%b())%s*(c?o?n?s?t?)%s*=?%s*0?%s*;%s*") 613 end 614 if not b then 615 -- try a single letter function name 616 b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?)%s*;%s*") 617 end 618 if b then 619 if virt and string.find(virt, "[=0]") then 620 if self.flags then 621 self.flags.pure_virtual = true 622 end 623 end 624 _curr_code = strsub(s,b,e) 625 Function(decl,arg,const) 626 return strsub(s,e+1) 627 end 628 end 629 630 -- try inline function 631 do 632 local b,e,decl,arg,const = strfind(s,"^%s*([^%(\n]+)%s*(%b())%s*(c?o?n?s?t?)[^;{]*%b{}%s*;?%s*") 633 --local b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&:<>]*[_%w>])%s*(%b())%s*(c?o?n?s?t?)[^;]*%b{}%s*;?%s*") 634 if not b then 635 -- try a single letter function name 636 b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?).-%b{}%s*;?%s*") 637 end 638 if b then 639 _curr_code = strsub(s,b,e) 640 Function(decl,arg,const) 641 return strsub(s,e+1) 642 end 643 end 644 645 -- try class 646 do 647 local b,e,name,base,body 648 base = '' body = '' 649 b,e,name = strfind(s,"^%s*class%s*([_%w][_%w@]*)%s*;") -- dummy class 650 if not b then 651 b,e,name = strfind(s,"^%s*struct%s*([_%w][_%w@]*)%s*;") -- dummy struct 652 if not b then 653 b,e,name,base,body = strfind(s,"^%s*class%s*([_%w][_%w@]*)%s*(.-)%s*(%b{})%s*;%s*") 654 if not b then 655 b,e,name,base,body = strfind(s,"^%s*struct%s*([_%w][_%w@]*)%s*(.-)%s*(%b{})%s*;%s*") 656 if not b then 657 b,e,name,base,body = strfind(s,"^%s*union%s*([_%w][_%w@]*)%s*(.-)%s*(%b{})%s*;%s*") 658 if not b then 659 base = '' 660 b,e,body,name = strfind(s,"^%s*typedef%s%s*struct%s%s*[_%w]*%s*(%b{})%s*([_%w][_%w@]*)%s*;%s*") 661 end 662 end 663 end 664 end 665 end 666 if b then 667 if base ~= '' then 668 base = string.gsub(base, "^%s*:%s*", "") 669 base = string.gsub(base, "%s*public%s*", "") 670 base = split(base, ",") 671 --local b,e 672 --b,e,base = strfind(base,".-([_%w][_%w<>,:]*)$") 673 else 674 base = {} 675 end 676 _curr_code = strsub(s,b,e) 677 Class(name,base,body) 678 return strsub(s,e+1) 679 end 680 end 681 682 -- try typedef 683 do 684 local b,e,types = strfind(s,"^%s*typedef%s%s*(.-)%s*;%s*") 685 if b then 686 _curr_code = strsub(s,b,e) 687 Typedef(types) 688 return strsub(s,e+1) 689 end 690 end 691 692 -- try variable 693 do 694 local b,e,decl = strfind(s,"^%s*([_%w][_@%s%w%d%*&:<>,]*[_%w%d])%s*;%s*") 695 if b then 696 _curr_code = strsub(s,b,e) 697 698 local list = split_c_tokens(decl, ",") 699 Variable(list[1]) 700 if list.n > 1 then 701 local _,_,type = strfind(list[1], "(.-)%s+([^%s]*)$"); 702 703 local i =2; 704 while list[i] do 705 Variable(type.." "..list[i]) 706 i=i+1 707 end 708 end 709 --Variable(decl) 710 return strsub(s,e+1) 711 end 712 end 713 714 -- try string 715 do 716 local b,e,decl = strfind(s,"^%s*([_%w]?[_%s%w%d]-char%s+[_@%w%d]*%s*%[%s*%S+%s*%])%s*;%s*") 717 if b then 718 _curr_code = strsub(s,b,e) 719 Variable(decl) 720 return strsub(s,e+1) 721 end 722 end 723 724 -- try array 725 do 726 local b,e,decl = strfind(s,"^%s*([_%w][][_@%s%w%d%*&:]*[]_%w%d])%s*;%s*") 727 if b then 728 _curr_code = strsub(s,b,e) 729 Array(decl) 730 return strsub(s,e+1) 731 end 732 end 733 734 -- no matching 735 if gsub(s,"%s%s*","") ~= "" then 736 _curr_code = s 737 error("#parse error") 738 else 739 return "" 740 end 741 742end 743 744function classContainer:parse (s) 745 746 self.curr_member_access = nil 747 748 while s ~= '' do 749 s = self:doparse(s) 750 methodisvirtual = false 751 end 752end 753 754 755-- property types 756 757function get_property_type() 758 759 return classContainer.curr:get_property_type() 760end 761 762function classContainer:set_property_type(ptype) 763 ptype = string.gsub(ptype, "^%s*", "") 764 ptype = string.gsub(ptype, "%s*$", "") 765 766 self.property_type = ptype 767end 768 769function classContainer:get_property_type() 770 return self.property_type or (self.parent and self.parent:get_property_type()) or "default" 771end 772