Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gui/src/tolua/lua/container.lua @ 1650

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

added preliminary automatic creation of tolua generator that creates the actual executable
(please don't ask about the egg-hen question…)

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.