Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 3151 was 2710, checked in by rgrieder, 16 years ago

Merged buildsystem3 containing buildsystem2 containing Adi's buildsystem branch back to the trunk.
Please update the media directory if you were not using buildsystem3 before.

  • 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.