Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/tolua/lua/function.lua @ 2988

Last change on this file since 2988 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: 21.4 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 = 'tolua_isusertype'
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        output('tolua_tousertype(tolua_S,1,0);')
253    elseif static then
254        _,_,self.mod = strfind(self.mod,'^%s*static%s%s*(.*)')
255    end
256    -- declare parameters
257    if self.args[1].type ~= 'void' then
258        local i=1
259        while self.args[i] do
260            self.args[i]:declare(narg)
261            if isbasic(self.args[i].type) ~= "state" then
262                narg = narg+1
263            end
264            i = i+1
265        end
266    end
267
268    -- check self
269    if class and self.name~='new' and static==nil then
270        output('#ifndef TOLUA_RELEASE\n')
271        output('  if (!self) tolua_error(tolua_S,"invalid \'self\' in function \''..self.name..'\'",NULL);');
272        output('#endif\n')
273    end
274
275    -- get array element values
276    if class then narg=2 else narg=1 end
277    if self.args[1].type ~= 'void' then
278        local i=1
279        while self.args[i] do
280            self.args[i]:getarray(narg)
281            narg = narg+1
282            i = i+1
283        end
284    end
285
286    --------------------------------------------------
287    -- CEGUILua mod
288    -- init exception handling
289    local throws = false
290    do
291        local pattern = "tolua_throws|.*|"
292        local i,j = string.find(self.mod, pattern)
293        if i then
294            throws = {}
295            -- ensure table is empty.  Used to be: table.setn(throws,0)
296            for x in pairs(throws) do
297                throws[x] = nil
298            end
299            local excepts = string.sub(self.mod, i+12,j)
300            local epattern = "|.-|"
301            local i,j = string.find(excepts, epattern)
302            while i do
303                local e = string.sub(excepts,i+1,j-1)
304                local _,_,name,rest = string.find(e, "([%w:_]+),?(.*)")
305                table.insert(throws,{name=name, ret=rest})
306                i,j = string.find(excepts, epattern, j)
307            end
308            self.mod = string.gsub(self.mod, pattern, "")
309        end
310    end
311    local exRaiseError = false
312    --------------------------------------------------
313
314    local out = string.find(self.mod, "tolua_outside")
315
316    ---------------
317    -- CEGUILua mod
318    -- remove "tolua_outside" from self.mod
319    if out then
320        self.mod = string.gsub(self.mod, "tolua_outside", "")
321    end
322
323    -- call function
324    if class and self.name=='delete' then
325        output('  delete self;')
326    elseif class and self.name == 'operator&[]' then
327        if flags['1'] then -- for compatibility with tolua5 ?
328            output('  self->operator[](',self.args[1].name,'-1) = ',self.args[2].name,';')
329        else
330            output('  self->operator[](',self.args[1].name,') = ',self.args[2].name,';')
331        end
332    else
333    -- CEGUILua mod begin- throws
334    if throws then
335        for i=1,table.getn(throws) do
336            if string.find(throws[i].ret, "error") then
337                output("char errorBuffer["..exceptionMessageBufferSize.."];\n")
338                output("bool errorDoIt = false;\n")
339                exRaiseError = true
340                break
341            end
342        end
343        output("try\n")
344    end
345    -- CEGUILua mod end - throws
346    output('  {')
347    if self.type ~= '' and self.type ~= 'void' then
348        output('  ',self.mod,self.type,self.ptr,'tolua_ret = ')
349        output('(',self.mod,self.type,self.ptr,') ')
350    else
351        output('  ')
352    end
353    if class and self.name=='new' then
354        output('new',self.type,'(')
355    elseif class and static then
356        if out then
357            output(self.name,'(')
358        else
359            output(class..'::'..self.name,'(')
360        end
361    elseif class then
362        if out then
363            output(self.name,'(')
364        else
365            if self.cast_operator then
366                output('static_cast<',self.mod,self.type,self.ptr,'>(*self')
367            else
368                output('self->'..self.name,'(')
369            end
370        end
371    else
372        output(self.name,'(')
373    end
374
375    if out and not static then
376        output('self')
377        if self.args[1] and self.args[1].name ~= '' then
378            output(',')
379        end
380    end
381    -- write parameters
382    local i=1
383    while self.args[i] do
384        self.args[i]:passpar()
385        i = i+1
386        if self.args[i] then
387            output(',')
388        end
389    end
390
391    if class and self.name == 'operator[]' and flags['1'] then
392        output('-1);')
393    else
394        output(');')
395    end
396
397    -- return values
398    if self.type ~= '' and self.type ~= 'void' then
399        nret = nret + 1
400        local t,ct = isbasic(self.type)
401        if t then
402            if self.cast_operator and _basic_raw_push[t] then
403                output('   ',_basic_raw_push[t],'(tolua_S,(',ct,')tolua_ret);')
404            else
405                output('   tolua_push'..t..'(tolua_S,(',ct,')tolua_ret);')
406            end
407            else
408                t = self.type
409                new_t = string.gsub(t, "const%s+", "")
410                if self.ptr == '' then
411                    output('   {')
412                    output('#ifdef __cplusplus\n')
413                    output('    void* tolua_obj = new',new_t,'(tolua_ret);')
414                    output('    tolua_pushusertype_and_takeownership(tolua_S,tolua_obj,"',t,'");')
415                    output('#else\n')
416                    output('    void* tolua_obj = tolua_copy(tolua_S,(void*)&tolua_ret,sizeof(',t,'));')
417                    output('    tolua_pushusertype_and_takeownership(tolua_S,tolua_obj,"',t,'");')
418                    output('#endif\n')
419                    output('   }')
420                elseif self.ptr == '&' then
421                    output('   tolua_pushusertype(tolua_S,(void*)&tolua_ret,"',t,'");')
422                else
423                    if local_constructor then
424                        output('   tolua_pushusertype_and_takeownership(tolua_S,(void *)tolua_ret,"',t,'");')
425                    else
426                        output('   tolua_pushusertype(tolua_S,(void*)tolua_ret,"',t,'");')
427                    end
428                end
429            end
430        end
431        local i=1
432        while self.args[i] do
433            nret = nret + self.args[i]:retvalue()
434            i = i+1
435        end
436        output('  }')
437
438        ------------------------------------------
439        -- CEGUILua mod
440        -- finish exception handling
441        -- catch
442        if throws then
443            outputExceptionCatchBlocks(self.name, throws, exRaiseError)
444        end
445        ------------------------------------------
446
447        -- set array element values
448        if class then narg=2 else narg=1 end
449        if self.args[1].type ~= 'void' then
450            local i=1
451            while self.args[i] do
452                self.args[i]:setarray(narg)
453                narg = narg+1
454                i = i+1
455            end
456        end
457
458        -- free dynamically allocated array
459        if self.args[1].type ~= 'void' then
460            local i=1
461            while self.args[i] do
462                self.args[i]:freearray()
463                i = i+1
464            end
465        end
466    end
467
468    output(' }')
469    output(' return '..nret..';')
470
471    -- call overloaded function or generate error
472    if overload < 0 then
473
474        output('#ifndef TOLUA_RELEASE\n')
475        output('tolua_lerror:\n')
476        output(' tolua_error(tolua_S,"#ferror in function \''..self.lname..'\'.",&tolua_err);')
477        output(' return 0;')
478        output('#endif\n')
479    else
480        local _local = ""
481        if local_constructor then
482            _local = "_local"
483        end
484        output('tolua_lerror:\n')
485        output(' return '..strsub(self.cname,1,-3)..format("%02d",overload).._local..'(tolua_S);')
486    end
487    output('}')
488    output('#endif //#ifndef TOLUA_DISABLE\n')
489    output('\n')
490
491    -- recursive call to write local constructor
492    if class and self.name=='new' and not local_constructor then
493
494        self:supcode(1)
495    end
496
497end
498
499
500-- register function
501function classFunction:register (pre)
502
503    if not self:check_public_access() then
504        return
505    end
506
507    if self.name == 'new' and self.parent.flags.pure_virtual then
508        -- no constructor for classes with pure virtual methods
509        return
510    end
511
512    output(pre..'tolua_function(tolua_S,"'..self.lname..'",'..self.cname..');')
513    if self.name == 'new' then
514        output(pre..'tolua_function(tolua_S,"new_local",'..self.cname..'_local);')
515        output(pre..'tolua_function(tolua_S,".call",'..self.cname..'_local);')
516        --output(' tolua_set_call_event(tolua_S,'..self.cname..'_local, "'..self.parent.type..'");')
517    end
518end
519
520-- Print method
521function classFunction:print (ident,close)
522    print(ident.."Function{")
523    print(ident.." mod  = '"..self.mod.."',")
524    print(ident.." type = '"..self.type.."',")
525    print(ident.." ptr  = '"..self.ptr.."',")
526    print(ident.." name = '"..self.name.."',")
527    print(ident.." lname = '"..self.lname.."',")
528    print(ident.." const = '"..self.const.."',")
529    print(ident.." cname = '"..self.cname.."',")
530    print(ident.." lname = '"..self.lname.."',")
531    print(ident.." args = {")
532    local i=1
533    while self.args[i] do
534        self.args[i]:print(ident.."  ",",")
535        i = i+1
536    end
537    print(ident.." }")
538    print(ident.."}"..close)
539end
540
541-- check if it returns an object by value
542function classFunction:requirecollection (t)
543    local r = false
544    if self.type ~= '' and not isbasic(self.type) and self.ptr=='' then
545        local type = gsub(self.type,"%s*const%s+","")
546        t[type] = "tolua_collect_" .. clean_template(type)
547        r = true
548    end
549    local i=1
550    while self.args[i] do
551        r = self.args[i]:requirecollection(t) or r
552        i = i+1
553    end
554    return r
555end
556
557-- determine lua function name overload
558function classFunction:overload ()
559    return self.parent:overload(self.lname)
560end
561
562
563function param_object(par) -- returns true if the parameter has an object as its default value
564
565    if not string.find(par, '=') then return false end -- it has no default value
566
567    local _,_,def = string.find(par, "=(.*)$")
568
569    if string.find(par, "|") then -- a list of flags
570
571        return true
572    end
573
574    if string.find(par, "%*") then -- it's a pointer with a default value
575
576        if string.find(par, '=%s*new') then -- it's a pointer with an instance as default parameter.. is that valid?
577            return true
578        end
579        return false -- default value is 'NULL' or something
580    end
581
582
583    if string.find(par, "[%(&]") then
584        return true
585    end -- default value is a constructor call (most likely for a const reference)
586
587    --if string.find(par, "&") then
588
589    --    if string.find(def, ":") or string.find(def, "^%s*new%s+") then
590
591    --        -- it's a reference with default to something like Class::member, or 'new Class'
592    --        return true
593    --    end
594    --end
595
596    return false -- ?
597end
598
599function strip_last_arg(all_args, last_arg) -- strips the default value from the last argument
600
601    local _,_,s_arg = string.find(last_arg, "^([^=]+)")
602    last_arg = string.gsub(last_arg, "([%%%(%)])", "%%%1");
603    all_args = string.gsub(all_args, "%s*,%s*"..last_arg.."%s*%)%s*$", ")")
604    return all_args, s_arg
605end
606
607
608
609-- Internal constructor
610function _Function (t)
611    setmetatable(t,classFunction)
612
613    if t.const ~= 'const' and t.const ~= '' then
614        error("#invalid 'const' specification")
615    end
616
617    append(t)
618    if t:inclass() then
619        --print ('t.name is '..t.name..', parent.name is '..t.parent.name)
620        if string.gsub(t.name, "%b<>", "") == string.gsub(t.parent.original_name or t.parent.name, "%b<>", "") then
621            t.name = 'new'
622            t.lname = 'new'
623            t.parent._new = true
624            t.type = t.parent.name
625            t.ptr = '*'
626        elseif string.gsub(t.name, "%b<>", "") == '~'..string.gsub(t.parent.original_name or t.parent.name, "%b<>", "") then
627            t.name = 'delete'
628            t.lname = 'delete'
629            t.parent._delete = true
630        end
631    end
632    t.cname = t:cfuncname("tolua")..t:overload(t)
633    return t
634end
635
636-- Constructor
637-- Expects three strings: one representing the function declaration,
638-- another representing the argument list, and the third representing
639-- the "const" or empty string.
640function Function (d,a,c)
641    --local t = split(strsub(a,2,-2),',') -- eliminate braces
642    --local t = split_params(strsub(a,2,-2))
643
644    if not flags['W'] and string.find(a, "%.%.%.%s*%)") then
645
646        warning("Functions with variable arguments (`...') are not supported. Ignoring "..d..a..c)
647        return nil
648    end
649
650
651    local i=1
652    local l = {n=0}
653
654    a = string.gsub(a, "%s*([%(%)])%s*", "%1")
655    local t,strip,last = strip_pars(strsub(a,2,-2));
656    if strip then
657        --local ns = string.sub(strsub(a,1,-2), 1, -(string.len(last)+1))
658        local ns = join(t, ",", 1, last-1)
659
660        ns = "("..string.gsub(ns, "%s*,%s*$", "")..')'
661        --ns = strip_defaults(ns)
662
663        Function(d, ns, c)
664        for i=1,last do
665            t[i] = string.gsub(t[i], "=.*$", "")
666        end
667    end
668
669    while t[i] do
670        l.n = l.n+1
671        l[l.n] = Declaration(t[i],'var',true)
672        i = i+1
673    end
674    local f = Declaration(d,'func')
675    f.args = l
676    f.const = c
677    return _Function(f)
678end
679
680function join(t, sep, first, last)
681
682    first = first or 1
683    last = last or table.getn(t)
684    local lsep = ""
685    local ret = ""
686    local loop = false
687    for i = first,last do
688
689        ret = ret..lsep..t[i]
690        lsep = sep
691        loop = true
692    end
693    if not loop then
694        return ""
695    end
696
697    return ret
698end
699
700function strip_pars(s)
701
702    local t = split_c_tokens(s, ',')
703    local strip = false
704    local last
705
706    for i=t.n,1,-1 do
707
708        if not strip and param_object(t[i]) then
709            last = i
710            strip = true
711        end
712        --if strip then
713        --    t[i] = string.gsub(t[i], "=.*$", "")
714        --end
715    end
716
717    return t,strip,last
718
719end
720
721function strip_defaults(s)
722
723    s = string.gsub(s, "^%(", "")
724    s = string.gsub(s, "%)$", "")
725
726    local t = split_c_tokens(s, ",")
727    local sep, ret = "",""
728    for i=1,t.n do
729        t[i] = string.gsub(t[i], "=.*$", "")
730        ret = ret..sep..t[i]
731        sep = ","
732    end
733
734    return "("..ret..")"
735end
736
737
Note: See TracBrowser for help on using the repository browser.