discard identical version of the method if present
[lqt.git] / generator / generator.lua
blob9010e8d2d600d431dea869b5e9d9414654270210
1 #!/usr/bin/lua
3 --[[
5 Copyright (c) 2007-2009 Mauro Iazzi
6 Copyright (c) 2008 Peter Kümmel
8 Permission is hereby granted, free of charge, to any person
9 obtaining a copy of this software and associated documentation
10 files (the "Software"), to deal in the Software without
11 restriction, including without limitation the rights to use,
12 copy, modify, merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom the
14 Software is furnished to do so, subject to the following
15 conditions:
17 The above copyright notice and this permission notice shall be
18 included in all copies or substantial portions of the Software.
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 OTHER DEALINGS IN THE SOFTWARE.
29 --]]
31 local osseparator = package.config:sub(1,1)
33 local path = string.match(arg[0], '(.*'..osseparator..')[^%'..osseparator..']+') or ''
34 if path == "" then
35 --- remove script name
36 path = string.sub(arg[0], 1, #arg[0] - #'generator.lua')
37 end
39 local filename = nil
40 local dirname = nil
41 local module_name = nil
42 local typefiles = {}
43 local filterfiles = {}
44 local output_includes = {
45 'lqt_common.hpp',
49 local i = 1
50 while select(i, ...) do
51 local argi = select(i, ...)
52 if argi=='-n' then
53 i = i + 1
54 module_name = select(i, ...)
55 elseif argi=='-i' then
56 i = i + 1
57 table.insert(output_includes, (select(i, ...)))
58 elseif argi=='-t' then
59 i = i + 1
60 table.insert(typefiles, (select(i, ...)))
61 elseif argi=='-f' then
62 i = i + 1
63 table.insert(filterfiles, (select(i, ...)))
64 else
65 filename = filename and error'duplicate filename' or argi
66 end
67 i = i + 1
68 end
69 end
71 local my_includes = ''
72 for _, i in ipairs(output_includes) do
73 if string.match(i, '^<.+>$') then
74 my_includes = my_includes .. '#include '..i..'\n'
75 elseif string.match(i, '^".+"$') then
76 my_includes = my_includes .. '#include '..i..'\n'
77 else
78 my_includes = my_includes .. '#include "'..i..'"\n'
79 end
80 end
81 output_includes = my_includes .. '\n'
83 local readfile = function(fn)
84 local f = assert(io.open(fn))
85 local s = f:read'*a'
86 f:close()
87 return s
88 end
90 local fprint = function(f)
91 return function(...)
92 for i = 1, select('#',...) do
93 f:write((i==1) and '' or '\t', tostring(select(i,...)))
94 end
95 f:write'\n'
96 f:flush()
97 end
98 end
100 local _src = '_src'..osseparator
101 local debug = fprint(io.stderr)
102 local print_enum = fprint(assert(io.open(module_name.._src..module_name..'_enum.cpp', 'w')))
103 local print_slot_h = fprint(assert(io.open(module_name.._src..module_name..'_slot.hpp', 'w')))
104 local print_slot_c = fprint(assert(io.open(module_name.._src..module_name..'_slot.cpp', 'w')))
106 local xmlstream, idindex = dofile(path..'xml.lua')(readfile(filename))
108 dofile(path..'classes.lua') -- this should become a require at some point
110 ----------------------------------------------------------------------------------
112 local copy_functions = function(index)
113 local ret = {}
114 for e in pairs(index) do
115 if e.label:match'^Function' then
116 e.label = 'Function'
117 ret[e] = true
120 return ret
124 local fix_arguments = function(all)
125 local fullnames = {}
126 for e in pairs(all or {}) do
127 if e.xarg.fullname then fullnames[e.xarg.fullname] = true end
129 for a in pairs(all) do
130 if a.label=='Argument'
131 and a.xarg.default=='1'
132 and (not string.match(a.xarg.defaultvalue, '^[-+]?%d+%.?%d*[L]?$'))
133 and (not string.match(a.xarg.defaultvalue, '^".*"$'))
134 and a.xarg.defaultvalue~='true'
135 and a.xarg.defaultvalue~='false'
136 and (not string.match(a.xarg.defaultvalue, '^0[xX]%d+$')) then
137 local dv, call = string.match(a.xarg.defaultvalue, '(.-)(%(%))')
138 dv = dv or a.xarg.defaultvalue
139 call = call or ''
140 local context = a.xarg.context
141 while not fullnames[context..'::'..dv] and context~='' do
142 context = string.match(context, '^(.*)::') or ''
144 if fullnames[context..'::'..dv] then
145 a.xarg.defaultvalue = context..'::'..dv..call
146 elseif fullnames[dv] then
147 a.xarg.defaultvalue = dv..call
148 else
149 a.xarg.default = nil
150 a.xarg.defaultvalue = nil
154 return all
157 local fix_functions = function(index)
158 for f in pairs(index) do
159 local args = {}
160 for i, a in ipairs(f) do
161 -- avoid bogus 'void' arguments
162 if a.xarg.type_name=='void' and i==1 and f[2]==nil then break end
163 if a.label=='Argument' then
164 table.insert(args, a)
167 f.arguments = args
168 f.return_type = f.xarg.type_name
169 if f.xarg.type_name=='void' then
170 f.return_type = nil
173 return index
176 local copy_enums = function(index)
177 local ret = {}
178 for e in pairs(index) do
179 if e.label=='Enum'
180 and not string.match(e.xarg.fullname, '%b<>')
181 and e.xarg.access=='public' then
182 ret[e] = true
185 return ret
188 local fill_enums = function(index)
189 for e in pairs(index) do
190 local values = {}
191 for _, v in ipairs(e) do
192 if v.label=='Enumerator' then
193 table.insert(values, v)
196 e.values = values
198 return index
201 local class_is_public = function (fullnames, c)
202 repeat
203 if c.xarg.access~='public' then return false end
204 if c.xarg.member_of_class then
205 local p = fullnames[c.xarg.member_of_class]
206 assert(p, 'member_of_class should exist')
207 assert(p.label=='Class', 'member_of_class should be a class')
208 c = fullnames[c.xarg.member_of_class]
209 else
210 break
212 until true
213 return true
216 local copy_classes = function(index)
217 local ret = {}
218 local fullnames = {}
219 for k,v in pairs(index) do
220 if k.label=='Class' then fullnames[k.xarg.fullname] = k end
222 for e in pairs(index) do
223 if e.label=='Class'
224 and class_is_public(fullnames, e)
225 and not e.xarg.fullname:match'%b<>' then
226 ret[e] = true
227 elseif e.label=='Class'
228 and not e.xarg.fullname:match'%b<>' then
229 --print('class', e.xarg.fullname, 'rejected because not public')
232 return ret
235 local get_qobjects = function(index)
236 local classes = {}
237 for c in pairs(index) do
238 classes[c.xarg.fullname] = c
240 local is_qobject
241 is_qobject = function(c)
242 if c==nil then return false end
243 if c.qobject then return true end
244 if c.xarg.fullname=='QObject' then
245 c.qobject = true
246 return true
248 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
249 local base = classes[b]
250 if is_qobject(base) then
251 --debug(c.xarg.fullname, "is a QObject")
252 c.qobject = true
253 return true
256 return false
258 for c in pairs(index) do
259 local qobj = is_qobject(c)
261 return index
264 local fill_virtuals = function(index)
265 local classes = {}
266 for c in pairs(index) do
267 classes[c.xarg.fullname] = c
269 local get_virtuals
270 get_virtuals = function(c)
271 local ret = {}
272 for _, f in ipairs(c) do
273 if f.label=='Function' and f.xarg.virtual=='1' then
274 local n = string.match(f.xarg.name, '~') or f.xarg.name
275 if n~='~' and n~='metaObject' then ret[n] = f end
278 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
279 local base = classes[b]
280 if type(base)=='table' then
281 local bv = get_virtuals(base)
282 for n, f in pairs(bv) do
283 if not ret[n] then ret[n] = f end
287 for _, f in ipairs(c) do
288 if f.label=='Function'
289 and f.xarg.access~='private'
290 and (ret[string.match(f.xarg.name, '~') or f.xarg.name]) then
291 f.xarg.virtual = '1'
292 local n = string.match(f.xarg.name, '~')or f.xarg.name
293 ret[n] = f
296 return ret
298 for c in pairs(index) do
299 c.virtuals = get_virtuals(c)
300 for _, f in pairs(c.virtuals) do
301 if f.xarg.abstract=='1' then c.abstract=true break end
304 return index
307 local distinguish_methods = function(index)
308 for c in pairs(index) do
309 local construct, destruct, normal = {}, nil, {}
310 local n = c.xarg.name
311 local copy = nil
312 for _, f in ipairs(c) do
313 if n==f.xarg.name then
314 table.insert(construct, f)
315 elseif f.xarg.name:match'~' then
316 destruct = f
317 else
318 if (not string.match(f.xarg.name, '^operator%W'))
319 and (not f.xarg.member_template_parameters) then
320 table.insert(normal, f)
324 c.constructors = construct
325 c.destructor = destruct
326 c.methods = normal
328 return index
331 local fill_public_destr = function(index)
332 local classes = {}
333 for c in pairs(index) do
334 classes[c.xarg.fullname] = c
336 local destr_is_public
337 destr_is_public = function(c)
338 if c.destructor then
339 return c.destructor.xarg.access=='public'
340 else
341 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
342 local base = classes[b]
343 if base and not destr_is_public(base) then
344 return false
347 return true
350 for c in pairs(index) do
351 c.public_destr = destr_is_public(c)
353 return index
356 local fill_copy_constructor = function(index)
357 local classes = {}
358 for c in pairs(index) do
359 classes[c.xarg.name] = c
361 for c in pairs(index) do
362 local copy = nil
363 for _, f in ipairs(c.constructors) do
364 if #(f.arguments)==1
365 and f.arguments[1].xarg.type_name==c.xarg.fullname..' const&' then
366 copy = f
367 break
370 c.copy_constructor = copy
372 local copy_constr_is_public
373 copy_constr_is_public = function(c)
374 if c.copy_constructor then
375 return (c.copy_constructor.xarg.access=='public')
376 or (c.copy_constructor.xarg.access=='protected')
377 else
378 local ret = nil
379 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
380 local base = classes[b]
381 if base and not copy_constr_is_public(base) then
382 return false
385 return true
388 for c in pairs(index) do
389 c.public_constr = copy_constr_is_public(c)
391 return index
394 local fill_typesystem_with_enums = function(enums, types)
395 local etype = function(en)
396 return {
397 push = function(n)
398 return 'lqtL_pushenum(L, '..n..', "'..string.gsub(en, '::', '.')..'")', 1
399 end,
400 get = function(n)
401 return 'static_cast<'..en..'>'
402 ..'(lqtL_toenum(L, '..n..', "'..string.gsub(en, '::', '.')..'"))', 1
403 end,
404 test = function(n)
405 return 'lqtL_isenum(L, '..n..', "'..string.gsub(en, '::', '.')..'")', 1
406 end,
407 onstack = string.gsub(en, '::', '.')..',',
410 local ret = {}
411 for e in pairs(enums) do
412 if types[e.xarg.fullname]==nil then
413 ret[e] = true
414 types[e.xarg.fullname] = etype(e.xarg.fullname)
415 else
416 --io.stderr:write(e.xarg.fullname, ': already present\n')
419 return ret
422 local put_class_in_filesystem = lqt.classes.insert
423 local fill_typesystem_with_classes = function(classes, types)
424 local ret = {}
425 for c in pairs(classes) do
426 ret[c] = put_class_in_filesystem(c.xarg.fullname, types) --, true)
428 return ret
431 local argument_name = function(tn, an)
432 local ret
433 if string.match(tn, '%(%*%)') then
434 ret = string.gsub(tn, '%(%*%)', '(*'..an..')', 1)
435 elseif string.match(tn, '%[.*%]') then
436 ret = string.gsub(tn, '%s*(%[.*%])', ' '..an..'%1')
437 else
438 ret = tn .. ' ' .. an
440 return ret
443 local fill_wrapper_code = function(f, types)
444 if f.wrapper_code then return f end
445 local stackn, argn = 1, 1
446 local stack_args, defects = '', 0
447 local wrap, line = ' int oldtop = lua_gettop(L);\n', ''
448 if f.xarg.abstract then return nil end
449 if f.xarg.member_of_class and f.xarg.static~='1' then
450 if not types[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
451 stack_args = stack_args .. types[f.xarg.member_of_class..'*'].onstack
452 defects = defects + 7 -- FIXME: arbitrary
453 if f.xarg.constant=='1' then
454 defects = defects + 8 -- FIXME: arbitrary
456 local sget, sn = types[f.xarg.member_of_class..'*'].get(stackn)
457 wrap = wrap .. ' ' .. f.xarg.member_of_class .. '* self = ' .. sget .. ';\n'
458 stackn = stackn + sn
459 wrap = wrap .. [[
460 if (NULL==self) {
461 lua_pushstring(L, "this pointer is NULL");
462 lua_error(L);
465 --print(sget, sn)
466 line = 'self->'..f.xarg.fullname..'('
467 else
468 line = f.xarg.fullname..'('
470 for i, a in ipairs(f.arguments) do
471 if not types[a.xarg.type_name] then return nil end
472 local aget, an, arg_as = types[a.xarg.type_name].get(stackn)
473 stack_args = stack_args .. types[a.xarg.type_name].onstack
474 if types[a.xarg.type_name].defect then defects = defects + types[a.xarg.type_name].defect end
475 wrap = wrap .. ' ' .. argument_name(arg_as or a.xarg.type_name, 'arg'..argn) .. ' = '
476 if a.xarg.default=='1' and an>0 then
477 wrap = wrap .. 'lua_isnoneornil(L, '..stackn..')'
478 for j = stackn+1,stackn+an-1 do
479 wrap = wrap .. ' && lua_isnoneornil(L, '..j..')'
481 local dv = a.xarg.defaultvalue
482 wrap = wrap .. ' ? static_cast< ' .. a.xarg.type_name .. ' >(' .. dv .. ') : '
484 wrap = wrap .. aget .. ';\n'
485 line = line .. (argn==1 and 'arg' or ', arg') .. argn
486 stackn = stackn + an
487 argn = argn + 1
489 line = line .. ')'
490 -- FIXME: hack follows for constructors
491 if f.calling_line then line = f.calling_line end
492 if f.return_type then line = f.return_type .. ' ret = ' .. line end
493 wrap = wrap .. ' ' .. line .. ';\n lua_settop(L, oldtop);\n' -- lua_pop(L, '..stackn..');\n'
494 if f.return_type then
495 if not types[f.return_type] then return nil end
496 local rput, rn = types[f.return_type].push'ret'
497 wrap = wrap .. ' luaL_checkstack(L, '..rn..', "cannot grow stack for return value");\n'
498 wrap = wrap .. ' '..rput..';\n return '..rn..';\n'
499 else
500 wrap = wrap .. ' return 0;\n'
502 f.wrapper_code = wrap
503 f.stack_arguments = stack_args
504 f.defects = defects
505 return f
508 local fill_test_code = function(f, types)
509 local stackn = 1
510 local test = ''
511 if f.xarg.member_of_class and f.xarg.static~='1' then
512 if not types[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
513 local stest, sn = types[f.xarg.member_of_class..'*'].test(stackn)
514 test = test .. ' && ' .. stest
515 stackn = stackn + sn
517 for i, a in ipairs(f.arguments) do
518 if not types[a.xarg.type_name] then return nil end -- print(a.xarg.type_name) return nil end
519 local atest, an = types[a.xarg.type_name].test(stackn)
520 if a.xarg.default=='1' and an>0 then
521 test = test .. ' && (lqtL_missarg(L, ' .. stackn .. ', ' .. an .. ') || '
522 test = test .. atest .. ')'
523 else
524 test = test .. ' && ' .. atest
526 stackn = stackn + an
528 -- can't make use of default values if I fix number of args
529 test = '(lua_gettop(L)<' .. stackn .. ')' .. test
530 f.test_code = test
531 return f
534 local fill_wrappers = function(functions, types)
535 local ret = {}
536 for f in pairs(functions) do
537 f = fill_wrapper_code(f, types)
538 if f then
539 f = assert(fill_test_code(f, types), f.xarg.fullname) -- MUST pass
540 ret[f] = true
541 --local out = 'extern "C" LQT_EXPORT int lqt_bind'..f.xarg.id..' (lua_State *L) {\n'
542 --.. f.wrapper_code .. '}\n'
543 --print(out)
546 return ret
549 local make_pushlines = function(args, types)
550 local pushlines, stack = '', 0
551 for i, a in ipairs(args) do
552 if not types[a.xarg.type_name] then return nil end
553 local apush, an = types[a.xarg.type_name].push('arg'..i)
554 pushlines = pushlines .. ' ' .. apush .. ';\n'
555 stack = stack + an
557 return pushlines, stack
560 local virtual_overload = function(v, types)
561 local ret = ''
562 if v.virtual_overload then return v end
563 -- make return type
564 if v.return_type and not types[v.return_type] then return nil end
565 local rget, rn = '', 0
566 if v.return_type then rget, rn, ret_as = types[v.return_type].get'oldtop+1' end
567 local retget = (v.return_type and argument_name(ret_as or v.return_type, 'ret')
568 .. ' = ' .. rget .. ';' or '') .. 'lua_settop(L, oldtop);return'
569 .. (v.return_type and ' ret' or '')
570 -- make argument push
571 local pushlines, stack = make_pushlines(v.arguments, types)
572 if not pushlines then return nil end
573 -- make lua call
574 local luacall = 'lqtL_pcall(L, '..(stack+1)..', '..rn..', 0)'
575 -- make prototype and fallback
576 local proto = (v.return_type or 'void')..' ;;'..v.xarg.name..' ('
577 local fallback = ''
578 for i, a in ipairs(v.arguments) do
579 proto = proto .. (i>1 and ', ' or '')
580 .. argument_name(a.xarg.type_name, 'arg'..i)
581 fallback = fallback .. (i>1 and ', arg' or 'arg') .. i
583 proto = proto .. ')' .. (v.xarg.constant=='1' and ' const' or '')
584 fallback = (v.return_type and 'return this->' or 'this->')
585 .. v.xarg.fullname .. '(' .. fallback .. ');\n}\n'
586 ret = proto .. [[ {
587 int oldtop = lua_gettop(L);
588 lqtL_pushudata(L, this, "]]..string.gsub(v.xarg.member_of_class, '::', '.')..[[*");
589 lqtL_getoverload(L, -1, "]]..v.xarg.name..[[");
590 if (lua_isfunction(L, -1)) {
591 lua_insert(L, -2);
592 ]] .. pushlines .. [[
593 if (!]]..luacall..[[) {
594 ]]..retget..[[;
597 lua_settop(L, oldtop);
598 ]] .. fallback
599 v.virtual_overload = ret
600 v.virtual_proto = string.gsub(proto, ';;', '', 1)
601 return v
604 local fill_shell_class = function(c, types)
605 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
606 local shell = 'class LQT_EXPORT ' .. shellname .. ' : public ' .. c.xarg.fullname .. ' {\npublic:\n'
607 shell = shell .. ' lua_State *L;\n'
608 for _, constr in ipairs(c.constructors) do
609 if constr.xarg.access~='private' then
610 local cline = ' '..shellname..' (lua_State *l'
611 local argline = ''
612 for i, a in ipairs(constr.arguments) do
613 cline = cline .. ', ' .. argument_name(a.xarg.type_name, 'arg'..i)
614 argline = argline .. (i>1 and ', arg' or 'arg') .. i
616 cline = cline .. ') : ' .. c.xarg.fullname
617 .. '(' .. argline .. '), L(l) '
618 .. '{ lqtL_register(L, this); }\n'
619 shell = shell .. cline
622 if c.copy_constructor==nil and c.public_constr then
623 local cline = ' '..shellname..' (lua_State *l, '..c.xarg.fullname..' const& arg1)'
624 cline = cline .. ' : ' .. c.xarg.fullname .. '(arg1), L(l) {}\n'
625 shell = shell .. cline
627 for i, v in pairs(c.virtuals) do
628 if v.xarg.access~='private' then
629 if v.virtual_proto then shell = shell .. ' virtual ' .. v.virtual_proto .. ';\n' end
632 shell = shell .. ' ~'..shellname..'() { lqtL_unregister(L, this); }\n'
633 if c.shell and c.qobject then
634 shell = shell .. ' static QMetaObject staticMetaObject;\n'
635 shell = shell .. ' virtual const QMetaObject *metaObject() const;\n'
636 shell = shell .. ' virtual int qt_metacall(QMetaObject::Call, int, void **);\n'
637 shell = shell .. 'private:\n'
638 shell = shell .. ' Q_DISABLE_COPY('..shellname..');\n'
640 shell = shell .. '};\n'
641 c.shell_class = shell
642 return c
645 local fill_virtual_overloads = function(classes, types)
646 for c in pairs(classes) do
647 for i, v in pairs(c.virtuals) do
648 if v.xarg.access~='private' then
649 local vret = virtual_overload(v, types)
653 return classes
656 local fill_shell_classes = function(classes, types)
657 local ret = {}
658 for c in pairs(classes) do
659 if c.shell then
660 c = fill_shell_class(c, types)
661 if c then ret[c] = true end
663 ret[c] = true
665 return ret
668 local print_shell_classes = function(classes)
669 local fhead = nil
670 for c in pairs(classes) do
671 if fhead then fhead:close() end
672 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
673 fhead = assert(io.open(module_name.._src..module_name..'_head_'..n..'.hpp', 'w'))
674 local print_head = function(...)
675 fhead:write(...)
676 fhead:write'\n'
678 print_head('#ifndef LQT_HEAD_'..n)
679 print_head('#define LQT_HEAD_'..n)
680 print_head(output_includes)
681 --print_head('#include <'..string.match(c.xarg.fullname, '^[^:]+')..'>')
682 print_head''
683 if c.shell then
684 if c then
685 print_head(c.shell_class)
686 else
687 --io.stderr:write(c.fullname, '\n')
690 print_head('#endif // LQT_HEAD_'..n)
692 if fhead then fhead:close() end
693 return classes
696 local print_virtual_overloads = function(classes)
697 for c in pairs(classes) do
698 if c.shell then
699 local vo = ''
700 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
701 for _,v in pairs(c.virtuals) do
702 if v.virtual_overload then
703 vo = vo .. string.gsub(v.virtual_overload, ';;', shellname..'::', 1)
706 c.virtual_overloads = vo
709 return classes
712 local print_wrappers = function(index)
713 for c in pairs(index) do
714 local meta = {}
715 local wrappers = ''
716 for _, f in ipairs(c.methods) do
717 -- FIXME: should we really discard virtual functions?
718 -- if the virtual overload in the shell uses rawget
719 -- on the environment then we can leave these in the
720 -- metatable
721 if f.wrapper_code and f.xarg.virtual~='1' then
722 local out = 'static int lqt_bind'..f.xarg.id
723 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
724 if f.xarg.access=='public' then
725 --print_meta(out)
726 wrappers = wrappers .. out .. '\n'
727 meta[f] = f.xarg.name
731 if not c.abstract then
732 for _, f in ipairs(c.constructors) do
733 if f.wrapper_code then
734 local out = 'static int lqt_bind'..f.xarg.id
735 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
736 if f.xarg.access=='public' then
737 --print_meta(out)
738 wrappers = wrappers .. out .. '\n'
739 meta[f] = 'new'
744 --local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
745 local lua_name = string.gsub(c.xarg.fullname, '::', '.')
746 local out = 'static int lqt_delete'..c.xarg.id..' (lua_State *L) {\n'
747 out = out ..' '..c.xarg.fullname..' *p = static_cast<'
748 ..c.xarg.fullname..'*>(lqtL_toudata(L, 1, "'..lua_name..'*"));\n'
749 if c.public_destr then
750 out = out .. ' if (p) delete p;\n'
752 out = out .. ' lqtL_eraseudata(L, 1, "'..lua_name..'*");\n return 0;\n}\n'
753 --print_meta(out)
754 wrappers = wrappers .. out .. '\n'
755 c.meta = meta
756 c.wrappers = wrappers
758 return index
761 local print_metatable = function(c)
762 local methods = {}
763 local wrappers = c.wrappers
764 for m, n in pairs(c.meta) do
765 methods[n] = methods[n] or {}
766 table.insert(methods[n], m)
768 for n, l in pairs(methods) do
769 local duplicates = {}
770 for _, f in ipairs(l) do
771 local itisnew = true
772 for sa, g in pairs(duplicates) do
773 if sa==f.stack_arguments then
774 --debug("function equal: ", f.xarg.fullname, f.stack_arguments, sa, f.defects, g.defects)
775 if f.defects<g.defects then
776 else
777 itisnew = false
779 elseif string.match(sa, "^"..f.stack_arguments) then -- there is already a version with more arguments
780 --debug("function superseded: ", f.xarg.fullname, f.stack_arguments, sa, f.defects, g.defects)
781 elseif string.match(f.stack_arguments, '^'..sa) then -- there is already a version with less arguments
782 --debug("function superseding: ", f.xarg.fullname, f.stack_arguments, sa, f.defects, g.defects)
785 if itisnew then
786 duplicates[f.stack_arguments] = f
789 --[[
790 local numinitial = 0
791 local numfinal = 0
792 for sa, f in pairs(l) do
793 numinitial = numinitial + 1
795 for sa, f in pairs(duplicates) do
796 numfinal = numfinal + 1
798 if numinitial-numfinal>0 then debug(c.xarg.fullname, "suppressed:", numinitial-numfinal) end
799 --]]
800 methods[n] = duplicates
802 for n, l in pairs(methods) do
803 local disp = 'static int lqt_dispatcher_'..n..c.xarg.id..' (lua_State *L) {\n'
804 for _, f in pairs(l) do
805 disp = disp..' if ('..f.test_code..') return lqt_bind'..f.xarg.id..'(L);\n'
807 disp = disp .. ' lua_settop(L, 0);\n'
808 disp = disp .. ' lua_pushstring(L, "'..c.xarg.fullname..'::'..n..': incorrect or extra arguments");\n'
809 disp = disp .. ' return lua_error(L);\n}\n'
810 --print_meta(disp)
811 wrappers = wrappers .. disp .. '\n'
813 local metatable = 'static luaL_Reg lqt_metatable'..c.xarg.id..'[] = {\n'
814 for n, l in pairs(methods) do
815 metatable = metatable .. ' { "'..n..'", lqt_dispatcher_'..n..c.xarg.id..' },\n'
817 metatable = metatable .. ' { "delete", lqt_delete'..c.xarg.id..' },\n'
818 metatable = metatable .. ' { 0, 0 },\n};\n'
819 --print_meta(metatable)
820 wrappers = wrappers .. metatable .. '\n'
821 local bases = ''
822 for b in string.gmatch(c.xarg.bases_with_attributes or '', '([^;]*);') do
823 if not string.match(b, '^virtual') then
824 b = string.gsub(b, '^[^%s]* ', '')
825 bases = bases .. ' {"'..string.gsub(b,'::','.')..'*", (char*)(void*)static_cast<'..b..'*>(('..c.xarg.fullname..'*)1)-(char*)1},\n'
828 bases = 'static lqt_Base lqt_base'..c.xarg.id..'[] = {\n'..bases..' {NULL, 0}\n};\n'
829 --print_meta(bases)
830 wrappers = wrappers .. bases .. '\n'
831 c.wrappers = wrappers
832 return c
835 local print_metatables = function(classes)
836 for c in pairs(classes) do
837 print_metatable(c)
839 return classes
842 local cpp_files = {}
844 local print_single_class = function(c)
845 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
846 local lua_name = string.gsub(c.xarg.fullname, '::', '.')
847 local cppname = module_name..'_meta_'..n..'.cpp'
848 table.insert(cpp_files, cppname);
849 local fmeta = assert(io.open(module_name.._src..cppname, 'w'))
850 local print_meta = function(...)
851 fmeta:write(...)
852 fmeta:write'\n'
854 print_meta('#include "'..module_name..'_head_'..n..'.hpp'..'"\n\n')
855 print_meta(c.wrappers)
856 if c.virtual_overloads then
857 print_meta(c.virtual_overloads)
859 print_meta('extern "C" LQT_EXPORT int luaopen_'..n..' (lua_State *L) {')
860 print_meta('\tlqtL_createclass(L, "'
861 ..lua_name..'*", lqt_metatable'
862 ..c.xarg.id..', lqt_base'
863 ..c.xarg.id..');')
864 print_meta'\treturn 0;'
865 print_meta'}'
866 print_meta''
867 if c.shell and c.qobject then
868 print_meta([[
869 #include <QDebug>
871 QMetaObject lqt_shell_]]..n..[[::staticMetaObject;
873 const QMetaObject *lqt_shell_]]..n..[[::metaObject() const {
874 //int oldtop = lua_gettop(L);
875 lqtL_pushudata(L, this, "]]..c.xarg.fullname..[[*");
876 lua_getfield(L, -1, LQT_OBJMETASTRING);
877 if (lua_isnil(L, -1)) {
878 lua_pop(L, 2);
879 return &]]..c.xarg.fullname..[[::staticMetaObject;
881 lua_getfield(L, -2, LQT_OBJMETADATA);
882 lqtL_touintarray(L);
883 //qDebug() << "copying qmeta object for slots in ]]..c.xarg.fullname..[[";
884 lqt_shell_]]..n..[[::staticMetaObject.d.superdata = &]]..c.xarg.fullname..[[::staticMetaObject;
885 lqt_shell_]]..n..[[::staticMetaObject.d.stringdata = lua_tostring(L, -2);
886 lqt_shell_]]..n..[[::staticMetaObject.d.data = (uint*)lua_touserdata(L, -1);
887 lqt_shell_]]..n..[[::staticMetaObject.d.extradata = 0; // slot_metaobj->d.extradata;
888 lua_setfield(L, LUA_REGISTRYINDEX, LQT_OBJMETADATA);
889 lua_setfield(L, LUA_REGISTRYINDEX, LQT_OBJMETASTRING);
890 lua_pop(L, 1);
891 //qDebug() << (lua_gettop(L) - oldtop);
892 return &lqt_shell_]]..n..[[::staticMetaObject;
895 int lqt_shell_]]..n..[[::qt_metacall(QMetaObject::Call call, int index, void **args) {
896 //qDebug() << "fake calling!";
897 index = ]]..c.xarg.fullname..[[::qt_metacall(call, index, args);
898 if (index < 0) return index;
899 return lqtL_qt_metacall(L, this, call, "QWidget*", index, args);
903 fmeta:close()
906 local print_merged_build = function()
907 local path = module_name.._src
908 local mergename = module_name..'_merged_build'
909 local merged = assert(io.open(path..mergename..'.cpp', 'w'))
910 for _, p in ipairs(cpp_files) do
911 merged:write('#include "'..p..'"\n')
913 local pro_file = assert(io.open(path..mergename..'.pro', 'w'))
915 local print_pro= function(...)
916 pro_file:write(...)
917 pro_file:write'\n'
919 print_pro('TEMPLATE = lib')
920 print_pro('TARGET = '..module_name)
921 print_pro('INCLUDEPATH += .')
922 print_pro('HEADERS += '..module_name..'_slot.hpp')
923 print_pro('SOURCES += ../common/lqt_common.cpp \\')
924 print_pro(' ../common/lqt_qt.cpp \\')
925 print_pro(' '..module_name..'_enum.cpp \\')
926 print_pro(' '..module_name..'_meta.cpp \\')
927 print_pro(' '..module_name..'_slot.cpp \\')
928 print_pro(' '..mergename..'.cpp')
931 local print_class_list = function(classes)
932 local qobject_present = false
933 local big_picture = {}
934 local type_list_t = {}
935 for c in pairs(classes) do
936 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
937 if n=='QObject' then qobject_present = true end
938 print_single_class(c)
939 table.insert(big_picture, 'luaopen_'..n)
940 table.insert(type_list_t, 'add_class(\''..c.xarg.fullname..'\', types)\n')
943 local type_list_f = assert(io.open(module_name.._src..module_name..'_types.lua', 'w'))
944 type_list_f:write([[
945 #!/usr/bin/lua
946 local types = (...) or {}
947 local add_class = lqt.classes.insert or error('module lqt.classes not loaded')
949 for k, v in ipairs(type_list_t) do
950 type_list_f:write(v)
952 type_list_f:write('return types\n')
953 type_list_f:close()
955 print_merged_build()
956 if fmeta then fmeta:close() end
957 fmeta = assert(io.open(module_name.._src..module_name..'_meta.cpp', 'w'))
958 local print_meta = function(...)
959 fmeta:write(...)
960 fmeta:write'\n'
962 print_meta()
963 print_meta('#include "lqt_common.hpp"')
964 print_meta('#include "'..module_name..'_slot.hpp'..'"\n\n')
965 for _, p in ipairs(big_picture) do
966 print_meta('extern "C" LQT_EXPORT int '..p..' (lua_State *);')
968 print_meta('void lqt_create_enums_'..module_name..' (lua_State *);')
969 print_meta('extern "C" LQT_EXPORT int luaopen_'..module_name..' (lua_State *L) {')
970 for _, p in ipairs(big_picture) do
971 print_meta('\t'..p..'(L);')
973 print_meta('\tlqt_create_enums_'..module_name..'(L);')
974 if qobject_present then
975 print_meta('\tlua_getfield(L, LUA_REGISTRYINDEX, "QObject*");')
976 print_meta('\tlua_pushstring(L, "__addmethod");')
977 print_meta('\tlqtL_pushaddmethod(L);')
978 print_meta('\tlua_rawset(L, -3);')
980 print_meta('\t//lua_pushlightuserdata(L, (void*)&LqtSlotAcceptor::staticMetaObject);')
981 print_meta('\t//lua_setfield(L, LUA_REGISTRYINDEX, LQT_METAOBJECT);')
982 print_meta('\tlqtL_passudata(L, (void*)(new LqtSlotAcceptor(L)), "QObject*");')
983 print_meta('\tlua_setfield(L, LUA_REGISTRYINDEX, LQT_METACALLER);')
984 print_meta('\treturn 0;\n}')
985 if fmeta then fmeta:close() end
986 return classes
989 local fix_methods_wrappers = function(classes)
990 for c in pairs(classes) do
991 c.shell = (not c.abstract) and c.public_destr
992 c.shell = c.shell and (next(c.virtuals)~=nil)
993 for _, constr in ipairs(c.constructors) do
994 if c.shell then
995 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
996 constr.calling_line = 'new '..shellname..'(L'
997 if #(constr.arguments)>0 then constr.calling_line = constr.calling_line .. ', ' end
998 else
999 local shellname = c.xarg.fullname
1000 constr.calling_line = 'new '..shellname..'('
1002 for i=1,#(constr.arguments) do
1003 constr.calling_line = constr.calling_line .. (i==1 and '' or ', ') .. 'arg' .. i
1005 constr.calling_line = '*('..constr.calling_line .. '))'
1006 constr.xarg.static = '1'
1007 constr.return_type = constr.xarg.type_base..'&'
1009 if c.destructor then
1010 c.destructor.return_type = nil
1013 return classes
1016 local print_enum_tables = function(enums)
1017 for e in pairs(enums) do
1018 local table = 'static lqt_Enum lqt_enum'..e.xarg.id..'[] = {\n'
1019 --io.stderr:write(e.xarg.fullname, '\t', #e.values, '\n')
1020 for _,v in pairs(e.values) do
1021 table = table .. ' { "' .. v.xarg.name
1022 .. '", static_cast<int>('..v.xarg.fullname..') },\n'
1024 table = table .. ' { 0, 0 }\n'
1025 table = table .. '};\n'
1026 e.enum_table = table
1027 print_enum(table)
1029 return enums
1031 local print_enum_creator = function(enums, n)
1032 local out = 'static lqt_Enumlist lqt_enum_list[] = {\n'
1033 for e in pairs(enums) do
1034 out = out..' { lqt_enum'..e.xarg.id..', "'..string.gsub(e.xarg.fullname, "::", ".")..'" },\n'
1036 out = out..' { 0, 0 },\n};\n'
1037 out = out .. 'void lqt_create_enums_'..n..' (lua_State *L) {\n'
1038 out = out .. ' lqtL_createenumlist(L, lqt_enum_list); return;\n}\n'
1039 print_enum(out)
1040 return enums
1043 local copy_signals = function(functions)
1044 local ret = {}
1045 for f in pairs(functions) do
1046 if f.xarg.signal=='1' then
1047 ret[f] = 1
1050 return ret
1053 local slots_for_signals = function(signals, types)
1054 local ret = {}
1055 for sig in pairs(signals) do
1056 local args, comma = '(', ''
1057 for i, a in ipairs(sig.arguments) do
1058 args = args .. comma .. a.xarg.type_name .. ' arg'..i
1059 comma = ', '
1061 args = args .. ')'
1062 local pushlines, stack = make_pushlines(sig.arguments, types)
1063 if not ret['void __slot '..args] and pushlines then
1064 ret['void __slot '..args] = 'void LqtSlotAcceptor::__slot '..args..[[ {
1065 //int oldtop = lua_gettop(L);
1066 //lua_getfield(L, -1, "__slot]]..string.gsub(args, ' arg.', '')..[[");
1067 //if (lua_isnil(L, -1)) {
1068 //lua_pop(L, 1);
1069 //lua_getfield(L, -1, "__slot");
1071 //if (!lua_isfunction(L, -1)) {
1072 //lua_settop(L, oldtop);
1073 //return;
1075 lua_pushvalue(L, -2);
1076 ]] .. pushlines .. [[
1077 if (lqtL_pcall(L, ]]..stack..[[+1, 0, 0)) {
1078 //lua_error(L);
1079 qDebug() << lua_tostring(L, -1);
1080 lua_pop(L, 1);
1082 //lua_settop(L, oldtop);
1087 return ret
1090 local print_slots = function(s)
1091 print_slot_h'class LqtSlotAcceptor : public QObject {'
1092 print_slot_h' Q_OBJECT'
1093 print_slot_h' lua_State *L;'
1094 print_slot_h' public:'
1095 print_slot_h' LqtSlotAcceptor(lua_State *l, QObject *p=NULL) : QObject(p), L(l) { lqtL_register(L, this); }'
1096 print_slot_h' virtual ~LqtSlotAcceptor() { lqtL_unregister(L, this); }'
1097 print_slot_h' public slots:'
1098 for p, b in pairs(s) do
1099 print_slot_h(' '..p..';')
1101 print_slot_h'};\n'
1102 for p, b in pairs(s) do
1103 print_slot_c(b)
1108 --------------------------------------------------------------------------------------
1110 local typesystem = dofile(path..'types.lua')
1112 local ts = {}
1113 for i, ft in ipairs(typefiles) do
1114 ts = assert(loadfile(ft))(ts)
1116 setmetatable(typesystem, {
1117 __newindex = function(t, k, v)
1118 --debug('added type', k)
1119 ts[k] = v
1120 end,
1121 __index = function(t, k)
1122 local ret = ts[k]
1123 --if not ret then debug("unknown type:", tostring(k), ret) end
1124 return ret
1125 end,
1129 fix_arguments(idindex) -- fixes default arguments if they are context-relative
1130 local functions = copy_functions(idindex) -- picks functions and fixes label
1131 local functions = fix_functions(functions) -- fixes name and fullname and fills arguments
1133 local enums = copy_enums(idindex) -- picks enums if public
1134 local enums = fill_enums(enums) -- fills field "values"
1136 local classes = copy_classes(idindex) -- picks classes if not private and not blacklisted
1137 local classes = fill_virtuals(classes) -- does that, destructor ("~") excluded
1138 local classes = distinguish_methods(classes) -- does that
1139 local classes = fill_public_destr(classes) -- does that: checks if destructor is public
1140 local classes = fill_copy_constructor(classes) -- does that: checks if copy contructor is public or protected
1141 local classes = fix_methods_wrappers(classes)
1142 local classes = get_qobjects(classes)
1144 for _, f in ipairs(filterfiles) do
1145 classes, enums = loadfile(f)(classes, enums)
1148 local enums = fill_typesystem_with_enums(enums, typesystem) -- does that
1149 local classes = fill_typesystem_with_classes(classes, typesystem)
1151 local functions = fill_wrappers(functions, typesystem)
1152 local classes = fill_virtual_overloads(classes, typesystem) -- does that
1153 local classes = fill_shell_classes(classes, typesystem) -- does that
1155 local signals = copy_signals(functions)
1156 local slots = slots_for_signals(signals, typesystem)
1159 ------------- BEGIN OUTPUT
1163 print_enum(output_includes)
1164 print_slot_h(output_includes)
1165 print_slot_c('#include "'..module_name..'_slot.hpp'..'"\n\n')
1168 local classes = print_shell_classes(classes) -- does that
1169 local classes = print_virtual_overloads(classes, typesystem) -- does that
1170 local enums = print_enum_tables(enums) -- does that
1171 local enums = print_enum_creator(enums, module_name) -- does that + print enum list
1172 local classes = print_wrappers(classes) -- just compiles metatable list
1173 local classes = print_metatables(classes) -- just collects the wrappers + generates dispatchers
1174 local classes = print_class_list(classes) -- does that + prints everything related to class
1176 local slots = print_slots(slots)
1178 --print_openmodule(module_name) -- does that