Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/gui/src/tolua/lua/function.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: 19.5 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.