use separate slot implementation for each module
[lqt.git] / generator / generator.lua
blobb19acd3cd1a0874b4f6acdb95866a494215658a6
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)
320 and (not f.xarg.friend) then
321 table.insert(normal, f)
325 c.constructors = construct
326 c.destructor = destruct
327 c.methods = normal
329 return index
332 local fill_public_destr = function(index)
333 local classes = {}
334 for c in pairs(index) do
335 classes[c.xarg.fullname] = c
337 local destr_is_public
338 destr_is_public = function(c)
339 if c.destructor then
340 return c.destructor.xarg.access=='public'
341 else
342 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
343 local base = classes[b]
344 if base and not destr_is_public(base) then
345 return false
348 return true
351 for c in pairs(index) do
352 c.public_destr = destr_is_public(c)
354 return index
357 local fill_copy_constructor = function(index)
358 local classes = {}
359 for c in pairs(index) do
360 classes[c.xarg.name] = c
362 for c in pairs(index) do
363 local copy = nil
364 for _, f in ipairs(c.constructors) do
365 if #(f.arguments)==1
366 and f.arguments[1].xarg.type_name==c.xarg.fullname..' const&' then
367 copy = f
368 break
371 c.copy_constructor = copy
373 local copy_constr_is_public
374 copy_constr_is_public = function(c)
375 if c.copy_constructor then
376 return (c.copy_constructor.xarg.access=='public')
377 or (c.copy_constructor.xarg.access=='protected')
378 else
379 local ret = nil
380 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
381 local base = classes[b]
382 if base and not copy_constr_is_public(base) then
383 return false
386 return true
389 for c in pairs(index) do
390 c.public_constr = copy_constr_is_public(c)
392 return index
395 local fill_typesystem_with_enums = function(enums, types)
396 local etype = function(en)
397 return {
398 push = function(n)
399 return 'lqtL_pushenum(L, '..n..', "'..string.gsub(en, '::', '.')..'")', 1
400 end,
401 get = function(n)
402 return 'static_cast<'..en..'>'
403 ..'(lqtL_toenum(L, '..n..', "'..string.gsub(en, '::', '.')..'"))', 1
404 end,
405 test = function(n)
406 return 'lqtL_isenum(L, '..n..', "'..string.gsub(en, '::', '.')..'")', 1
407 end,
408 onstack = string.gsub(en, '::', '.')..',',
411 local ret = {}
412 for e in pairs(enums) do
413 if types[e.xarg.fullname]==nil then
414 ret[e] = true
415 types[e.xarg.fullname] = etype(e.xarg.fullname)
416 else
417 --io.stderr:write(e.xarg.fullname, ': already present\n')
420 return ret
423 local put_class_in_filesystem = lqt.classes.insert
424 local fill_typesystem_with_classes = function(classes, types)
425 local ret = {}
426 for c in pairs(classes) do
427 ret[c] = put_class_in_filesystem(c.xarg.fullname, types) --, true)
429 return ret
432 local argument_name = function(tn, an)
433 local ret
434 if string.match(tn, '%(%*%)') then
435 ret = string.gsub(tn, '%(%*%)', '(*'..an..')', 1)
436 elseif string.match(tn, '%[.*%]') then
437 ret = string.gsub(tn, '%s*(%[.*%])', ' '..an..'%1')
438 else
439 ret = tn .. ' ' .. an
441 return ret
444 local fill_wrapper_code = function(f, types)
445 if f.wrapper_code then return f end
446 local stackn, argn = 1, 1
447 local stack_args, defects = '', 0
448 local wrap, line = ' int oldtop = lua_gettop(L);\n', ''
449 if f.xarg.abstract then return nil end
450 if f.xarg.member_of_class and f.xarg.static~='1' then
451 if not types[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
452 stack_args = stack_args .. types[f.xarg.member_of_class..'*'].onstack
453 defects = defects + 7 -- FIXME: arbitrary
454 if f.xarg.constant=='1' then
455 defects = defects + 8 -- FIXME: arbitrary
457 local sget, sn = types[f.xarg.member_of_class..'*'].get(stackn)
458 wrap = wrap .. ' ' .. f.xarg.member_of_class .. '* self = ' .. sget .. ';\n'
459 stackn = stackn + sn
460 wrap = wrap .. [[
461 if (NULL==self) {
462 lua_pushstring(L, "this pointer is NULL");
463 lua_error(L);
466 --print(sget, sn)
467 line = 'self->'..f.xarg.fullname..'('
468 else
469 line = f.xarg.fullname..'('
471 for i, a in ipairs(f.arguments) do
472 if not types[a.xarg.type_name] then return nil end
473 local aget, an, arg_as = types[a.xarg.type_name].get(stackn)
474 stack_args = stack_args .. types[a.xarg.type_name].onstack
475 if types[a.xarg.type_name].defect then defects = defects + types[a.xarg.type_name].defect end
476 wrap = wrap .. ' ' .. argument_name(arg_as or a.xarg.type_name, 'arg'..argn) .. ' = '
477 if a.xarg.default=='1' and an>0 then
478 wrap = wrap .. 'lua_isnoneornil(L, '..stackn..')'
479 for j = stackn+1,stackn+an-1 do
480 wrap = wrap .. ' && lua_isnoneornil(L, '..j..')'
482 local dv = a.xarg.defaultvalue
483 wrap = wrap .. ' ? static_cast< ' .. a.xarg.type_name .. ' >(' .. dv .. ') : '
485 wrap = wrap .. aget .. ';\n'
486 line = line .. (argn==1 and 'arg' or ', arg') .. argn
487 stackn = stackn + an
488 argn = argn + 1
490 line = line .. ')'
491 -- FIXME: hack follows for constructors
492 if f.calling_line then line = f.calling_line end
493 if f.return_type then line = f.return_type .. ' ret = ' .. line end
494 wrap = wrap .. ' ' .. line .. ';\n lua_settop(L, oldtop);\n' -- lua_pop(L, '..stackn..');\n'
495 if f.return_type then
496 if not types[f.return_type] then return nil end
497 local rput, rn = types[f.return_type].push'ret'
498 wrap = wrap .. ' luaL_checkstack(L, '..rn..', "cannot grow stack for return value");\n'
499 wrap = wrap .. ' '..rput..';\n return '..rn..';\n'
500 else
501 wrap = wrap .. ' return 0;\n'
503 f.wrapper_code = wrap
504 f.stack_arguments = stack_args
505 f.defects = defects
506 return f
509 local fill_test_code = function(f, types)
510 local stackn = 1
511 local test = ''
512 if f.xarg.member_of_class and f.xarg.static~='1' then
513 if not types[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
514 local stest, sn = types[f.xarg.member_of_class..'*'].test(stackn)
515 test = test .. ' && ' .. stest
516 stackn = stackn + sn
518 for i, a in ipairs(f.arguments) do
519 if not types[a.xarg.type_name] then return nil end -- print(a.xarg.type_name) return nil end
520 local atest, an = types[a.xarg.type_name].test(stackn)
521 if a.xarg.default=='1' and an>0 then
522 test = test .. ' && (lqtL_missarg(L, ' .. stackn .. ', ' .. an .. ') || '
523 test = test .. atest .. ')'
524 else
525 test = test .. ' && ' .. atest
527 stackn = stackn + an
529 -- can't make use of default values if I fix number of args
530 test = '(lua_gettop(L)<' .. stackn .. ')' .. test
531 f.test_code = test
532 return f
535 local fill_wrappers = function(functions, types)
536 local ret = {}
537 for f in pairs(functions) do
538 f = fill_wrapper_code(f, types)
539 if f then
540 f = assert(fill_test_code(f, types), f.xarg.fullname) -- MUST pass
541 ret[f] = true
542 --local out = 'extern "C" LQT_EXPORT int lqt_bind'..f.xarg.id..' (lua_State *L) {\n'
543 --.. f.wrapper_code .. '}\n'
544 --print(out)
547 return ret
550 local make_pushlines = function(args, types)
551 local pushlines, stack = '', 0
552 for i, a in ipairs(args) do
553 if not types[a.xarg.type_name] then return nil end
554 local apush, an = types[a.xarg.type_name].push('arg'..i)
555 pushlines = pushlines .. ' ' .. apush .. ';\n'
556 stack = stack + an
558 return pushlines, stack
561 local virtual_overload = function(v, types)
562 local ret = ''
563 if v.virtual_overload then return v end
564 -- make return type
565 if v.return_type and not types[v.return_type] then return nil end
566 local rget, rn = '', 0
567 if v.return_type then rget, rn, ret_as = types[v.return_type].get'oldtop+1' end
568 local retget = (v.return_type and argument_name(ret_as or v.return_type, 'ret')
569 .. ' = ' .. rget .. ';' or '') .. 'lua_settop(L, oldtop);return'
570 .. (v.return_type and ' ret' or '')
571 -- make argument push
572 local pushlines, stack = make_pushlines(v.arguments, types)
573 if not pushlines then return nil end
574 -- make lua call
575 local luacall = 'lqtL_pcall(L, '..(stack+1)..', '..rn..', 0)'
576 -- make prototype and fallback
577 local proto = (v.return_type or 'void')..' ;;'..v.xarg.name..' ('
578 local fallback = ''
579 for i, a in ipairs(v.arguments) do
580 proto = proto .. (i>1 and ', ' or '')
581 .. argument_name(a.xarg.type_name, 'arg'..i)
582 fallback = fallback .. (i>1 and ', arg' or 'arg') .. i
584 proto = proto .. ')' .. (v.xarg.constant=='1' and ' const' or '')
585 fallback = (v.return_type and 'return this->' or 'this->')
586 .. v.xarg.fullname .. '(' .. fallback .. ');\n}\n'
587 ret = proto .. [[ {
588 int oldtop = lua_gettop(L);
589 lqtL_pushudata(L, this, "]]..string.gsub(v.xarg.member_of_class, '::', '.')..[[*");
590 lqtL_getoverload(L, -1, "]]..v.xarg.name..[[");
591 if (lua_isfunction(L, -1)) {
592 lua_insert(L, -2);
593 ]] .. pushlines .. [[
594 if (!]]..luacall..[[) {
595 lua_error(L);
598 lua_settop(L, oldtop);
599 ]] .. fallback
600 v.virtual_overload = ret
601 v.virtual_proto = string.gsub(proto, ';;', '', 1)
602 return v
605 local fill_shell_class = function(c, types)
606 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
607 local shell = 'class LQT_EXPORT ' .. shellname .. ' : public ' .. c.xarg.fullname .. ' {\npublic:\n'
608 shell = shell .. ' lua_State *L;\n'
609 for _, constr in ipairs(c.constructors) do
610 if constr.xarg.access~='private' then
611 local cline = ' '..shellname..' (lua_State *l'
612 local argline = ''
613 for i, a in ipairs(constr.arguments) do
614 cline = cline .. ', ' .. argument_name(a.xarg.type_name, 'arg'..i)
615 argline = argline .. (i>1 and ', arg' or 'arg') .. i
617 cline = cline .. ') : ' .. c.xarg.fullname
618 .. '(' .. argline .. '), L(l) '
619 .. '{ lqtL_register(L, this); }\n'
620 shell = shell .. cline
623 if c.copy_constructor==nil and c.public_constr then
624 local cline = ' '..shellname..' (lua_State *l, '..c.xarg.fullname..' const& arg1)'
625 cline = cline .. ' : ' .. c.xarg.fullname .. '(arg1), L(l) {}\n'
626 shell = shell .. cline
628 for i, v in pairs(c.virtuals) do
629 if v.xarg.access~='private' then
630 if v.virtual_proto then shell = shell .. ' virtual ' .. v.virtual_proto .. ';\n' end
633 shell = shell .. ' ~'..shellname..'() { lqtL_unregister(L, this); }\n'
634 if c.shell and c.qobject then
635 shell = shell .. ' static QMetaObject staticMetaObject;\n'
636 shell = shell .. ' virtual const QMetaObject *metaObject() const;\n'
637 shell = shell .. ' virtual int qt_metacall(QMetaObject::Call, int, void **);\n'
638 shell = shell .. 'private:\n'
639 shell = shell .. ' Q_DISABLE_COPY('..shellname..');\n'
641 shell = shell .. '};\n'
642 c.shell_class = shell
643 return c
646 local fill_virtual_overloads = function(classes, types)
647 for c in pairs(classes) do
648 for i, v in pairs(c.virtuals) do
649 if v.xarg.access~='private' then
650 local vret = virtual_overload(v, types)
654 return classes
657 local fill_shell_classes = function(classes, types)
658 local ret = {}
659 for c in pairs(classes) do
660 if c.shell then
661 c = fill_shell_class(c, types)
662 if c then ret[c] = true end
664 ret[c] = true
666 return ret
669 local print_shell_classes = function(classes)
670 local fhead = nil
671 for c in pairs(classes) do
672 if fhead then fhead:close() end
673 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
674 fhead = assert(io.open(module_name.._src..module_name..'_head_'..n..'.hpp', 'w'))
675 local print_head = function(...)
676 fhead:write(...)
677 fhead:write'\n'
679 print_head('#ifndef LQT_HEAD_'..n)
680 print_head('#define LQT_HEAD_'..n)
681 print_head(output_includes)
682 --print_head('#include <'..string.match(c.xarg.fullname, '^[^:]+')..'>')
683 print_head''
684 if c.shell then
685 print_head('#include "'..module_name..'_slot.hpp'..'"\n\n')
686 if c then
687 print_head(c.shell_class)
688 else
689 --io.stderr:write(c.fullname, '\n')
692 print_head('#endif // LQT_HEAD_'..n)
694 if fhead then fhead:close() end
695 return classes
698 local print_virtual_overloads = function(classes)
699 for c in pairs(classes) do
700 if c.shell then
701 local vo = ''
702 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
703 for _,v in pairs(c.virtuals) do
704 if v.virtual_overload then
705 vo = vo .. string.gsub(v.virtual_overload, ';;', shellname..'::', 1)
708 c.virtual_overloads = vo
711 return classes
714 local print_wrappers = function(index)
715 for c in pairs(index) do
716 local meta = {}
717 local wrappers = ''
718 for _, f in ipairs(c.methods) do
719 -- FIXME: should we really discard virtual functions?
720 -- if the virtual overload in the shell uses rawget
721 -- on the environment then we can leave these in the
722 -- metatable
723 if f.wrapper_code and f.xarg.abstract~='1' then
724 local out = 'static int lqt_bind'..f.xarg.id
725 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
726 if f.xarg.access=='public' then
727 --print_meta(out)
728 wrappers = wrappers .. out .. '\n'
729 meta[f] = f.xarg.name
733 if not c.abstract then
734 for _, f in ipairs(c.constructors) do
735 if f.wrapper_code then
736 local out = 'static int lqt_bind'..f.xarg.id
737 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
738 if f.xarg.access=='public' then
739 --print_meta(out)
740 wrappers = wrappers .. out .. '\n'
741 meta[f] = 'new'
746 --local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
747 local lua_name = string.gsub(c.xarg.fullname, '::', '.')
748 local out = 'static int lqt_delete'..c.xarg.id..' (lua_State *L) {\n'
749 out = out ..' '..c.xarg.fullname..' *p = static_cast<'
750 ..c.xarg.fullname..'*>(lqtL_toudata(L, 1, "'..lua_name..'*"));\n'
751 if c.public_destr then
752 out = out .. ' if (p) delete p;\n'
754 out = out .. ' lqtL_eraseudata(L, 1, "'..lua_name..'*");\n return 0;\n}\n'
755 --print_meta(out)
756 wrappers = wrappers .. out .. '\n'
757 c.meta = meta
758 c.wrappers = wrappers
760 return index
763 local print_metatable = function(c)
764 local methods = {}
765 local wrappers = c.wrappers
766 for m, n in pairs(c.meta) do
767 methods[n] = methods[n] or {}
768 table.insert(methods[n], m)
770 for n, l in pairs(methods) do
771 local duplicates = {}
772 for _, f in ipairs(l) do
773 local itisnew = true
774 for sa, g in pairs(duplicates) do
775 if sa==f.stack_arguments then
776 --debug("function equal: ", f.xarg.fullname, f.stack_arguments, sa, f.defects, g.defects)
777 if f.defects<g.defects then
778 else
779 itisnew = false
781 elseif string.match(sa, "^"..f.stack_arguments) then -- there is already a version with more arguments
782 --debug("function superseded: ", f.xarg.fullname, f.stack_arguments, sa, f.defects, g.defects)
783 elseif string.match(f.stack_arguments, '^'..sa) then -- there is already a version with less arguments
784 --debug("function superseding: ", f.xarg.fullname, f.stack_arguments, sa, f.defects, g.defects)
787 if itisnew then
788 duplicates[f.stack_arguments] = f
791 --[[
792 local numinitial = 0
793 local numfinal = 0
794 for sa, f in pairs(l) do
795 numinitial = numinitial + 1
797 for sa, f in pairs(duplicates) do
798 numfinal = numfinal + 1
800 if numinitial-numfinal>0 then debug(c.xarg.fullname, "suppressed:", numinitial-numfinal) end
801 --]]
802 methods[n] = duplicates
804 for n, l in pairs(methods) do
805 local disp = 'static int lqt_dispatcher_'..n..c.xarg.id..' (lua_State *L) {\n'
806 for _, f in pairs(l) do
807 disp = disp..' if ('..f.test_code..') return lqt_bind'..f.xarg.id..'(L);\n'
809 disp = disp .. ' lua_settop(L, 0);\n'
810 disp = disp .. ' lua_pushstring(L, "'..c.xarg.fullname..'::'..n..': incorrect or extra arguments");\n'
811 disp = disp .. ' return lua_error(L);\n}\n'
812 --print_meta(disp)
813 wrappers = wrappers .. disp .. '\n'
815 local metatable = 'static luaL_Reg lqt_metatable'..c.xarg.id..'[] = {\n'
816 for n, l in pairs(methods) do
817 metatable = metatable .. ' { "'..n..'", lqt_dispatcher_'..n..c.xarg.id..' },\n'
819 metatable = metatable .. ' { "delete", lqt_delete'..c.xarg.id..' },\n'
820 metatable = metatable .. ' { 0, 0 },\n};\n'
821 --print_meta(metatable)
822 wrappers = wrappers .. metatable .. '\n'
823 local bases = ''
824 for b in string.gmatch(c.xarg.bases_with_attributes or '', '([^;]*);') do
825 if not string.match(b, '^virtual') then
826 b = string.gsub(b, '^[^%s]* ', '')
827 bases = bases .. ' {"'..string.gsub(b,'::','.')..'*", (char*)(void*)static_cast<'..b..'*>(('..c.xarg.fullname..'*)1)-(char*)1},\n'
830 bases = 'static lqt_Base lqt_base'..c.xarg.id..'[] = {\n'..bases..' {NULL, 0}\n};\n'
831 --print_meta(bases)
832 wrappers = wrappers .. bases .. '\n'
833 c.wrappers = wrappers
834 return c
837 local print_metatables = function(classes)
838 for c in pairs(classes) do
839 print_metatable(c)
841 return classes
844 local cpp_files = {}
846 local print_single_class = function(c)
847 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
848 local lua_name = string.gsub(c.xarg.fullname, '::', '.')
849 local cppname = module_name..'_meta_'..n..'.cpp'
850 table.insert(cpp_files, cppname);
851 local fmeta = assert(io.open(module_name.._src..cppname, 'w'))
852 local print_meta = function(...)
853 fmeta:write(...)
854 fmeta:write'\n'
856 print_meta('#include "'..module_name..'_head_'..n..'.hpp'..'"\n\n')
857 print_meta(c.wrappers)
858 if c.virtual_overloads then
859 print_meta(c.virtual_overloads)
861 print_meta('extern "C" LQT_EXPORT int luaopen_'..n..' (lua_State *L) {')
862 print_meta('\tlqtL_createclass(L, "'
863 ..lua_name..'*", lqt_metatable'
864 ..c.xarg.id..', lqt_base'
865 ..c.xarg.id..');')
866 print_meta'\treturn 0;'
867 print_meta'}'
868 print_meta''
869 if c.shell and c.qobject then
870 print_meta([[
871 #include <QDebug>
873 QMetaObject lqt_shell_]]..n..[[::staticMetaObject;
875 const QMetaObject *lqt_shell_]]..n..[[::metaObject() const {
876 //int oldtop = lua_gettop(L);
877 lqtL_pushudata(L, this, "]]..c.xarg.fullname..[[*");
878 lua_getfield(L, -1, LQT_OBJMETASTRING);
879 if (lua_isnil(L, -1)) {
880 lua_pop(L, 2);
881 return &]]..c.xarg.fullname..[[::staticMetaObject;
883 lua_getfield(L, -2, LQT_OBJMETADATA);
884 lqtL_touintarray(L);
885 //qDebug() << "copying qmeta object for slots in ]]..c.xarg.fullname..[[";
886 lqt_shell_]]..n..[[::staticMetaObject.d.superdata = &]]..c.xarg.fullname..[[::staticMetaObject;
887 lqt_shell_]]..n..[[::staticMetaObject.d.stringdata = lua_tostring(L, -2);
888 lqt_shell_]]..n..[[::staticMetaObject.d.data = (uint*)lua_touserdata(L, -1);
889 lqt_shell_]]..n..[[::staticMetaObject.d.extradata = 0; // slot_metaobj->d.extradata;
890 lua_setfield(L, LUA_REGISTRYINDEX, LQT_OBJMETADATA);
891 lua_setfield(L, LUA_REGISTRYINDEX, LQT_OBJMETASTRING);
892 lua_pop(L, 1);
893 //qDebug() << (lua_gettop(L) - oldtop);
894 return &lqt_shell_]]..n..[[::staticMetaObject;
897 int lqt_shell_]]..n..[[::qt_metacall(QMetaObject::Call call, int index, void **args) {
898 //qDebug() << "fake calling!";
899 index = ]]..c.xarg.fullname..[[::qt_metacall(call, index, args);
900 if (index < 0) return index;
901 return lqtL_qt_metacall(L, this, lqtSlotAcceptor_]]..module_name..[[, call, "]]..c.xarg.fullname..[[*", index, args);
905 fmeta:close()
908 local print_merged_build = function()
909 local path = module_name.._src
910 local mergename = module_name..'_merged_build'
911 local merged = assert(io.open(path..mergename..'.cpp', 'w'))
912 for _, p in ipairs(cpp_files) do
913 merged:write('#include "'..p..'"\n')
915 local pro_file = assert(io.open(path..mergename..'.pro', 'w'))
917 local print_pro= function(...)
918 pro_file:write(...)
919 pro_file:write'\n'
921 print_pro('TEMPLATE = lib')
922 print_pro('TARGET = '..module_name)
923 print_pro('INCLUDEPATH += .')
924 print_pro('HEADERS += '..module_name..'_slot.hpp')
925 print_pro('SOURCES += ../common/lqt_common.cpp \\')
926 print_pro(' ../common/lqt_qt.cpp \\')
927 print_pro(' '..module_name..'_enum.cpp \\')
928 print_pro(' '..module_name..'_meta.cpp \\')
929 print_pro(' '..module_name..'_slot.cpp \\')
930 print_pro(' '..mergename..'.cpp')
933 local print_class_list = function(classes)
934 local qobject_present = false
935 local big_picture = {}
936 local type_list_t = {}
937 for c in pairs(classes) do
938 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
939 if n=='QObject' then qobject_present = true end
940 print_single_class(c)
941 table.insert(big_picture, 'luaopen_'..n)
942 table.insert(type_list_t, 'add_class(\''..c.xarg.fullname..'\', types)\n')
945 local type_list_f = assert(io.open(module_name.._src..module_name..'_types.lua', 'w'))
946 type_list_f:write([[
947 #!/usr/bin/lua
948 local types = (...) or {}
949 local add_class = lqt.classes.insert or error('module lqt.classes not loaded')
951 for k, v in ipairs(type_list_t) do
952 type_list_f:write(v)
954 type_list_f:write('return types\n')
955 type_list_f:close()
957 print_merged_build()
958 if fmeta then fmeta:close() end
959 fmeta = assert(io.open(module_name.._src..module_name..'_meta.cpp', 'w'))
960 local print_meta = function(...)
961 fmeta:write(...)
962 fmeta:write'\n'
964 print_meta()
965 print_meta('#include "lqt_common.hpp"')
966 print_meta('#include "'..module_name..'_slot.hpp'..'"\n\n')
967 for _, p in ipairs(big_picture) do
968 print_meta('extern "C" LQT_EXPORT int '..p..' (lua_State *);')
970 print_meta('void lqt_create_enums_'..module_name..' (lua_State *);')
971 print_meta('extern "C" LQT_EXPORT int luaopen_'..module_name..' (lua_State *L) {')
972 for _, p in ipairs(big_picture) do
973 print_meta('\t'..p..'(L);')
975 print_meta('\tlqt_create_enums_'..module_name..'(L);')
976 if qobject_present then
977 print_meta('\tlua_getfield(L, LUA_REGISTRYINDEX, "QObject*");')
978 print_meta('\tlua_pushstring(L, "__addmethod");')
979 print_meta('\tlqtL_pushaddmethod(L);')
980 print_meta('\tlua_rawset(L, -3);')
982 print_meta('\t//lua_pushlightuserdata(L, (void*)&LqtSlotAcceptor::staticMetaObject);')
983 print_meta('\t//lua_setfield(L, LUA_REGISTRYINDEX, LQT_METAOBJECT);')
984 print_meta('\t//lqtL_passudata(L, (void*)(new LqtSlotAcceptor(L)), "QObject*");')
985 print_meta('\t//lua_setfield(L, LUA_REGISTRYINDEX, LQT_METACALLER);')
986 print_meta('\tlqtSlotAcceptor_'..module_name..' = new LqtSlotAcceptor(L);')
987 print_meta('\treturn 0;\n}')
988 if fmeta then fmeta:close() end
989 return classes
992 local fix_methods_wrappers = function(classes)
993 for c in pairs(classes) do
994 c.shell = (not c.abstract) and c.public_destr
995 c.shell = c.shell and (next(c.virtuals)~=nil)
996 for _, constr in ipairs(c.constructors) do
997 if c.shell then
998 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
999 constr.calling_line = 'new '..shellname..'(L'
1000 if #(constr.arguments)>0 then constr.calling_line = constr.calling_line .. ', ' end
1001 else
1002 local shellname = c.xarg.fullname
1003 constr.calling_line = 'new '..shellname..'('
1005 for i=1,#(constr.arguments) do
1006 constr.calling_line = constr.calling_line .. (i==1 and '' or ', ') .. 'arg' .. i
1008 constr.calling_line = '*('..constr.calling_line .. '))'
1009 constr.xarg.static = '1'
1010 constr.return_type = constr.xarg.type_base..'&'
1012 if c.destructor then
1013 c.destructor.return_type = nil
1016 return classes
1019 local print_enum_tables = function(enums)
1020 for e in pairs(enums) do
1021 local table = 'static lqt_Enum lqt_enum'..e.xarg.id..'[] = {\n'
1022 --io.stderr:write(e.xarg.fullname, '\t', #e.values, '\n')
1023 for _,v in pairs(e.values) do
1024 table = table .. ' { "' .. v.xarg.name
1025 .. '", static_cast<int>('..v.xarg.fullname..') },\n'
1027 table = table .. ' { 0, 0 }\n'
1028 table = table .. '};\n'
1029 e.enum_table = table
1030 print_enum(table)
1032 return enums
1034 local print_enum_creator = function(enums, n)
1035 local out = 'static lqt_Enumlist lqt_enum_list[] = {\n'
1036 for e in pairs(enums) do
1037 out = out..' { lqt_enum'..e.xarg.id..', "'..string.gsub(e.xarg.fullname, "::", ".")..'" },\n'
1039 out = out..' { 0, 0 },\n};\n'
1040 out = out .. 'void lqt_create_enums_'..n..' (lua_State *L) {\n'
1041 out = out .. ' lqtL_createenumlist(L, lqt_enum_list); return;\n}\n'
1042 print_enum(out)
1043 return enums
1046 local copy_signals = function(functions)
1047 local ret = {}
1048 for f in pairs(functions) do
1049 if f.xarg.signal=='1' then
1050 ret[f] = 1
1053 return ret
1056 local slots_for_signals = function(signals, types)
1057 local ret = {}
1058 for sig in pairs(signals) do
1059 local args, comma = '(', ''
1060 for i, a in ipairs(sig.arguments) do
1061 args = args .. comma .. a.xarg.type_name .. ' arg'..i
1062 comma = ', '
1064 args = args .. ')'
1065 local pushlines, stack = make_pushlines(sig.arguments, types)
1066 if not ret['void __slot '..args] and pushlines then
1067 ret['void __slot '..args] = 'void LqtSlotAcceptor::__slot '..args..[[ {
1068 //int oldtop = lua_gettop(L);
1069 //lua_getfield(L, -1, "__slot]]..string.gsub(args, ' arg.', '')..[[");
1070 //if (lua_isnil(L, -1)) {
1071 //lua_pop(L, 1);
1072 //lua_getfield(L, -1, "__slot");
1074 //if (!lua_isfunction(L, -1)) {
1075 //lua_settop(L, oldtop);
1076 //return;
1078 lua_pushvalue(L, -2);
1079 ]] .. pushlines .. [[
1080 if (lqtL_pcall(L, ]]..stack..[[+1, 0, 0)) {
1081 //lua_error(L);
1082 qDebug() << lua_tostring(L, -1);
1083 lua_pop(L, 1);
1085 //lua_settop(L, oldtop);
1090 return ret
1093 local print_slots = function(s)
1094 print_slot_h'class LqtSlotAcceptor : public QObject {'
1095 print_slot_h' Q_OBJECT'
1096 print_slot_h' lua_State *L;'
1097 print_slot_h' public:'
1098 print_slot_h(' LqtSlotAcceptor(lua_State *l, QObject *p=NULL) : QObject(p), L(l) { setObjectName("'..module_name..'"); lqtL_register(L, this); }')
1099 print_slot_h' virtual ~LqtSlotAcceptor() { lqtL_unregister(L, this); }'
1100 print_slot_h' public slots:'
1101 for p, b in pairs(s) do
1102 print_slot_h(' '..p..';')
1104 print_slot_h'};\n'
1105 print_slot_h('\nextern LqtSlotAcceptor *lqtSlotAcceptor_'..module_name..';')
1106 for p, b in pairs(s) do
1107 print_slot_c(b)
1109 print_slot_c('\nLqtSlotAcceptor *lqtSlotAcceptor_'..module_name..';')
1113 --------------------------------------------------------------------------------------
1115 local typesystem = dofile(path..'types.lua')
1117 local ts = {}
1118 for i, ft in ipairs(typefiles) do
1119 ts = assert(loadfile(ft))(ts)
1121 setmetatable(typesystem, {
1122 __newindex = function(t, k, v)
1123 --debug('added type', k)
1124 ts[k] = v
1125 end,
1126 __index = function(t, k)
1127 local ret = ts[k]
1128 --if not ret then debug("unknown type:", tostring(k), ret) end
1129 return ret
1130 end,
1134 fix_arguments(idindex) -- fixes default arguments if they are context-relative
1135 local functions = copy_functions(idindex) -- picks functions and fixes label
1136 local functions = fix_functions(functions) -- fixes name and fullname and fills arguments
1138 local enums = copy_enums(idindex) -- picks enums if public
1139 local enums = fill_enums(enums) -- fills field "values"
1141 local classes = copy_classes(idindex) -- picks classes if not private and not blacklisted
1142 local classes = fill_virtuals(classes) -- does that, destructor ("~") excluded
1143 local classes = distinguish_methods(classes) -- does that
1144 local classes = fill_public_destr(classes) -- does that: checks if destructor is public
1145 local classes = fill_copy_constructor(classes) -- does that: checks if copy contructor is public or protected
1146 local classes = fix_methods_wrappers(classes)
1147 local classes = get_qobjects(classes)
1149 for _, f in ipairs(filterfiles) do
1150 classes, enums = loadfile(f)(classes, enums)
1153 local enums = fill_typesystem_with_enums(enums, typesystem) -- does that
1154 local classes = fill_typesystem_with_classes(classes, typesystem)
1156 local functions = fill_wrappers(functions, typesystem)
1157 local classes = fill_virtual_overloads(classes, typesystem) -- does that
1158 local classes = fill_shell_classes(classes, typesystem) -- does that
1160 local signals = copy_signals(functions)
1161 local slots = slots_for_signals(signals, typesystem)
1164 ------------- BEGIN OUTPUT
1168 print_enum(output_includes)
1169 print_slot_h('#ifndef LQT_SLOT_'..module_name)
1170 print_slot_h('#define LQT_SLOT_'..module_name)
1171 print_slot_h(output_includes)
1172 print_slot_c('#include "'..module_name..'_slot.hpp'..'"\n\n')
1175 local classes = print_shell_classes(classes) -- does that
1176 local classes = print_virtual_overloads(classes, typesystem) -- does that
1177 local enums = print_enum_tables(enums) -- does that
1178 local enums = print_enum_creator(enums, module_name) -- does that + print enum list
1179 local classes = print_wrappers(classes) -- just compiles metatable list
1180 local classes = print_metatables(classes) -- just collects the wrappers + generates dispatchers
1181 local classes = print_class_list(classes) -- does that + prints everything related to class
1183 local slots = print_slots(slots)
1185 print_slot_h('#endif')
1187 --print_openmodule(module_name) -- does that