Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 2110 was 2087, checked in by landauf, 16 years ago

merged objecthierarchy branch back to trunk

  • Property svn:eol-style set to native
File size: 18.8 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.