Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/external/tolua/lua/container.lua @ 8836

Last change on this file since 8836 was 5738, checked in by landauf, 15 years ago

merged libraries2 back to trunk

  • Property svn:eol-style set to native
File size: 19.9 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        global_enums[namespace..t.utype] = true
217    end
218end
219
220-- append usertype: return full type
221function classContainer:appendusertype (t)
222    local container
223    if t == (self.original_name or self.name) then
224        container = self.prox
225    else
226        container = self
227    end
228    local ft = getnamespace(container) .. t
229    container.usertypes[t] = ft
230    _usertype[ft] = ft
231    return ft
232end
233
234-- append enum
235function classContainer:appendenum (t)
236    local namespace = getnamespace(classContainer.curr)
237    self.enums.tolua_n = self.enums.tolua_n + 1
238    self.enums[self.enums.tolua_n] = t
239    global_enums[namespace..t.name] = t
240end
241
242-- determine lua function name overload
243function classContainer:overload (lname)
244    if not self.lnames[lname] then
245        self.lnames[lname] = 0
246    else
247        self.lnames[lname] = self.lnames[lname] + 1
248    end
249    return format("%02d",self.lnames[lname])
250end
251
252-- applies typedef: returns the 'the facto' modifier and type
253function classContainer:applytypedef (mod,type)
254    if global_typedefs[type] then
255        --print("found typedef "..global_typedefs[type].type)
256        local mod1, type1 = global_typedefs[type].mod, global_typedefs[type].ftype
257        local mod2, type2 = applytypedef(mod.." "..mod1, type1)
258        --return mod2 .. ' ' .. mod1, type2
259        return mod2, type2
260    end
261    do return mod,type end
262end
263
264-- check if it is a typedef
265function classContainer:istypedef (type)
266    local env = self
267    while env do
268        if env.typedefs then
269            local i=1
270            while env.typedefs[i] do
271                if env.typedefs[i].utype == type then
272                    return type
273                end
274                i = i+1
275            end
276        end
277        env = env.parent
278    end
279    return nil
280end
281
282function find_enum_var(var)
283
284    if tonumber(var) then return var end
285
286    local c = classContainer.curr
287    while c do
288        local ns = getnamespace(c)
289        for k,v in pairs(_global_enums) do
290            if match_type(var, v, ns) then
291                return v
292            end
293        end
294        if c.base and c.base ~= '' then
295            c = _global_classes[c:findtype(c.base)]
296        else
297            c = nil
298        end
299    end
300
301    return var
302end
303
304-- check if is a registered type: return full type or nil
305function classContainer:findtype (t)
306
307    t = string.gsub(t, "=.*", "")
308    if _basic[t] then
309     return t
310    end
311
312    local _,_,em = string.find(t, "([&%*])%s*$")
313    t = string.gsub(t, "%s*([&%*])%s*$", "")
314    p = self
315    while p and type(p)=='table' do
316        local st = getnamespace(p)
317
318        for i=_global_types.n,1,-1 do -- in reverse order
319
320            if match_type(t, _global_types[i], st) then
321                return _global_types[i]..(em or "")
322            end
323        end
324        if p.base and p.base ~= '' and p.base ~= t then
325            --print("type is "..t..", p is "..p.base.." self.type is "..self.type.." self.name is "..self.name)
326            p = _global_classes[p:findtype(p.base)]
327        else
328            p = nil
329        end
330    end
331
332    return nil
333end
334
335function append_global_type(t, class)
336    _global_types.n = _global_types.n +1
337    _global_types[_global_types.n] = t
338    _global_types_hash[t] = 1
339    if class then append_class_type(t, class) end
340end
341
342function append_class_type(t,class)
343    if _global_classes[t] then
344        class.flags = _global_classes[t].flags
345        class.lnames = _global_classes[t].lnames
346        if _global_classes[t].base and (_global_classes[t].base ~= '') then
347            class.base = _global_classes[t].base or class.base
348        end
349    end
350    _global_classes[t] = class
351    class.flags = class.flags or {}
352end
353
354function match_type(childtype, regtype, st)
355--print("findtype "..childtype..", "..regtype..", "..st)
356    local b,e = string.find(regtype, childtype, -string.len(childtype), true)
357    if b then
358
359        if e == string.len(regtype) and
360                (b == 1 or (string.sub(regtype, b-1, b-1) == ':' and
361                string.sub(regtype, 1, b-1) == string.sub(st, 1, b-1))) then
362            return true
363        end
364    end
365
366    return false
367end
368
369function findtype_on_childs(self, t)
370
371    local tchild
372    if self.classtype == 'class' or self.classtype == 'namespace' then
373        for k,v in ipairs(self) do
374            if v.classtype == 'class' or v.classtype == 'namespace' then
375                if v.typedefs and v.typedefs[t] then
376                 return v.typedefs[t]
377                elseif v.usertypes and v.usertypes[t] then
378                 return v.usertypes[t]
379                end
380                tchild = findtype_on_childs(v, t)
381                if tchild then return tchild end
382            end
383        end
384    end
385    return nil
386
387end
388
389function classContainer:isenum (type)
390    if global_enums[type] then
391        return type
392    else
393        return false
394    end
395
396    local basetype = gsub(type,"^.*::","")
397    local env = self
398    while env do
399        if env.enums then
400            local i=1
401            while env.enums[i] do
402                if env.enums[i].name == basetype then
403                    return true
404                end
405                i = i+1
406            end
407        end
408        env = env.parent
409    end
410    return false
411end
412
413methodisvirtual = false -- a global
414
415-- parse chunk
416function classContainer:doparse (s)
417    --print ("parse "..s)
418
419    -- try the parser hook
420    do
421        local sub = parser_hook(s)
422        if sub then
423            return sub
424        end
425    end
426
427    -- try the null statement
428    do
429        local b,e,code = string.find(s, "^%s*;")
430        if b then
431            return strsub(s,e+1)
432        end
433    end
434
435    -- try empty verbatim line
436    do
437        local b,e,code = string.find(s, "^%s*$\n")
438        if b then
439            return strsub(s,e+1)
440        end
441    end
442
443    -- try Lua code
444    do
445        local b,e,code = strfind(s,"^%s*(%b\1\2)")
446        if b then
447            Code(strsub(code,2,-2))
448            return strsub(s,e+1)
449        end
450    end
451
452    -- try C code
453    do
454        local b,e,code = strfind(s,"^%s*(%b\3\4)")
455        if b then
456            code = '{'..strsub(code,2,-2)..'\n}\n'
457            Verbatim(code,'r')        -- verbatim code for 'r'egister fragment
458            return strsub(s,e+1)
459        end
460    end
461
462    -- try C code for preamble section
463    do
464        local b,e,code = string.find(s, "^%s*(%b\5\6)")
465        if b then
466            code = string.sub(code, 2, -2).."\n"
467            Verbatim(code, '')
468            return string.sub(s, e+1)
469        end
470    end
471
472    -- try default_property directive
473    do
474        local b,e,ptype = strfind(s, "^%s*TOLUA_PROPERTY_TYPE%s*%(+%s*([^%)%s]*)%s*%)+%s*;?")
475        if b then
476            if not ptype or ptype == "" then
477                ptype = "default"
478            end
479            self:set_property_type(ptype)
480            return strsub(s, e+1)
481        end
482    end
483
484    -- try protected_destructor directive
485    do
486        local b,e = string.find(s, "^%s*TOLUA_PROTECTED_DESTRUCTOR%s*;?")
487        if b then
488            if self.set_protected_destructor then
489                self:set_protected_destructor(true)
490            end
491            return strsub(s, e+1)
492        end
493    end
494
495    -- try 'extern' keyword
496    do
497        local b,e = string.find(s, "^%s*extern%s+")
498        if b then
499            -- do nothing
500            return strsub(s, e+1)
501        end
502    end
503
504    -- try 'virtual' keyworkd
505    do
506        local b,e = string.find(s, "^%s*virtual%s+")
507        if b then
508            methodisvirtual = true
509            return strsub(s, e+1)
510        end
511    end
512
513    -- try labels (public, private, etc)
514    do
515        local b,e = string.find(s, "^%s*%w*%s*:[^:]")
516        if b then
517            return strsub(s, e) -- preserve the [^:]
518        end
519    end
520
521    -- try module
522    do
523        local b,e,name,body = strfind(s,"^%s*module%s%s*([_%w][_%w]*)%s*(%b{})%s*")
524        if b then
525            _curr_code = strsub(s,b,e)
526            Module(name,body)
527            return strsub(s,e+1)
528        end
529    end
530
531    -- try namesapce
532    do
533        local b,e,name,body = strfind(s,"^%s*namespace%s%s*([_%w][_%w]*)%s*(%b{})%s*;?")
534        if b then
535            _curr_code = strsub(s,b,e)
536            Namespace(name,body)
537            return strsub(s,e+1)
538        end
539    end
540
541    -- try define
542    do
543        local b,e,name = strfind(s,"^%s*#define%s%s*([^%s]*)[^\n]*\n%s*")
544        if b then
545            _curr_code = strsub(s,b,e)
546            Define(name)
547            return strsub(s,e+1)
548        end
549    end
550
551    -- try enumerates
552
553    do
554        local b,e,name,body,varname = strfind(s,"^%s*enum%s+(%S*)%s*(%b{})%s*([^%s;]*)%s*;?%s*")
555        if b then
556            --error("#Sorry, declaration of enums and variables on the same statement is not supported.\nDeclare your variable separately (example: '"..name.." "..varname..";')")
557            _curr_code = strsub(s,b,e)
558            Enumerate(name,body,varname)
559            return strsub(s,e+1)
560        end
561    end
562
563    -- do
564    --    local b,e,name,body = strfind(s,"^%s*enum%s+(%S*)%s*(%b{})%s*;?%s*")
565    --    if b then
566    --        _curr_code = strsub(s,b,e)
567    --        Enumerate(name,body)
568    --        return strsub(s,e+1)
569    --    end
570    -- end
571
572    do
573        local b,e,body,name = strfind(s,"^%s*typedef%s+enum[^{]*(%b{})%s*([%w_][^%s]*)%s*;%s*")
574        if b then
575        _curr_code = strsub(s,b,e)
576        Enumerate(name,body)
577        return strsub(s,e+1)
578        end
579    end
580
581    -- try operator
582    do
583        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*")
584        if not b then
585            -- try inline
586            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*")
587        end
588        if not b then
589            -- try cast operator
590            b,e,decl,kind,arg,const = strfind(s, "^%s*(operator)%s+([%w_:%d<>%*%&%s]+)%s*(%b())%s*(c?o?n?s?t?)");
591            if b then
592                local _,ie = string.find(s, "^%s*%b{}", e+1)
593                if ie then
594                    e = ie
595                end
596            end
597        end
598        if b then
599            _curr_code = strsub(s,b,e)
600            Operator(decl,kind,arg,const)
601            return strsub(s,e+1)
602        end
603    end
604
605    -- try function
606    do
607        --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*")
608        local b,e,decl,arg,const,virt = strfind(s,"^%s*([^%(\n]+)%s*(%b())%s*(c?o?n?s?t?)%s*(=?%s*0?)%s*;%s*")
609        if not b then
610            -- try function with template
611            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*")
612        end
613        if not b then
614            -- try a single letter function name
615            b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?)%s*;%s*")
616        end
617        if b then
618            if virt and string.find(virt, "[=0]") then
619                if self.flags then
620                    self.flags.pure_virtual = true
621                end
622            end
623            _curr_code = strsub(s,b,e)
624            Function(decl,arg,const)
625            return strsub(s,e+1)
626        end
627    end
628
629    -- try inline function
630    do
631        local b,e,decl,arg,const = strfind(s,"^%s*([^%(\n]+)%s*(%b())%s*(c?o?n?s?t?)[^;{]*%b{}%s*;?%s*")
632        --local b,e,decl,arg,const = strfind(s,"^%s*([~_%w][_@%w%s%*&:<>]*[_%w>])%s*(%b())%s*(c?o?n?s?t?)[^;]*%b{}%s*;?%s*")
633        if not b then
634            -- try a single letter function name
635            b,e,decl,arg,const = strfind(s,"^%s*([_%w])%s*(%b())%s*(c?o?n?s?t?).-%b{}%s*;?%s*")
636        end
637        if b then
638            _curr_code = strsub(s,b,e)
639            Function(decl,arg,const)
640            return strsub(s,e+1)
641        end
642    end
643
644    -- try class
645    do
646        local b,e,name,base,body
647        base = '' body = ''
648        b,e,name = strfind(s,"^%s*class%s*([_%w][_%w@]*)%s*;")  -- dummy class
649        if not b then
650            b,e,name = strfind(s,"^%s*struct%s*([_%w][_%w@]*)%s*;")    -- dummy struct
651            if not b then
652                b,e,name,base,body = strfind(s,"^%s*class%s*([_%w][_%w@]*)%s*(.-)%s*(%b{})%s*;%s*")
653                if not b then
654                    b,e,name,base,body = strfind(s,"^%s*struct%s*([_%w][_%w@]*)%s*(.-)%s*(%b{})%s*;%s*")
655                    if not b then
656                        b,e,name,base,body = strfind(s,"^%s*union%s*([_%w][_%w@]*)%s*(.-)%s*(%b{})%s*;%s*")
657                        if not b then
658                            base = ''
659                            b,e,body,name = strfind(s,"^%s*typedef%s%s*struct%s%s*[_%w]*%s*(%b{})%s*([_%w][_%w@]*)%s*;%s*")
660                        end
661                    end
662                end
663            end
664        end
665        if b then
666            if base ~= '' then
667                base = string.gsub(base, "^%s*:%s*", "")
668                base = string.gsub(base, "%s*public%s*", "")
669                base = split(base, ",")
670                --local b,e
671                --b,e,base = strfind(base,".-([_%w][_%w<>,:]*)$")
672            else
673                base = {}
674            end
675            _curr_code = strsub(s,b,e)
676            Class(name,base,body)
677            return strsub(s,e+1)
678        end
679    end
680
681    -- try typedef
682    do
683        local b,e,types = strfind(s,"^%s*typedef%s%s*(.-)%s*;%s*")
684        if b then
685            _curr_code = strsub(s,b,e)
686            Typedef(types)
687            return strsub(s,e+1)
688        end
689    end
690
691    -- try variable
692    do
693        local b,e,decl = strfind(s,"^%s*([_%w][_@%s%w%d%*&:<>,]*[_%w%d])%s*;%s*")
694        if b then
695            _curr_code = strsub(s,b,e)
696
697            local list = split_c_tokens(decl, ",")
698            Variable(list[1])
699            if list.n > 1 then
700                local _,_,type = strfind(list[1], "(.-)%s+([^%s]*)$");
701
702                local i =2;
703                while list[i] do
704                    Variable(type.." "..list[i])
705                    i=i+1
706                end
707            end
708            --Variable(decl)
709            return strsub(s,e+1)
710        end
711    end
712
713    -- try string
714    do
715        local b,e,decl = strfind(s,"^%s*([_%w]?[_%s%w%d]-char%s+[_@%w%d]*%s*%[%s*%S+%s*%])%s*;%s*")
716        if b then
717            _curr_code = strsub(s,b,e)
718            Variable(decl)
719            return strsub(s,e+1)
720        end
721    end
722
723    -- try array
724    do
725        local b,e,decl = strfind(s,"^%s*([_%w][][_@%s%w%d%*&:]*[]_%w%d])%s*;%s*")
726        if b then
727            _curr_code = strsub(s,b,e)
728            Array(decl)
729            return strsub(s,e+1)
730        end
731    end
732
733    -- no matching
734    if gsub(s,"%s%s*","") ~= "" then
735        _curr_code = s
736        error("#parse error")
737    else
738        return ""
739    end
740
741end
742
743function classContainer:parse (s)
744
745    self.curr_member_access = nil
746
747    while s ~= '' do
748        s = self:doparse(s)
749        methodisvirtual = false
750    end
751end
752
753
754-- property types
755
756function get_property_type()
757
758    return classContainer.curr:get_property_type()
759end
760
761function classContainer:set_property_type(ptype)
762    ptype = string.gsub(ptype, "^%s*", "")
763    ptype = string.gsub(ptype, "%s*$", "")
764
765    self.property_type = ptype
766end
767
768function classContainer:get_property_type()
769    return self.property_type or (self.parent and self.parent:get_property_type()) or "default"
770end
Note: See TracBrowser for help on using the repository browser.