Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/tolua/lua/container.lua @ 2663

Last change on this file since 2663 was 2087, checked in by landauf, 16 years ago

merged objecthierarchy branch back to trunk

  • Property svn:eol-style set to native
File size: 16.6 KB
Line 
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
Note: See TracBrowser for help on using the repository browser.