print the method name on argument mismatch
[lqt.git] / generator / generator.lua
blobd5eb37abbc5c5179edfc5a2482d592e07e526507
1 #!/usr/bin/lua
3 --[[
5 Copyright (c) 2007-2008 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*$'))
133 and a.xarg.defaultvalue~='true'
134 and a.xarg.defaultvalue~='false'
135 and (not string.match(a.xarg.defaultvalue, '^0[xX]%d+$')) then
136 local dv, call = string.match(a.xarg.defaultvalue, '(.-)(%b())')
137 dv = dv or a.xarg.defaultvalue
138 call = call or ''
139 if not fullnames[dv] then
140 dv = a.xarg.context..'::'..dv..call
142 if fullnames[dv] then
143 a.xarg.defaultvalue = dv..call
144 else
145 a.xarg.default = nil
146 a.xarg.defaultvalue = nil
150 return all
153 local fix_functions = function(index)
154 for f in pairs(index) do
155 local args = {}
156 for i, a in ipairs(f) do
157 -- avoid bogus 'void' arguments
158 if a.xarg.type_name=='void' and i==1 and f[2]==nil then break end
159 if a.label=='Argument' then
160 table.insert(args, a)
163 f.arguments = args
164 f.return_type = f.xarg.type_name
165 if f.xarg.type_name=='void' then
166 f.return_type = nil
169 return index
172 local copy_enums = function(index)
173 local ret = {}
174 for e in pairs(index) do
175 if e.label=='Enum'
176 and not string.match(e.xarg.fullname, '%b<>')
177 and e.xarg.access=='public' then
178 ret[e] = true
181 return ret
184 local fill_enums = function(index)
185 for e in pairs(index) do
186 local values = {}
187 for _, v in ipairs(e) do
188 if v.label=='Enumerator' then
189 table.insert(values, v)
192 e.values = values
194 return index
197 local copy_classes = function(index)
198 local ret = {}
199 for e in pairs(index) do
200 if e.label=='Class'
201 and e.xarg.access~='private'
202 and not e.xarg.fullname:match'%b<>' then
203 --if e.xarg.fullname=='QMetaObject' then debug'been here' end
204 ret[e] = true
207 return ret
210 local get_qobjects = function(index)
211 local classes = {}
212 for c in pairs(index) do
213 classes[c.xarg.fullname] = c
215 local is_qobject
216 is_qobject = function(c)
217 if c==nil then return false end
218 if c.qobject then return true end
219 if c.xarg.fullname=='QObject' then
220 c.qobject = true
221 return true
223 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
224 local base = classes[b]
225 if is_qobject(base) then
226 --debug(c.xarg.fullname, "is a QObject")
227 c.qobject = true
228 return true
231 return false
233 for c in pairs(index) do
234 local qobj = is_qobject(c)
236 return index
239 local fill_virtuals = function(index)
240 local classes = {}
241 for c in pairs(index) do
242 classes[c.xarg.fullname] = c
244 local get_virtuals
245 get_virtuals = function(c)
246 local ret = {}
247 for _, f in ipairs(c) do
248 if f.label=='Function' and f.xarg.virtual=='1' then
249 local n = string.match(f.xarg.name, '~') or f.xarg.name
250 if n~='~' and n~='metaObject' then ret[n] = f end
253 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
254 local base = classes[b]
255 if type(base)=='table' then
256 local bv = get_virtuals(base)
257 for n, f in pairs(bv) do
258 if not ret[n] then ret[n] = f end
262 for _, f in ipairs(c) do
263 if f.label=='Function'
264 and f.xarg.access~='private'
265 and (ret[string.match(f.xarg.name, '~') or f.xarg.name]) then
266 f.xarg.virtual = '1'
267 local n = string.match(f.xarg.name, '~')or f.xarg.name
268 ret[n] = f
271 return ret
273 for c in pairs(index) do
274 c.virtuals = get_virtuals(c)
275 for _, f in pairs(c.virtuals) do
276 if f.xarg.abstract=='1' then c.abstract=true break end
279 return index
282 local distinguish_methods = function(index)
283 for c in pairs(index) do
284 local construct, destruct, normal = {}, nil, {}
285 local n = c.xarg.name
286 local copy = nil
287 for _, f in ipairs(c) do
288 if n==f.xarg.name then
289 table.insert(construct, f)
290 elseif f.xarg.name:match'~' then
291 destruct = f
292 else
293 if (not string.match(f.xarg.name, '^operator%W'))
294 and (not f.xarg.member_template_parameters) then
295 table.insert(normal, f)
299 c.constructors = construct
300 c.destructor = destruct
301 c.methods = normal
303 return index
306 local fill_public_destr = function(index)
307 local classes = {}
308 for c in pairs(index) do
309 classes[c.xarg.fullname] = c
311 local destr_is_public
312 destr_is_public = function(c)
313 if c.destructor then
314 return c.destructor.xarg.access=='public'
315 else
316 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
317 local base = classes[b]
318 if base and not destr_is_public(base) then
319 return false
322 return true
325 for c in pairs(index) do
326 c.public_destr = destr_is_public(c)
328 return index
331 local fill_copy_constructor = function(index)
332 local classes = {}
333 for c in pairs(index) do
334 classes[c.xarg.name] = c
336 for c in pairs(index) do
337 local copy = nil
338 for _, f in ipairs(c.constructors) do
339 if #(f.arguments)==1
340 and f.arguments[1].xarg.type_name==c.xarg.fullname..' const&' then
341 copy = f
342 break
345 c.copy_constructor = copy
347 local copy_constr_is_public
348 copy_constr_is_public = function(c)
349 if c.copy_constructor then
350 return (c.copy_constructor.xarg.access=='public')
351 or (c.copy_constructor.xarg.access=='protected')
352 else
353 local ret = nil
354 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
355 local base = classes[b]
356 if base and not copy_constr_is_public(base) then
357 return false
360 return true
363 for c in pairs(index) do
364 c.public_constr = copy_constr_is_public(c)
366 return index
369 local fill_typesystem_with_enums = function(enums, types)
370 local etype = function(en)
371 return {
372 push = function(n)
373 return 'lqtL_pushenum(L, '..n..', "'..en..'")', 1
374 end,
375 get = function(n)
376 return 'static_cast<'..en..'>'
377 ..'(lqtL_toenum(L, '..n..', "'..en..'"))', 1
378 end,
379 test = function(n)
380 return 'lqtL_isenum(L, '..n..', "'..en..'")', 1
381 end,
384 local ret = {}
385 for e in pairs(enums) do
386 if types[e.xarg.fullname]==nil then
387 ret[e] = true
388 types[e.xarg.fullname] = etype(e.xarg.fullname)
389 else
390 --io.stderr:write(e.xarg.fullname, ': already present\n')
393 return ret
396 local put_class_in_filesystem = lqt.classes.insert
397 local fill_typesystem_with_classes = function(classes, types)
398 local ret = {}
399 for c in pairs(classes) do
400 ret[c] = put_class_in_filesystem(c.xarg.fullname, types) --, true)
402 return ret
405 local argument_name = function(tn, an)
406 local ret
407 if string.match(tn, '%(%*%)') then
408 ret = string.gsub(tn, '%(%*%)', '(*'..an..')', 1)
409 elseif string.match(tn, '%[.*%]') then
410 ret = string.gsub(tn, '%s*(%[.*%])', ' '..an..'%1')
411 else
412 ret = tn .. ' ' .. an
414 return ret
417 local fill_wrapper_code = function(f, types)
418 if f.wrapper_code then return f end
419 local stackn, argn = 1, 1
420 local wrap, line = ' int oldtop = lua_gettop(L);\n', ''
421 if f.xarg.abstract then return nil end
422 if f.xarg.member_of_class and f.xarg.static~='1' then
423 if not types[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
424 local sget, sn = types[f.xarg.member_of_class..'*'].get(stackn)
425 wrap = wrap .. ' ' .. f.xarg.member_of_class .. '* self = ' .. sget .. ';\n'
426 stackn = stackn + sn
427 wrap = wrap .. [[
428 if (NULL==self) {
429 lua_pushstring(L, "this pointer is NULL");
430 lua_error(L);
433 --print(sget, sn)
434 line = 'self->'..f.xarg.fullname..'('
435 else
436 line = f.xarg.fullname..'('
438 for i, a in ipairs(f.arguments) do
439 if not types[a.xarg.type_name] then return nil end
440 local aget, an, arg_as = types[a.xarg.type_name].get(stackn)
441 wrap = wrap .. ' ' .. argument_name(arg_as or a.xarg.type_name, 'arg'..argn) .. ' = '
442 if a.xarg.default=='1' and an>0 then
443 wrap = wrap .. 'lua_isnoneornil(L, '..stackn..')'
444 for j = stackn+1,stackn+an-1 do
445 wrap = wrap .. ' && lua_isnoneornil(L, '..j..')'
447 local dv = a.xarg.defaultvalue
448 wrap = wrap .. ' ? static_cast< ' .. a.xarg.type_name .. ' >(' .. dv .. ') : '
450 wrap = wrap .. aget .. ';\n'
451 line = line .. (argn==1 and 'arg' or ', arg') .. argn
452 stackn = stackn + an
453 argn = argn + 1
455 line = line .. ')'
456 -- FIXME: hack follows for constructors
457 if f.calling_line then line = f.calling_line end
458 if f.return_type then line = f.return_type .. ' ret = ' .. line end
459 wrap = wrap .. ' ' .. line .. ';\n lua_settop(L, oldtop);\n' -- lua_pop(L, '..stackn..');\n'
460 if f.return_type then
461 if not types[f.return_type] then return nil end
462 local rput, rn = types[f.return_type].push'ret'
463 wrap = wrap .. ' luaL_checkstack(L, '..rn..', "cannot grow stack for return value");\n'
464 wrap = wrap .. ' '..rput..';\n return '..rn..';\n'
465 else
466 wrap = wrap .. ' return 0;\n'
468 f.wrapper_code = wrap
469 return f
472 local fill_test_code = function(f, types)
473 local stackn = 1
474 local test = ''
475 if f.xarg.member_of_class and f.xarg.static~='1' then
476 if not types[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
477 local stest, sn = types[f.xarg.member_of_class..'*'].test(stackn)
478 test = test .. ' && ' .. stest
479 stackn = stackn + sn
481 for i, a in ipairs(f.arguments) do
482 if not types[a.xarg.type_name] then return nil end -- print(a.xarg.type_name) return nil end
483 local atest, an = types[a.xarg.type_name].test(stackn)
484 if a.xarg.default=='1' and an>0 then
485 test = test .. ' && (lqtL_missarg(L, ' .. stackn .. ', ' .. an .. ') || '
486 test = test .. atest .. ')'
487 else
488 test = test .. ' && ' .. atest
490 stackn = stackn + an
492 -- can't make use of default values if I fix number of args
493 test = '(lua_gettop(L)<' .. stackn .. ')' .. test
494 f.test_code = test
495 return f
498 local fill_wrappers = function(functions, types)
499 local ret = {}
500 for f in pairs(functions) do
501 f = fill_wrapper_code(f, types)
502 if f then
503 f = assert(fill_test_code(f, types), f.xarg.fullname) -- MUST pass
504 ret[f] = true
505 --local out = 'extern "C" LQT_EXPORT int lqt_bind'..f.xarg.id..' (lua_State *L) {\n'
506 --.. f.wrapper_code .. '}\n'
507 --print(out)
510 return ret
513 local make_pushlines = function(args, types)
514 local pushlines, stack = '', 0
515 for i, a in ipairs(args) do
516 if not types[a.xarg.type_name] then return nil end
517 local apush, an = types[a.xarg.type_name].push('arg'..i)
518 pushlines = pushlines .. ' ' .. apush .. ';\n'
519 stack = stack + an
521 return pushlines, stack
524 local virtual_overload = function(v, types)
525 local ret = ''
526 if v.virtual_overload then return v end
527 -- make return type
528 if v.return_type and not types[v.return_type] then return nil end
529 local rget, rn = '', 0
530 if v.return_type then rget, rn, ret_as = types[v.return_type].get'oldtop+1' end
531 local retget = (v.return_type and argument_name(ret_as or v.return_type, 'ret')
532 .. ' = ' .. rget .. ';' or '') .. 'lua_settop(L, oldtop);return'
533 .. (v.return_type and ' ret' or '')
534 -- make argument push
535 local pushlines, stack = make_pushlines(v.arguments, types)
536 if not pushlines then return nil end
537 -- make lua call
538 local luacall = 'lua_pcall(L, '..(stack+1)..', '..rn..', 0)'
539 -- make prototype and fallback
540 local proto = (v.return_type or 'void')..' ;;'..v.xarg.name..' ('
541 local fallback = ''
542 for i, a in ipairs(v.arguments) do
543 proto = proto .. (i>1 and ', ' or '')
544 .. argument_name(a.xarg.type_name, 'arg'..i)
545 fallback = fallback .. (i>1 and ', arg' or 'arg') .. i
547 proto = proto .. ')' .. (v.xarg.constant=='1' and ' const' or '')
548 fallback = (v.return_type and 'return this->' or 'this->')
549 .. v.xarg.fullname .. '(' .. fallback .. ');\n}\n'
550 ret = proto .. [[ {
551 int oldtop = lua_gettop(L);
552 lqtL_pushudata(L, this, "]]..string.gsub(v.xarg.member_of_class, '::', '.')..[[*");
553 lua_getfield(L, -1, "]]..v.xarg.name..[[");
554 if (lua_isfunction(L, -1)) {
555 lua_insert(L, -2);
556 ]] .. pushlines .. [[
557 if (!]]..luacall..[[) {
558 ]]..retget..[[;
561 lua_settop(L, oldtop);
562 ]] .. fallback
563 v.virtual_overload = ret
564 v.virtual_proto = string.gsub(proto, ';;', '', 1)
565 return v
568 local fill_shell_class = function(c, types)
569 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
570 local shell = 'class LQT_EXPORT ' .. shellname .. ' : public ' .. c.xarg.fullname .. ' {\npublic:\n'
571 shell = shell .. ' lua_State *L;\n'
572 for _, constr in ipairs(c.constructors) do
573 if constr.xarg.access~='private' then
574 local cline = ' '..shellname..' (lua_State *l'
575 local argline = ''
576 for i, a in ipairs(constr.arguments) do
577 cline = cline .. ', ' .. argument_name(a.xarg.type_name, 'arg'..i)
578 argline = argline .. (i>1 and ', arg' or 'arg') .. i
580 cline = cline .. ') : ' .. c.xarg.fullname
581 .. '(' .. argline .. '), L(l) '
582 .. '{ lqtL_register(L, this); }\n'
583 shell = shell .. cline
586 if c.copy_constructor==nil and c.public_constr then
587 local cline = ' '..shellname..' (lua_State *l, '..c.xarg.fullname..' const& arg1)'
588 cline = cline .. ' : ' .. c.xarg.fullname .. '(arg1), L(l) {}\n'
589 shell = shell .. cline
591 for i, v in pairs(c.virtuals) do
592 if v.xarg.access~='private' then
593 if v.virtual_proto then shell = shell .. ' virtual ' .. v.virtual_proto .. ';\n' end
596 shell = shell .. ' ~'..shellname..'() { lqtL_unregister(L, this); }\n'
597 if c.shell and c.qobject then
598 shell = shell .. ' static QMetaObject staticMetaObject;\n'
599 shell = shell .. ' virtual const QMetaObject *metaObject() const;\n'
600 shell = shell .. ' virtual int qt_metacall(QMetaObject::Call, int, void **);\n'
601 shell = shell .. 'private:\n'
602 shell = shell .. ' Q_DISABLE_COPY('..shellname..');\n'
604 shell = shell .. '};\n'
605 c.shell_class = shell
606 return c
609 local fill_virtual_overloads = function(classes, types)
610 for c in pairs(classes) do
611 for i, v in pairs(c.virtuals) do
612 if v.xarg.access~='private' then
613 local vret = virtual_overload(v, types)
617 return classes
620 local fill_shell_classes = function(classes, types)
621 local ret = {}
622 for c in pairs(classes) do
623 if c.shell then
624 c = fill_shell_class(c, types)
625 if c then ret[c] = true end
627 ret[c] = true
629 return ret
632 local print_shell_classes = function(classes)
633 local fhead = nil
634 for c in pairs(classes) do
635 if fhead then fhead:close() end
636 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
637 fhead = assert(io.open(module_name.._src..module_name..'_head_'..n..'.hpp', 'w'))
638 local print_head = function(...)
639 fhead:write(...)
640 fhead:write'\n'
642 print_head('#ifndef LQT_HEAD_'..n)
643 print_head('#define LQT_HEAD_'..n)
644 print_head(output_includes)
645 --print_head('#include <'..string.match(c.xarg.fullname, '^[^:]+')..'>')
646 print_head''
647 if c.shell then
648 if c then
649 print_head(c.shell_class)
650 else
651 --io.stderr:write(c.fullname, '\n')
654 print_head('#endif // LQT_HEAD_'..n)
656 if fhead then fhead:close() end
657 return classes
660 local print_virtual_overloads = function(classes)
661 for c in pairs(classes) do
662 if c.shell then
663 local vo = ''
664 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
665 for _,v in pairs(c.virtuals) do
666 if v.virtual_overload then
667 vo = vo .. string.gsub(v.virtual_overload, ';;', shellname..'::', 1)
670 c.virtual_overloads = vo
673 return classes
676 local print_wrappers = function(index)
677 for c in pairs(index) do
678 local meta = {}
679 local wrappers = ''
680 for _, f in ipairs(c.methods) do
681 if f.wrapper_code and f.xarg.virtual~='1' then
682 local out = 'static int lqt_bind'..f.xarg.id
683 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
684 if f.xarg.access=='public' then
685 --print_meta(out)
686 wrappers = wrappers .. out .. '\n'
687 meta[f] = f.xarg.name
691 if c.shell then
692 for _, f in ipairs(c.constructors) do
693 if f.wrapper_code then
694 local out = 'static int lqt_bind'..f.xarg.id
695 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
696 if f.xarg.access=='public' then
697 --print_meta(out)
698 wrappers = wrappers .. out .. '\n'
699 meta[f] = 'new'
703 --local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
704 local lua_name = string.gsub(c.xarg.fullname, '::', '.')
705 local out = 'static int lqt_delete'..c.xarg.id..' (lua_State *L) {\n'
706 out = out ..' '..c.xarg.fullname..' *p = static_cast<'
707 ..c.xarg.fullname..'*>(lqtL_toudata(L, 1, "'..lua_name..'*"));\n'
708 out = out .. ' if (p) delete p;\n'
709 out = out .. ' lqtL_eraseudata(L, 1, "'..lua_name..'*");\n return 0;\n}\n'
710 --print_meta(out)
711 wrappers = wrappers .. out .. '\n'
713 c.meta = meta
714 c.wrappers = wrappers
716 return index
719 local print_metatable = function(c)
720 local methods = {}
721 local wrappers = c.wrappers
722 for m, n in pairs(c.meta) do
723 methods[n] = methods[n] or {}
724 table.insert(methods[n], m)
726 for n, l in pairs(methods) do
727 local disp = 'static int lqt_dispatcher_'..n..c.xarg.id..' (lua_State *L) {\n'
728 for _, f in ipairs(l) do
729 disp = disp..' if ('..f.test_code..') return lqt_bind'..f.xarg.id..'(L);\n'
731 disp = disp .. ' lua_settop(L, 0);\n'
732 disp = disp .. ' lua_pushstring(L, "'..c.xarg.fullname..'::'..n..': incorrect or extra arguments");\n'
733 disp = disp .. ' return lua_error(L);\n}\n'
734 --print_meta(disp)
735 wrappers = wrappers .. disp .. '\n'
737 local metatable = 'static luaL_Reg lqt_metatable'..c.xarg.id..'[] = {\n'
738 for n, l in pairs(methods) do
739 metatable = metatable .. ' { "'..n..'", lqt_dispatcher_'..n..c.xarg.id..' },\n'
741 if c.shell then
742 metatable = metatable .. ' { "delete", lqt_delete'..c.xarg.id..' },\n'
744 metatable = metatable .. ' { 0, 0 },\n};\n'
745 --print_meta(metatable)
746 wrappers = wrappers .. metatable .. '\n'
747 local bases = ''
748 for b in string.gmatch(c.xarg.bases_with_attributes or '', '([^;]*);') do
749 if not string.match(b, '^virtual') then
750 b = string.gsub(b, '^[^%s]* ', '')
751 bases = bases .. '{"' .. b .. '*", (char*)(void*)static_cast<'..b..'*>(('..c.xarg.fullname..'*)1)-(char*)1},\n'
754 bases = 'static lqt_Base lqt_base'..c.xarg.id..'[] = { '..bases..'{NULL, 0} };\n'
755 --print_meta(bases)
756 wrappers = wrappers .. bases .. '\n'
757 c.wrappers = wrappers
758 return c
761 local print_metatables = function(classes)
762 for c in pairs(classes) do
763 print_metatable(c)
765 return classes
768 local cpp_files = {}
770 local print_single_class = function(c)
771 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
772 local lua_name = string.gsub(c.xarg.fullname, '::', '.')
773 local cppname = module_name..'_meta_'..n..'.cpp'
774 table.insert(cpp_files, cppname);
775 local fmeta = assert(io.open(module_name.._src..cppname, 'w'))
776 local print_meta = function(...)
777 fmeta:write(...)
778 fmeta:write'\n'
780 print_meta('#include "'..module_name..'_head_'..n..'.hpp'..'"\n\n')
781 print_meta(c.wrappers)
782 if c.virtual_overloads then
783 print_meta(c.virtual_overloads)
785 print_meta('extern "C" LQT_EXPORT int luaopen_'..n..' (lua_State *L) {')
786 print_meta('\tlqtL_createclass(L, "'
787 ..lua_name..'*", lqt_metatable'
788 ..c.xarg.id..', lqt_base'
789 ..c.xarg.id..');')
790 print_meta'\treturn 0;'
791 print_meta'}'
792 print_meta''
793 if c.shell and c.qobject then
794 print_meta([[
795 #include <QDebug>
797 QMetaObject lqt_shell_]]..n..[[::staticMetaObject;
799 const QMetaObject *lqt_shell_]]..n..[[::metaObject() const {
800 //int oldtop = lua_gettop(L);
801 lqtL_pushudata(L, this, "]]..c.xarg.fullname..[[*");
802 lua_getfield(L, -1, LQT_OBJMETASTRING);
803 if (lua_isnil(L, -1)) {
804 lua_pop(L, 2);
805 return &]]..c.xarg.fullname..[[::staticMetaObject;
807 lua_getfield(L, -2, LQT_OBJMETADATA);
808 lqtL_touintarray(L);
809 //qDebug() << "copying qmeta object for slots in ]]..c.xarg.fullname..[[";
810 lqt_shell_]]..n..[[::staticMetaObject.d.superdata = &]]..c.xarg.fullname..[[::staticMetaObject;
811 lqt_shell_]]..n..[[::staticMetaObject.d.stringdata = lua_tostring(L, -2);
812 lqt_shell_]]..n..[[::staticMetaObject.d.data = (uint*)lua_touserdata(L, -1);
813 lqt_shell_]]..n..[[::staticMetaObject.d.extradata = 0; // slot_metaobj->d.extradata;
814 lua_setfield(L, LUA_REGISTRYINDEX, LQT_OBJMETADATA);
815 lua_setfield(L, LUA_REGISTRYINDEX, LQT_OBJMETASTRING);
816 lua_pop(L, 1);
817 //qDebug() << (lua_gettop(L) - oldtop);
818 return &lqt_shell_]]..n..[[::staticMetaObject;
821 int lqt_shell_]]..n..[[::qt_metacall(QMetaObject::Call call, int index, void **args) {
822 //qDebug() << "fake calling!";
823 index = ]]..c.xarg.fullname..[[::qt_metacall(call, index, args);
824 if (index < 0) return index;
825 return lqtL_qt_metacall(L, this, call, "QWidget*", index, args);
829 fmeta:close()
832 local print_merged_build = function()
833 local path = module_name.._src
834 local mergename = module_name..'_merged_build'
835 local merged = assert(io.open(path..mergename..'.cpp', 'w'))
836 for _, p in ipairs(cpp_files) do
837 merged:write('#include "'..p..'"\n')
839 local pro_file = assert(io.open(path..mergename..'.pro', 'w'))
841 local print_pro= function(...)
842 pro_file:write(...)
843 pro_file:write'\n'
845 print_pro('TEMPLATE = lib')
846 print_pro('TARGET = '..module_name)
847 print_pro('INCLUDEPATH += .')
848 print_pro('HEADERS += '..module_name..'_slot.hpp')
849 print_pro('SOURCES += ../common/lqt_common.cpp \\')
850 print_pro(' ../common/lqt_qt.cpp \\')
851 print_pro(' '..module_name..'_enum.cpp \\')
852 print_pro(' '..module_name..'_meta.cpp \\')
853 print_pro(' '..module_name..'_slot.cpp \\')
854 print_pro(' '..mergename..'.cpp')
857 local print_class_list = function(classes)
858 local qobject_present = false
859 local big_picture = {}
860 local type_list_t = {}
861 for c in pairs(classes) do
862 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
863 if n=='QObject' then qobject_present = true end
864 print_single_class(c)
865 table.insert(big_picture, 'luaopen_'..n)
866 table.insert(type_list_t, 'add_class(\''..c.xarg.fullname..'\', types)\n')
869 local type_list_f = assert(io.open(module_name.._src..module_name..'_types.lua', 'w'))
870 type_list_f:write([[
871 #!/usr/bin/lua
872 local types = (...) or {}
873 local add_class = lqt.classes.insert or error('module lqt.classes not loaded')
875 for k, v in ipairs(type_list_t) do
876 type_list_f:write(v)
878 type_list_f:write('return types\n')
879 type_list_f:close()
881 print_merged_build()
882 if fmeta then fmeta:close() end
883 fmeta = assert(io.open(module_name.._src..module_name..'_meta.cpp', 'w'))
884 local print_meta = function(...)
885 fmeta:write(...)
886 fmeta:write'\n'
888 print_meta()
889 print_meta('#include "lqt_common.hpp"')
890 print_meta('#include "'..module_name..'_slot.hpp'..'"\n\n')
891 for _, p in ipairs(big_picture) do
892 print_meta('extern "C" LQT_EXPORT int '..p..' (lua_State *);')
894 print_meta('void lqt_create_enums_'..module_name..' (lua_State *);')
895 print_meta('extern "C" LQT_EXPORT int luaopen_'..module_name..' (lua_State *L) {')
896 for _, p in ipairs(big_picture) do
897 print_meta('\t'..p..'(L);')
899 print_meta('\tlqt_create_enums_'..module_name..'(L);')
900 if qobject_present then
901 print_meta('\tlua_getfield(L, LUA_REGISTRYINDEX, "QObject*");')
902 print_meta('\tlua_pushstring(L, "__addmethod");')
903 print_meta('\tlqtL_pushaddmethod(L);')
904 print_meta('\tlua_rawset(L, -3);')
906 print_meta('\t//lua_pushlightuserdata(L, (void*)&LqtSlotAcceptor::staticMetaObject);')
907 print_meta('\t//lua_setfield(L, LUA_REGISTRYINDEX, LQT_METAOBJECT);')
908 print_meta('\tlua_pushlightuserdata(L, (void*)new LqtSlotAcceptor(L));')
909 print_meta('\tlua_setfield(L, LUA_REGISTRYINDEX, LQT_METACALLER);')
910 print_meta('\treturn 0;\n}')
911 if fmeta then fmeta:close() end
912 return classes
915 local fix_methods_wrappers = function(classes)
916 for c in pairs(classes) do
917 c.shell = (not c.abstract) and c.public_destr
918 for _, constr in ipairs(c.constructors) do
919 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
920 constr.calling_line = '*new '..shellname..'(L'
921 for i=1,#(constr.arguments) do
922 constr.calling_line = constr.calling_line .. ', arg' .. i
924 constr.calling_line = constr.calling_line .. ')'
925 constr.xarg.static = '1'
926 constr.return_type = constr.xarg.type_base..'&'
928 if c.destructor then
929 c.destructor.return_type = nil
932 return classes
935 local print_enum_tables = function(enums)
936 for e in pairs(enums) do
937 local table = 'static lqt_Enum lqt_enum'..e.xarg.id..'[] = {\n'
938 --io.stderr:write(e.xarg.fullname, '\t', #e.values, '\n')
939 for _,v in pairs(e.values) do
940 table = table .. ' { "' .. v.xarg.name
941 .. '", static_cast<int>('..v.xarg.fullname..') },\n'
943 table = table .. ' { 0, 0 }\n'
944 table = table .. '};\n'
945 e.enum_table = table
946 print_enum(table)
948 return enums
950 local print_enum_creator = function(enums, n)
951 local out = 'static lqt_Enumlist lqt_enum_list[] = {\n'
952 for e in pairs(enums) do
953 out = out..' { lqt_enum'..e.xarg.id..', "'..e.xarg.fullname..'" },\n'
955 out = out..' { 0, 0 },\n};\n'
956 out = out .. 'void lqt_create_enums_'..n..' (lua_State *L) {\n'
957 out = out .. ' lqtL_createenumlist(L, lqt_enum_list); return;\n}\n'
958 print_enum(out)
959 return enums
962 local copy_signals = function(functions)
963 local ret = {}
964 for f in pairs(functions) do
965 if f.xarg.signal=='1' then
966 ret[f] = 1
969 return ret
972 local slots_for_signals = function(signals, types)
973 local ret = {}
974 for sig in pairs(signals) do
975 local args, comma = '(', ''
976 for i, a in ipairs(sig.arguments) do
977 args = args .. comma .. a.xarg.type_name .. ' arg'..i
978 comma = ', '
980 args = args .. ')'
981 local pushlines, stack = make_pushlines(sig.arguments, types)
982 if not ret['void __slot '..args] and pushlines then
983 ret['void __slot '..args] = 'void LqtSlotAcceptor::__slot '..args..[[ {
984 //int oldtop = lua_gettop(L);
985 //lua_getfield(L, -1, "__slot]]..string.gsub(args, ' arg.', '')..[[");
986 //if (lua_isnil(L, -1)) {
987 //lua_pop(L, 1);
988 //lua_getfield(L, -1, "__slot");
990 //if (!lua_isfunction(L, -1)) {
991 //lua_settop(L, oldtop);
992 //return;
994 lua_pushvalue(L, -2);
995 ]] .. pushlines .. [[
996 if (lua_pcall(L, ]]..stack..[[+1, 0, 0)) {
997 //lua_error(L);
998 qDebug() << lua_tostring(L, -1);
999 lua_pop(L, 1);
1001 //lua_settop(L, oldtop);
1006 return ret
1009 local print_slots = function(s)
1010 print_slot_h'class LqtSlotAcceptor : public QObject {'
1011 print_slot_h' Q_OBJECT'
1012 print_slot_h' lua_State *L;'
1013 print_slot_h' public:'
1014 print_slot_h' LqtSlotAcceptor(lua_State *l, QObject *p=NULL) : QObject(p), L(l) { lqtL_register(L, this); }'
1015 print_slot_h' virtual ~LqtSlotAcceptor() { lqtL_unregister(L, this); }'
1016 print_slot_h' public slots:'
1017 for p, b in pairs(s) do
1018 print_slot_h(' '..p..';')
1020 print_slot_h'};\n'
1021 for p, b in pairs(s) do
1022 print_slot_c(b)
1027 --------------------------------------------------------------------------------------
1029 local typesystem = dofile(path..'types.lua')
1031 local ts = {}
1032 for i, ft in ipairs(typefiles) do
1033 ts = assert(loadfile(ft))(ts)
1035 setmetatable(typesystem, {
1036 __newindex = function(t, k, v)
1037 --debug('added type', k)
1038 ts[k] = v
1039 end,
1040 __index = function(t, k)
1041 local ret = ts[k]
1042 --if not ret then debug("unknown type:", tostring(k), ret) end
1043 return ret
1044 end,
1048 fix_arguments(idindex) -- fixes default arguments if they are context-relative
1049 local functions = copy_functions(idindex) -- picks functions and fixes label
1050 local functions = fix_functions(functions) -- fixes name and fullname and fills arguments
1052 local enums = copy_enums(idindex) -- picks enums if public
1053 local enums = fill_enums(enums) -- fills field "values"
1055 local classes = copy_classes(idindex) -- picks classes if not private and not blacklisted
1056 local classes = fill_virtuals(classes) -- does that, destructor ("~") excluded
1057 local classes = distinguish_methods(classes) -- does that
1058 local classes = fill_public_destr(classes) -- does that: checks if destructor is public
1059 local classes = fill_copy_constructor(classes) -- does that: checks if copy contructor is public or protected
1060 local classes = fix_methods_wrappers(classes)
1061 local classes = get_qobjects(classes)
1063 for _, f in ipairs(filterfiles) do
1064 classes, enums = loadfile(f)(classes, enums)
1067 local enums = fill_typesystem_with_enums(enums, typesystem) -- does that
1068 local classes = fill_typesystem_with_classes(classes, typesystem)
1070 local functions = fill_wrappers(functions, typesystem)
1071 local classes = fill_virtual_overloads(classes, typesystem) -- does that
1072 local classes = fill_shell_classes(classes, typesystem) -- does that
1074 local signals = copy_signals(functions)
1075 local slots = slots_for_signals(signals, typesystem)
1078 ------------- BEGIN OUTPUT
1082 print_enum(output_includes)
1083 print_slot_h(output_includes)
1084 print_slot_c('#include "'..module_name..'_slot.hpp'..'"\n\n')
1087 local classes = print_shell_classes(classes) -- does that
1088 local classes = print_virtual_overloads(classes, typesystem) -- does that
1089 local enums = print_enum_tables(enums) -- does that
1090 local enums = print_enum_creator(enums, module_name) -- does that + print enum list
1091 local classes = print_wrappers(classes) -- just compiles metatable list
1092 local classes = print_metatables(classes) -- just collects the wrappers + generates dispatchers
1093 local classes = print_class_list(classes) -- does that + prints everything related to class
1095 local slots = print_slots(slots)
1097 --print_openmodule(module_name) -- does that