Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/pch/src/tolua/lua/function.lua @ 3127

Last change on this file since 3127 was 3127, checked in by rgrieder, 15 years ago

Update to tolua 1.0.93

  • Property svn:eol-style set to native
File size: 22.1 KB
Line 
1-- tolua: function class
2-- Written by Waldemar Celes
3-- TeCGraf/PUC-Rio
4-- Jul 1998
5-- $Id: function.lua 1597 2008-03-11 22:25:01Z ice-drezday $
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
13-- CEGUILua mod
14-- exception handling
15-- patch from Tov
16-- modded by Lindquist
17exceptionDefs = exceptionDefs or {}
18exceptionDefs["std::exception"]             = {}
19exceptionDefs["std::exception"]["var"]      = "&e"
20exceptionDefs["std::exception"]["c_str"]    = "e.what()"
21
22exceptionDefs["any"]                        = {}
23exceptionDefs["any"]["var"]                 = ""
24exceptionDefs["any"]["c_str"]               = '"Unknown"'
25
26exceptionMessageBufferSize = 512
27
28function outputExceptionError(f,e,errBuf)
29    -- if the exception is not "..." then use the "c_str" info the get a real exception message
30    local messageC_str = true
31    if e.name == "any" then
32        messageC_str = false
33    end
34
35    -- make a default e.ret if empty
36    if not e.ret or e.ret == "" then
37        e.ret = "nil,message"
38    end
39
40    -- create a default exceptionDef if we dont have one
41    if not exceptionDefs[e.name] then
42        exceptionDefs[e.name] = {}
43        exceptionDefs[e.name].var = "&e"
44        exceptionDefs[e.name].c_str = '"Unknown"'
45    end
46
47    -- print catch header
48    local nameToEcho = e.name
49    if nameToEcho == "any" then
50        nameToEcho = "..."
51    end
52    if e.ret == "nil" then
53        output("catch(",nameToEcho," CEGUIDeadException(",exceptionDefs[e.name].var,"))\n{\n")
54    else
55        output("catch(",nameToEcho,exceptionDefs[e.name].var,")\n{\n")
56    end
57
58    -- if just a nil
59    if e.ret == "nil" then
60        output("return 0;\n")
61    -- if error should be raised
62    elseif string.find(e.ret,"error") then
63        if messageC_str then
64            output("snprintf(errorBuffer,"..exceptionMessageBufferSize..",\"Exception of type '"..e.name.."' was thrown by function '"..f.."'\\nMessage: %s\","..exceptionDefs[e.name].c_str..");\n")
65        else
66            output("snprintf(errorBuffer,"..exceptionMessageBufferSize..",\"Unknown exception thrown by function '"..f.."'\");\n")
67        end
68        output("errorDoIt = true;\n")
69    -- else go through the returns
70    else
71        -- buffer for message
72        if string.find(e.ret,"message") and messageC_str and errBuf == false then
73            output("char errorBuffer["..exceptionMessageBufferSize.."];\n")
74        end
75        local numrets = 0
76        local retpat = "(%w+),?"
77        local i,j,retval = string.find(e.ret,retpat)
78        while i do
79            local code = ""
80
81            -- NIL
82            if retval == "nil" then
83                code = "tolua_pushnil(tolua_S);\n"
84
85            -- MESSAGE
86            elseif retval == "message" then
87                if messageC_str then
88                    code = "snprintf(errorBuffer,"..exceptionMessageBufferSize..",\"Exception of type '"..e.name.."' was thrown by function '"..f.."'\\nMessage: %s\","..exceptionDefs[e.name].c_str..");\ntolua_pushstring(tolua_S,errorBuffer);\n"
89                else
90                    code = "tolua_pushstring(tolua_S,\"Unknown exception thrown by function '"..f.."'\");\n"
91                end
92
93            -- TRUE
94            elseif retval == "true" then
95                code = "tolua_pushboolean(tolua_S, 1);\n"
96
97            -- FALSE
98            elseif retval == "false" then
99                code = "tolua_pushboolean(tolua_S, 0);\n"
100            end
101
102            -- print code for this return value
103            if code ~= "" then
104                output(code)
105                numrets = numrets + 1
106            end
107
108            -- next return value
109            i,j,retval = string.find(e.ret,retpat,j+1)
110        end
111        output("return ",numrets,";\n")
112    end
113
114    -- print catch footer
115    output("}\n")
116end
117
118function outputExceptionCatchBlocks(func,throws,err)
119    for i=1,table.getn(throws) do
120        outputExceptionError(func, throws[i], err)
121    end
122
123    -- if an error should be raised, we do it here
124    if err then
125        output("if (errorDoIt) {\n")
126        output("luaL_error(tolua_S,errorBuffer);\n")
127        output("}\n")
128    end
129end
130
131
132-- Function class
133-- Represents a function or a class method.
134-- The following fields are stored:
135--  mod  = type modifiers
136--  type = type
137--  ptr  = "*" or "&", if representing a pointer or a reference
138--  name = name
139--  lname = lua name
140--  args  = list of argument declarations
141--  const = if it is a method receiving a const "this".
142classFunction = {
143    mod = '',
144    type = '',
145    ptr = '',
146    name = '',
147    args = {n=0},
148    const = '',
149}
150classFunction.__index = classFunction
151setmetatable(classFunction,classFeature)
152
153-- declare tags
154function classFunction:decltype ()
155    self.type = typevar(self.type)
156    if strfind(self.mod,'const') then
157        self.type = 'const '..self.type
158        self.mod = gsub(self.mod,'const','')
159    end
160    local i=1
161    while self.args[i] do
162        self.args[i]:decltype()
163        i = i+1
164    end
165end
166
167
168-- Write binding function
169-- Outputs C/C++ binding function.
170function classFunction:supcode (local_constructor)
171
172    local overload = strsub(self.cname,-2,-1) - 1  -- indicate overloaded func
173    local nret = 0      -- number of returned values
174    local class = self:inclass()
175    local _,_,static = strfind(self.mod,'^%s*(static)')
176    if class then
177
178        if self.name == 'new' and self.parent.flags.pure_virtual then
179            -- no constructor for classes with pure virtual methods
180            return
181        end
182
183        if local_constructor then
184           output("/* method: new_local of class ",class," */")
185       else
186           output("/* method:",self.name," of class ",class," */")
187       end
188    else
189        output("/* function:",self.name," */")
190    end
191
192    if local_constructor then
193        output("#ifndef TOLUA_DISABLE_"..self.cname.."_local")
194        output("\nstatic int",self.cname.."_local","(lua_State* tolua_S)")
195    else
196        output("#ifndef TOLUA_DISABLE_"..self.cname)
197        output("\nstatic int",self.cname,"(lua_State* tolua_S)")
198    end
199    output("{")
200
201    -- check types
202    if overload < 0 then
203        output('#ifndef TOLUA_RELEASE\n')
204    end
205    output(' tolua_Error tolua_err;')
206    output(' if (\n')
207    -- check self
208    local narg
209    if class then narg=2 else narg=1 end
210    if class then
211        local func = get_is_function(self.parent.type)
212        local type = self.parent.type
213        if self.name=='new' or static~=nil then
214            func = 'tolua_isusertable'
215            type = self.parent.type
216        end
217        if self.const ~= '' then
218            type = "const "..type
219        end
220        output('     !'..func..'(tolua_S,1,"'..type..'",0,&tolua_err) ||\n')
221    end
222    -- check args
223    if self.args[1].type ~= 'void' then
224        local i=1
225        while self.args[i] do
226            local btype = isbasic(self.args[i].type)
227            if btype ~= 'value' and btype ~= 'state' then
228                output('     '..self.args[i]:outchecktype(narg)..' ||\n')
229            end
230            if btype ~= 'state' then
231                narg = narg+1
232            end
233            i = i+1
234        end
235    end
236    -- check end of list
237    output('     !tolua_isnoobj(tolua_S,'..narg..',&tolua_err)\n )')
238    output('  goto tolua_lerror;')
239
240    output(' else\n')
241    if overload < 0 then
242        output('#endif\n')
243    end
244    output(' {')
245
246    -- declare self, if the case
247    local narg
248    if class then narg=2 else narg=1 end
249    if class and self.name~='new' and static==nil then
250        output(' ',self.const,self.parent.type,'*','self = ')
251        output('(',self.const,self.parent.type,'*) ')
252        local to_func = get_to_function(self.parent.type)
253        output(to_func,'(tolua_S,1,0);')
254    elseif static then
255        _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)')
256    end
257    -- declare parameters
258    if self.args[1].type ~= 'void' then
259        local i=1
260        while self.args[i] do
261            self.args[i]:declare(narg)
262            if isbasic(self.args[i].type) ~= "state" then
263                narg = narg+1
264            end
265            i = i+1
266        end
267    end
268
269    -- check self
270    if class and self.name~='new' and static==nil then
271        output('#ifndef TOLUA_RELEASE\n')
272        output('  if (!self) tolua_error(tolua_S,"'..output_error_hook("invalid \'self\' in function \'%s\'", self.name)..'", NULL);');
273        output('#endif\n')
274    end
275
276    -- get array element values
277    if class then narg=2 else narg=1 end
278    if self.args[1].type ~= 'void' then
279        local i=1
280        while self.args[i] do
281            self.args[i]:getarray(narg)
282            narg = narg+1
283            i = i+1
284        end
285    end
286
287    --------------------------------------------------
288    -- CEGUILua mod
289    -- init exception handling
290    local throws = false
291    do
292        local pattern = "tolua_throws|.*|"
293        local i,j = string.find(self.mod, pattern)
294        if i then
295            throws = {}
296            -- ensure table is empty.  Used to be: table.setn(throws,0)
297            for x in pairs(throws) do
298                throws[x] = nil
299            end
300            local excepts = string.sub(self.mod, i+12,j)
301            local epattern = "|.-|"
302            local i,j = string.find(excepts, epattern)
303            while i do
304                local e = string.sub(excepts,i+1,j-1)
305                local _,_,name,rest = string.find(e, "([%w:_]+),?(.*)")
306                table.insert(throws,{name=name, ret=rest})
307                i,j = string.find(excepts, epattern, j)
308            end
309            self.mod = string.gsub(self.mod, pattern, "")
310        end
311    end
312    local exRaiseError = false
313    --------------------------------------------------
314
315    pre_call_hook(self)
316
317    local out = string.find(self.mod, "tolua_outside")
318
319    ---------------
320    -- CEGUILua mod
321    -- remove "tolua_outside" from self.mod
322    if out then
323        self.mod = string.gsub(self.mod, "tolua_outside", "")
324    end
325
326    -- call function
327    if class and self.name=='delete' then
328        output('  Mtolua_delete(self);')
329    elseif class and self.name == 'operator&[]' then
330        if flags['1'] then -- for compatibility with tolua5 ?
331            output('  self->operator[](',self.args[1].name,'-1) = ',self.args[2].name,';')
332        else
333            output('  self->operator[](',self.args[1].name,') = ',self.args[2].name,';')
334        end
335    else
336    -- CEGUILua mod begin- throws
337    if throws then
338        for i=1,table.getn(throws) do
339            if string.find(throws[i].ret, "error") then
340                output("char errorBuffer["..exceptionMessageBufferSize.."];\n")
341                output("bool errorDoIt = false;\n")
342                exRaiseError = true
343                break
344            end
345        end
346        output("try\n")
347    end
348    -- CEGUILua mod end - throws
349    output('  {')
350    if self.type ~= '' and self.type ~= 'void' then
351        output('  ',self.mod,self.type,self.ptr,'tolua_ret = ')
352        output('(',self.mod,self.type,self.ptr,') ')
353    else
354        output('  ')
355    end
356    if class and self.name=='new' then
357        output('Mtolua_new((',self.type,')(')
358    elseif class and static then
359        if out then
360            output(self.name,'(')
361        else
362            output(class..'::'..self.name,'(')
363        end
364    elseif class then
365        if out then
366            output(self.name,'(')
367        else
368            if self.cast_operator then
369                --output('static_cast<',self.mod,self.type,self.ptr,' >(*self')
370                output('self->operator ',self.mod,self.type,'(')
371            else
372                output('self->'..self.name,'(')
373            end
374        end
375    else
376        output(self.name,'(')
377    end
378
379    if out and not static then
380        output('self')
381        if self.args[1] and self.args[1].name ~= '' then
382            output(',')
383        end
384    end
385    -- write parameters
386    local i=1
387    while self.args[i] do
388        self.args[i]:passpar()
389        i = i+1
390        if self.args[i] then
391            output(',')
392        end
393    end
394
395    if class and self.name == 'operator[]' and flags['1'] then
396        output('-1);')
397    else
398        if class and self.name=='new' then
399            output('));') -- close Mtolua_new(
400        else
401            output(');')
402        end
403    end
404
405    -- return values
406    if self.type ~= '' and self.type ~= 'void' then
407        nret = nret + 1
408        local t,ct = isbasic(self.type)
409        if t and self.name ~= "new" then
410            if self.cast_operator and _basic_raw_push[t] then
411                output('   ',_basic_raw_push[t],'(tolua_S,(',ct,')tolua_ret);')
412            else
413                output('   tolua_push'..t..'(tolua_S,(',ct,')tolua_ret);')
414            end
415            else
416                t = self.type
417                new_t = string.gsub(t, "const%s+", "")
418                local owned = false
419                if string.find(self.mod, "tolua_owned") then
420                    owned = true
421                end
422                local push_func = get_push_function(t)
423                if self.ptr == '' then
424                    output('   {')
425                    output('#ifdef __cplusplus\n')
426                    output('    void* tolua_obj = Mtolua_new((',new_t,')(tolua_ret));')
427                    output('    ',push_func,'(tolua_S,tolua_obj,"',t,'");')
428                    output('    tolua_register_gc(tolua_S,lua_gettop(tolua_S));')
429                    output('#else\n')
430                    output('    void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(',t,'));')
431                    output('    ',push_func,'(tolua_S,tolua_obj,"',t,'");')
432                    output('    tolua_register_gc(tolua_S,lua_gettop(tolua_S));')
433                    output('#endif\n')
434                    output('   }')
435                elseif self.ptr == '&' then
436                    output('   ',push_func,'(tolua_S,(void*)&tolua_ret,"',t,'");')
437                else
438                    output('   ',push_func,'(tolua_S,(void*)tolua_ret,"',t,'");')
439                    if owned or local_constructor then
440                        output('    tolua_register_gc(tolua_S,lua_gettop(tolua_S));')
441                    end
442                end
443            end
444        end
445        local i=1
446        while self.args[i] do
447            nret = nret + self.args[i]:retvalue()
448            i = i+1
449        end
450        output('  }')
451
452        ------------------------------------------
453        -- CEGUILua mod
454        -- finish exception handling
455        -- catch
456        if throws then
457            outputExceptionCatchBlocks(self.name, throws, exRaiseError)
458        end
459        ------------------------------------------
460
461        -- set array element values
462        if class then narg=2 else narg=1 end
463        if self.args[1].type ~= 'void' then
464            local i=1
465            while self.args[i] do
466                self.args[i]:setarray(narg)
467                narg = narg+1
468                i = i+1
469            end
470        end
471
472        -- free dynamically allocated array
473        if self.args[1].type ~= 'void' then
474            local i=1
475            while self.args[i] do
476                self.args[i]:freearray()
477                i = i+1
478            end
479        end
480    end
481
482    post_call_hook(self)
483
484    output(' }')
485    output(' return '..nret..';')
486
487    -- call overloaded function or generate error
488    if overload < 0 then
489
490        output('#ifndef TOLUA_RELEASE\n')
491        output('tolua_lerror:\n')
492        output(' tolua_error(tolua_S,"'..output_error_hook("#ferror in function \'%s\'.", self.lname)..'",&tolua_err);')
493        output(' return 0;')
494        output('#endif\n')
495    else
496        local _local = ""
497        if local_constructor then
498            _local = "_local"
499        end
500        output('tolua_lerror:\n')
501        output(' return '..strsub(self.cname,1,-3)..format("%02d",overload).._local..'(tolua_S);')
502    end
503    output('}')
504    output('#endif //#ifndef TOLUA_DISABLE\n')
505    output('\n')
506
507    -- recursive call to write local constructor
508    if class and self.name=='new' and not local_constructor then
509
510        self:supcode(1)
511    end
512
513end
514
515
516-- register function
517function classFunction:register (pre)
518
519    if not self:check_public_access() then
520        return
521    end
522
523    if self.name == 'new' and self.parent.flags.pure_virtual then
524        -- no constructor for classes with pure virtual methods
525        return
526    end
527
528    output(pre..'tolua_function(tolua_S,"'..self.lname..'",'..self.cname..');')
529    if self.name == 'new' then
530        output(pre..'tolua_function(tolua_S,"new_local",'..self.cname..'_local);')
531        output(pre..'tolua_function(tolua_S,".call",'..self.cname..'_local);')
532        --output(' tolua_set_call_event(tolua_S,'..self.cname..'_local, "'..self.parent.type..'");')
533    end
534end
535
536-- Print method
537function classFunction:print (ident,close)
538    print(ident.."Function{")
539    print(ident.." mod  = '"..self.mod.."',")
540    print(ident.." type = '"..self.type.."',")
541    print(ident.." ptr  = '"..self.ptr.."',")
542    print(ident.." name = '"..self.name.."',")
543    print(ident.." lname = '"..self.lname.."',")
544    print(ident.." const = '"..self.const.."',")
545    print(ident.." cname = '"..self.cname.."',")
546    print(ident.." lname = '"..self.lname.."',")
547    print(ident.." args = {")
548    local i=1
549    while self.args[i] do
550        self.args[i]:print(ident.."  ",",")
551        i = i+1
552    end
553    print(ident.." }")
554    print(ident.."}"..close)
555end
556
557-- check if it returns an object by value
558function classFunction:requirecollection (t)
559    local r = false
560    if self.type ~= '' and not isbasic(self.type) and self.ptr=='' then
561        local type = gsub(self.type,"%s*const%s+","")
562        t[type] = "tolua_collect_" .. clean_template(type)
563        r = true
564    end
565    local i=1
566    while self.args[i] do
567        r = self.args[i]:requirecollection(t) or r
568        i = i+1
569    end
570    return r
571end
572
573-- determine lua function name overload
574function classFunction:overload ()
575    return self.parent:overload(self.lname)
576end
577
578
579function param_object(par) -- returns true if the parameter has an object as its default value
580
581    if not string.find(par, '=') then return false end -- it has no default value
582
583    local _,_,def = string.find(par, "=(.*)$")
584
585    if string.find(par, "|") then -- a list of flags
586
587        return true
588    end
589
590    if string.find(par, "%*") then -- it's a pointer with a default value
591
592        if string.find(par, '=%s*new') or string.find(par, "%(") then -- it's a pointer with an instance as default parameter.. is that valid?
593            return true
594        end
595        return false -- default value is 'NULL' or something
596    end
597
598
599    if string.find(par, "[%(&]") then
600        return true
601    end -- default value is a constructor call (most likely for a const reference)
602
603    --if string.find(par, "&") then
604
605    --    if string.find(def, ":") or string.find(def, "^%s*new%s+") then
606
607    --        -- it's a reference with default to something like Class::member, or 'new Class'
608    --        return true
609    --    end
610    --end
611
612    return false -- ?
613end
614
615function strip_last_arg(all_args, last_arg) -- strips the default value from the last argument
616
617    local _,_,s_arg = string.find(last_arg, "^([^=]+)")
618    last_arg = string.gsub(last_arg, "([%%%(%)])", "%%%1");
619    all_args = string.gsub(all_args, "%s*,%s*"..last_arg.."%s*%)%s*$", ")")
620    return all_args, s_arg
621end
622
623
624
625-- Internal constructor
626function _Function (t)
627    setmetatable(t,classFunction)
628
629    if t.const ~= 'const' and t.const ~= '' then
630        error("#invalid 'const' specification")
631    end
632
633    append(t)
634    if t:inclass() then
635        --print ('t.name is '..t.name..', parent.name is '..t.parent.name)
636        if string.gsub(t.name, "%b<>", "") == string.gsub(t.parent.original_name or t.parent.name, "%b<>", "") then
637            t.name = 'new'
638            t.lname = 'new'
639            t.parent._new = true
640            t.type = t.parent.name
641            t.ptr = '*'
642        elseif string.gsub(t.name, "%b<>", "") == '~'..string.gsub(t.parent.original_name or t.parent.name, "%b<>", "") then
643            t.name = 'delete'
644            t.lname = 'delete'
645            t.parent._delete = true
646        end
647    end
648    t.cname = t:cfuncname("tolua")..t:overload(t)
649    return t
650end
651
652-- Constructor
653-- Expects three strings: one representing the function declaration,
654-- another representing the argument list, and the third representing
655-- the "const" or empty string.
656function Function (d,a,c)
657    --local t = split(strsub(a,2,-2),',') -- eliminate braces
658    --local t = split_params(strsub(a,2,-2))
659
660    if not flags['W'] and string.find(a, "%.%.%.%s*%)") then
661
662        warning("Functions with variable arguments (`...') are not supported. Ignoring "..d..a..c)
663        return nil
664    end
665
666
667    local i=1
668    local l = {n=0}
669
670    a = string.gsub(a, "%s*([%(%)])%s*", "%1")
671    local t,strip,last = strip_pars(strsub(a,2,-2));
672    if strip then
673        --local ns = string.sub(strsub(a,1,-2), 1, -(string.len(last)+1))
674        local ns = join(t, ",", 1, last-1)
675
676        ns = "("..string.gsub(ns, "%s*,%s*$", "")..')'
677        --ns = strip_defaults(ns)
678
679        local f = Function(d, ns, c)
680        for i=1,last do
681            t[i] = string.gsub(t[i], "=.*$", "")
682        end
683    end
684
685    while t[i] do
686        l.n = l.n+1
687        l[l.n] = Declaration(t[i],'var',true)
688        i = i+1
689    end
690    local f = Declaration(d,'func')
691    f.args = l
692    f.const = c
693    return _Function(f)
694end
695
696function join(t, sep, first, last)
697
698    first = first or 1
699    last = last or table.getn(t)
700    local lsep = ""
701    local ret = ""
702    local loop = false
703    for i = first,last do
704
705        ret = ret..lsep..t[i]
706        lsep = sep
707        loop = true
708    end
709    if not loop then
710        return ""
711    end
712
713    return ret
714end
715
716function strip_pars(s)
717
718    local t = split_c_tokens(s, ',')
719    local strip = false
720    local last
721
722    for i=t.n,1,-1 do
723
724        if not strip and param_object(t[i]) then
725            last = i
726            strip = true
727        end
728        --if strip then
729        --    t[i] = string.gsub(t[i], "=.*$", "")
730        --end
731    end
732
733    return t,strip,last
734
735end
736
737function strip_defaults(s)
738
739    s = string.gsub(s, "^%(", "")
740    s = string.gsub(s, "%)$", "")
741
742    local t = split_c_tokens(s, ",")
743    local sep, ret = "",""
744    for i=1,t.n do
745        t[i] = string.gsub(t[i], "=.*$", "")
746        ret = ret..sep..t[i]
747        sep = ","
748    end
749
750    return "("..ret..")"
751end
752
753
Note: See TracBrowser for help on using the repository browser.