added some tests to noqt
[lqt.git] / generator / generator.lua
blobff14dee7fdddd2e9703165ccf2e9dc41244c8f3f
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*$'))
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 copy_classes = function(index)
202 local ret = {}
203 for e in pairs(index) do
204 if e.label=='Class'
205 and e.xarg.access~='private'
206 and not e.xarg.fullname:match'%b<>' then
207 --if e.xarg.fullname=='QMetaObject' then debug'been here' end
208 ret[e] = true
211 return ret
214 local get_qobjects = function(index)
215 local classes = {}
216 for c in pairs(index) do
217 classes[c.xarg.fullname] = c
219 local is_qobject
220 is_qobject = function(c)
221 if c==nil then return false end
222 if c.qobject then return true end
223 if c.xarg.fullname=='QObject' then
224 c.qobject = true
225 return true
227 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
228 local base = classes[b]
229 if is_qobject(base) then
230 --debug(c.xarg.fullname, "is a QObject")
231 c.qobject = true
232 return true
235 return false
237 for c in pairs(index) do
238 local qobj = is_qobject(c)
240 return index
243 local fill_virtuals = function(index)
244 local classes = {}
245 for c in pairs(index) do
246 classes[c.xarg.fullname] = c
248 local get_virtuals
249 get_virtuals = function(c)
250 local ret = {}
251 for _, f in ipairs(c) do
252 if f.label=='Function' and f.xarg.virtual=='1' then
253 local n = string.match(f.xarg.name, '~') or f.xarg.name
254 if n~='~' and n~='metaObject' then ret[n] = f end
257 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
258 local base = classes[b]
259 if type(base)=='table' then
260 local bv = get_virtuals(base)
261 for n, f in pairs(bv) do
262 if not ret[n] then ret[n] = f end
266 for _, f in ipairs(c) do
267 if f.label=='Function'
268 and f.xarg.access~='private'
269 and (ret[string.match(f.xarg.name, '~') or f.xarg.name]) then
270 f.xarg.virtual = '1'
271 local n = string.match(f.xarg.name, '~')or f.xarg.name
272 ret[n] = f
275 return ret
277 for c in pairs(index) do
278 c.virtuals = get_virtuals(c)
279 for _, f in pairs(c.virtuals) do
280 if f.xarg.abstract=='1' then c.abstract=true break end
283 return index
286 local distinguish_methods = function(index)
287 for c in pairs(index) do
288 local construct, destruct, normal = {}, nil, {}
289 local n = c.xarg.name
290 local copy = nil
291 for _, f in ipairs(c) do
292 if n==f.xarg.name then
293 table.insert(construct, f)
294 elseif f.xarg.name:match'~' then
295 destruct = f
296 else
297 if (not string.match(f.xarg.name, '^operator%W'))
298 and (not f.xarg.member_template_parameters) then
299 table.insert(normal, f)
303 c.constructors = construct
304 c.destructor = destruct
305 c.methods = normal
307 return index
310 local fill_public_destr = function(index)
311 local classes = {}
312 for c in pairs(index) do
313 classes[c.xarg.fullname] = c
315 local destr_is_public
316 destr_is_public = function(c)
317 if c.destructor then
318 return c.destructor.xarg.access=='public'
319 else
320 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
321 local base = classes[b]
322 if base and not destr_is_public(base) then
323 return false
326 return true
329 for c in pairs(index) do
330 c.public_destr = destr_is_public(c)
332 return index
335 local fill_copy_constructor = function(index)
336 local classes = {}
337 for c in pairs(index) do
338 classes[c.xarg.name] = c
340 for c in pairs(index) do
341 local copy = nil
342 for _, f in ipairs(c.constructors) do
343 if #(f.arguments)==1
344 and f.arguments[1].xarg.type_name==c.xarg.fullname..' const&' then
345 copy = f
346 break
349 c.copy_constructor = copy
351 local copy_constr_is_public
352 copy_constr_is_public = function(c)
353 if c.copy_constructor then
354 return (c.copy_constructor.xarg.access=='public')
355 or (c.copy_constructor.xarg.access=='protected')
356 else
357 local ret = nil
358 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
359 local base = classes[b]
360 if base and not copy_constr_is_public(base) then
361 return false
364 return true
367 for c in pairs(index) do
368 c.public_constr = copy_constr_is_public(c)
370 return index
373 local fill_typesystem_with_enums = function(enums, types)
374 local etype = function(en)
375 return {
376 push = function(n)
377 return 'lqtL_pushenum(L, '..n..', "'..en..'")', 1
378 end,
379 get = function(n)
380 return 'static_cast<'..en..'>'
381 ..'(lqtL_toenum(L, '..n..', "'..en..'"))', 1
382 end,
383 test = function(n)
384 return 'lqtL_isenum(L, '..n..', "'..en..'")', 1
385 end,
388 local ret = {}
389 for e in pairs(enums) do
390 if types[e.xarg.fullname]==nil then
391 ret[e] = true
392 types[e.xarg.fullname] = etype(e.xarg.fullname)
393 else
394 --io.stderr:write(e.xarg.fullname, ': already present\n')
397 return ret
400 local put_class_in_filesystem = lqt.classes.insert
401 local fill_typesystem_with_classes = function(classes, types)
402 local ret = {}
403 for c in pairs(classes) do
404 ret[c] = put_class_in_filesystem(c.xarg.fullname, types) --, true)
406 return ret
409 local argument_name = function(tn, an)
410 local ret
411 if string.match(tn, '%(%*%)') then
412 ret = string.gsub(tn, '%(%*%)', '(*'..an..')', 1)
413 elseif string.match(tn, '%[.*%]') then
414 ret = string.gsub(tn, '%s*(%[.*%])', ' '..an..'%1')
415 else
416 ret = tn .. ' ' .. an
418 return ret
421 local fill_wrapper_code = function(f, types)
422 if f.wrapper_code then return f end
423 local stackn, argn = 1, 1
424 local wrap, line = ' int oldtop = lua_gettop(L);\n', ''
425 if f.xarg.abstract then return nil end
426 if f.xarg.member_of_class and f.xarg.static~='1' then
427 if not types[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
428 local sget, sn = types[f.xarg.member_of_class..'*'].get(stackn)
429 wrap = wrap .. ' ' .. f.xarg.member_of_class .. '* self = ' .. sget .. ';\n'
430 stackn = stackn + sn
431 wrap = wrap .. [[
432 if (NULL==self) {
433 lua_pushstring(L, "this pointer is NULL");
434 lua_error(L);
437 --print(sget, sn)
438 line = 'self->'..f.xarg.fullname..'('
439 else
440 line = f.xarg.fullname..'('
442 for i, a in ipairs(f.arguments) do
443 if not types[a.xarg.type_name] then return nil end
444 local aget, an, arg_as = types[a.xarg.type_name].get(stackn)
445 wrap = wrap .. ' ' .. argument_name(arg_as or a.xarg.type_name, 'arg'..argn) .. ' = '
446 if a.xarg.default=='1' and an>0 then
447 wrap = wrap .. 'lua_isnoneornil(L, '..stackn..')'
448 for j = stackn+1,stackn+an-1 do
449 wrap = wrap .. ' && lua_isnoneornil(L, '..j..')'
451 local dv = a.xarg.defaultvalue
452 wrap = wrap .. ' ? static_cast< ' .. a.xarg.type_name .. ' >(' .. dv .. ') : '
454 wrap = wrap .. aget .. ';\n'
455 line = line .. (argn==1 and 'arg' or ', arg') .. argn
456 stackn = stackn + an
457 argn = argn + 1
459 line = line .. ')'
460 -- FIXME: hack follows for constructors
461 if f.calling_line then line = f.calling_line end
462 if f.return_type then line = f.return_type .. ' ret = ' .. line end
463 wrap = wrap .. ' ' .. line .. ';\n lua_settop(L, oldtop);\n' -- lua_pop(L, '..stackn..');\n'
464 if f.return_type then
465 if not types[f.return_type] then return nil end
466 local rput, rn = types[f.return_type].push'ret'
467 wrap = wrap .. ' luaL_checkstack(L, '..rn..', "cannot grow stack for return value");\n'
468 wrap = wrap .. ' '..rput..';\n return '..rn..';\n'
469 else
470 wrap = wrap .. ' return 0;\n'
472 f.wrapper_code = wrap
473 return f
476 local fill_test_code = function(f, types)
477 local stackn = 1
478 local test = ''
479 if f.xarg.member_of_class and f.xarg.static~='1' then
480 if not types[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
481 local stest, sn = types[f.xarg.member_of_class..'*'].test(stackn)
482 test = test .. ' && ' .. stest
483 stackn = stackn + sn
485 for i, a in ipairs(f.arguments) do
486 if not types[a.xarg.type_name] then return nil end -- print(a.xarg.type_name) return nil end
487 local atest, an = types[a.xarg.type_name].test(stackn)
488 if a.xarg.default=='1' and an>0 then
489 test = test .. ' && (lqtL_missarg(L, ' .. stackn .. ', ' .. an .. ') || '
490 test = test .. atest .. ')'
491 else
492 test = test .. ' && ' .. atest
494 stackn = stackn + an
496 -- can't make use of default values if I fix number of args
497 test = '(lua_gettop(L)<' .. stackn .. ')' .. test
498 f.test_code = test
499 return f
502 local fill_wrappers = function(functions, types)
503 local ret = {}
504 for f in pairs(functions) do
505 f = fill_wrapper_code(f, types)
506 if f then
507 f = assert(fill_test_code(f, types), f.xarg.fullname) -- MUST pass
508 ret[f] = true
509 --local out = 'extern "C" LQT_EXPORT int lqt_bind'..f.xarg.id..' (lua_State *L) {\n'
510 --.. f.wrapper_code .. '}\n'
511 --print(out)
514 return ret
517 local make_pushlines = function(args, types)
518 local pushlines, stack = '', 0
519 for i, a in ipairs(args) do
520 if not types[a.xarg.type_name] then return nil end
521 local apush, an = types[a.xarg.type_name].push('arg'..i)
522 pushlines = pushlines .. ' ' .. apush .. ';\n'
523 stack = stack + an
525 return pushlines, stack
528 local virtual_overload = function(v, types)
529 local ret = ''
530 if v.virtual_overload then return v end
531 -- make return type
532 if v.return_type and not types[v.return_type] then return nil end
533 local rget, rn = '', 0
534 if v.return_type then rget, rn, ret_as = types[v.return_type].get'oldtop+1' end
535 local retget = (v.return_type and argument_name(ret_as or v.return_type, 'ret')
536 .. ' = ' .. rget .. ';' or '') .. 'lua_settop(L, oldtop);return'
537 .. (v.return_type and ' ret' or '')
538 -- make argument push
539 local pushlines, stack = make_pushlines(v.arguments, types)
540 if not pushlines then return nil end
541 -- make lua call
542 local luacall = 'lua_pcall(L, '..(stack+1)..', '..rn..', 0)'
543 -- make prototype and fallback
544 local proto = (v.return_type or 'void')..' ;;'..v.xarg.name..' ('
545 local fallback = ''
546 for i, a in ipairs(v.arguments) do
547 proto = proto .. (i>1 and ', ' or '')
548 .. argument_name(a.xarg.type_name, 'arg'..i)
549 fallback = fallback .. (i>1 and ', arg' or 'arg') .. i
551 proto = proto .. ')' .. (v.xarg.constant=='1' and ' const' or '')
552 fallback = (v.return_type and 'return this->' or 'this->')
553 .. v.xarg.fullname .. '(' .. fallback .. ');\n}\n'
554 ret = proto .. [[ {
555 int oldtop = lua_gettop(L);
556 lqtL_pushudata(L, this, "]]..string.gsub(v.xarg.member_of_class, '::', '.')..[[*");
557 lua_getfield(L, -1, "]]..v.xarg.name..[[");
558 if (lua_isfunction(L, -1)) {
559 lua_insert(L, -2);
560 ]] .. pushlines .. [[
561 if (!]]..luacall..[[) {
562 ]]..retget..[[;
565 lua_settop(L, oldtop);
566 ]] .. fallback
567 v.virtual_overload = ret
568 v.virtual_proto = string.gsub(proto, ';;', '', 1)
569 return v
572 local fill_shell_class = function(c, types)
573 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
574 local shell = 'class LQT_EXPORT ' .. shellname .. ' : public ' .. c.xarg.fullname .. ' {\npublic:\n'
575 shell = shell .. ' lua_State *L;\n'
576 for _, constr in ipairs(c.constructors) do
577 if constr.xarg.access~='private' then
578 local cline = ' '..shellname..' (lua_State *l'
579 local argline = ''
580 for i, a in ipairs(constr.arguments) do
581 cline = cline .. ', ' .. argument_name(a.xarg.type_name, 'arg'..i)
582 argline = argline .. (i>1 and ', arg' or 'arg') .. i
584 cline = cline .. ') : ' .. c.xarg.fullname
585 .. '(' .. argline .. '), L(l) '
586 .. '{ lqtL_register(L, this); }\n'
587 shell = shell .. cline
590 if c.copy_constructor==nil and c.public_constr then
591 local cline = ' '..shellname..' (lua_State *l, '..c.xarg.fullname..' const& arg1)'
592 cline = cline .. ' : ' .. c.xarg.fullname .. '(arg1), L(l) {}\n'
593 shell = shell .. cline
595 for i, v in pairs(c.virtuals) do
596 if v.xarg.access~='private' then
597 if v.virtual_proto then shell = shell .. ' virtual ' .. v.virtual_proto .. ';\n' end
600 shell = shell .. ' ~'..shellname..'() { lqtL_unregister(L, this); }\n'
601 if c.shell and c.qobject then
602 shell = shell .. ' static QMetaObject staticMetaObject;\n'
603 shell = shell .. ' virtual const QMetaObject *metaObject() const;\n'
604 shell = shell .. ' virtual int qt_metacall(QMetaObject::Call, int, void **);\n'
605 shell = shell .. 'private:\n'
606 shell = shell .. ' Q_DISABLE_COPY('..shellname..');\n'
608 shell = shell .. '};\n'
609 c.shell_class = shell
610 return c
613 local fill_virtual_overloads = function(classes, types)
614 for c in pairs(classes) do
615 for i, v in pairs(c.virtuals) do
616 if v.xarg.access~='private' then
617 local vret = virtual_overload(v, types)
621 return classes
624 local fill_shell_classes = function(classes, types)
625 local ret = {}
626 for c in pairs(classes) do
627 if c.shell then
628 c = fill_shell_class(c, types)
629 if c then ret[c] = true end
631 ret[c] = true
633 return ret
636 local print_shell_classes = function(classes)
637 local fhead = nil
638 for c in pairs(classes) do
639 if fhead then fhead:close() end
640 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
641 fhead = assert(io.open(module_name.._src..module_name..'_head_'..n..'.hpp', 'w'))
642 local print_head = function(...)
643 fhead:write(...)
644 fhead:write'\n'
646 print_head('#ifndef LQT_HEAD_'..n)
647 print_head('#define LQT_HEAD_'..n)
648 print_head(output_includes)
649 --print_head('#include <'..string.match(c.xarg.fullname, '^[^:]+')..'>')
650 print_head''
651 if c.shell then
652 if c then
653 print_head(c.shell_class)
654 else
655 --io.stderr:write(c.fullname, '\n')
658 print_head('#endif // LQT_HEAD_'..n)
660 if fhead then fhead:close() end
661 return classes
664 local print_virtual_overloads = function(classes)
665 for c in pairs(classes) do
666 if c.shell then
667 local vo = ''
668 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
669 for _,v in pairs(c.virtuals) do
670 if v.virtual_overload then
671 vo = vo .. string.gsub(v.virtual_overload, ';;', shellname..'::', 1)
674 c.virtual_overloads = vo
677 return classes
680 local print_wrappers = function(index)
681 for c in pairs(index) do
682 local meta = {}
683 local wrappers = ''
684 for _, f in ipairs(c.methods) do
685 if f.wrapper_code and f.xarg.virtual~='1' then
686 local out = 'static int lqt_bind'..f.xarg.id
687 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
688 if f.xarg.access=='public' then
689 --print_meta(out)
690 wrappers = wrappers .. out .. '\n'
691 meta[f] = f.xarg.name
695 if not c.abstract then
696 for _, f in ipairs(c.constructors) do
697 if f.wrapper_code then
698 local out = 'static int lqt_bind'..f.xarg.id
699 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
700 if f.xarg.access=='public' then
701 --print_meta(out)
702 wrappers = wrappers .. out .. '\n'
703 meta[f] = 'new'
708 --local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
709 local lua_name = string.gsub(c.xarg.fullname, '::', '.')
710 local out = 'static int lqt_delete'..c.xarg.id..' (lua_State *L) {\n'
711 out = out ..' '..c.xarg.fullname..' *p = static_cast<'
712 ..c.xarg.fullname..'*>(lqtL_toudata(L, 1, "'..lua_name..'*"));\n'
713 if c.public_destr then
714 out = out .. ' if (p) delete p;\n'
716 out = out .. ' lqtL_eraseudata(L, 1, "'..lua_name..'*");\n return 0;\n}\n'
717 --print_meta(out)
718 wrappers = wrappers .. out .. '\n'
719 c.meta = meta
720 c.wrappers = wrappers
722 return index
725 local print_metatable = function(c)
726 local methods = {}
727 local wrappers = c.wrappers
728 for m, n in pairs(c.meta) do
729 methods[n] = methods[n] or {}
730 table.insert(methods[n], m)
732 for n, l in pairs(methods) do
733 local disp = 'static int lqt_dispatcher_'..n..c.xarg.id..' (lua_State *L) {\n'
734 for _, f in ipairs(l) do
735 disp = disp..' if ('..f.test_code..') return lqt_bind'..f.xarg.id..'(L);\n'
737 disp = disp .. ' lua_settop(L, 0);\n'
738 disp = disp .. ' lua_pushstring(L, "'..c.xarg.fullname..'::'..n..': incorrect or extra arguments");\n'
739 disp = disp .. ' return lua_error(L);\n}\n'
740 --print_meta(disp)
741 wrappers = wrappers .. disp .. '\n'
743 local metatable = 'static luaL_Reg lqt_metatable'..c.xarg.id..'[] = {\n'
744 for n, l in pairs(methods) do
745 metatable = metatable .. ' { "'..n..'", lqt_dispatcher_'..n..c.xarg.id..' },\n'
747 metatable = metatable .. ' { "delete", lqt_delete'..c.xarg.id..' },\n'
748 metatable = metatable .. ' { 0, 0 },\n};\n'
749 --print_meta(metatable)
750 wrappers = wrappers .. metatable .. '\n'
751 local bases = ''
752 for b in string.gmatch(c.xarg.bases_with_attributes or '', '([^;]*);') do
753 if not string.match(b, '^virtual') then
754 b = string.gsub(b, '^[^%s]* ', '')
755 bases = bases .. ' {"'..string.gsub(b,'::','.')..'*", (char*)(void*)static_cast<'..b..'*>(('..c.xarg.fullname..'*)1)-(char*)1},\n'
758 bases = 'static lqt_Base lqt_base'..c.xarg.id..'[] = {\n'..bases..' {NULL, 0}\n};\n'
759 --print_meta(bases)
760 wrappers = wrappers .. bases .. '\n'
761 c.wrappers = wrappers
762 return c
765 local print_metatables = function(classes)
766 for c in pairs(classes) do
767 print_metatable(c)
769 return classes
772 local cpp_files = {}
774 local print_single_class = function(c)
775 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
776 local lua_name = string.gsub(c.xarg.fullname, '::', '.')
777 local cppname = module_name..'_meta_'..n..'.cpp'
778 table.insert(cpp_files, cppname);
779 local fmeta = assert(io.open(module_name.._src..cppname, 'w'))
780 local print_meta = function(...)
781 fmeta:write(...)
782 fmeta:write'\n'
784 print_meta('#include "'..module_name..'_head_'..n..'.hpp'..'"\n\n')
785 print_meta(c.wrappers)
786 if c.virtual_overloads then
787 print_meta(c.virtual_overloads)
789 print_meta('extern "C" LQT_EXPORT int luaopen_'..n..' (lua_State *L) {')
790 print_meta('\tlqtL_createclass(L, "'
791 ..lua_name..'*", lqt_metatable'
792 ..c.xarg.id..', lqt_base'
793 ..c.xarg.id..');')
794 print_meta'\treturn 0;'
795 print_meta'}'
796 print_meta''
797 if c.shell and c.qobject then
798 print_meta([[
799 #include <QDebug>
801 QMetaObject lqt_shell_]]..n..[[::staticMetaObject;
803 const QMetaObject *lqt_shell_]]..n..[[::metaObject() const {
804 //int oldtop = lua_gettop(L);
805 lqtL_pushudata(L, this, "]]..c.xarg.fullname..[[*");
806 lua_getfield(L, -1, LQT_OBJMETASTRING);
807 if (lua_isnil(L, -1)) {
808 lua_pop(L, 2);
809 return &]]..c.xarg.fullname..[[::staticMetaObject;
811 lua_getfield(L, -2, LQT_OBJMETADATA);
812 lqtL_touintarray(L);
813 //qDebug() << "copying qmeta object for slots in ]]..c.xarg.fullname..[[";
814 lqt_shell_]]..n..[[::staticMetaObject.d.superdata = &]]..c.xarg.fullname..[[::staticMetaObject;
815 lqt_shell_]]..n..[[::staticMetaObject.d.stringdata = lua_tostring(L, -2);
816 lqt_shell_]]..n..[[::staticMetaObject.d.data = (uint*)lua_touserdata(L, -1);
817 lqt_shell_]]..n..[[::staticMetaObject.d.extradata = 0; // slot_metaobj->d.extradata;
818 lua_setfield(L, LUA_REGISTRYINDEX, LQT_OBJMETADATA);
819 lua_setfield(L, LUA_REGISTRYINDEX, LQT_OBJMETASTRING);
820 lua_pop(L, 1);
821 //qDebug() << (lua_gettop(L) - oldtop);
822 return &lqt_shell_]]..n..[[::staticMetaObject;
825 int lqt_shell_]]..n..[[::qt_metacall(QMetaObject::Call call, int index, void **args) {
826 //qDebug() << "fake calling!";
827 index = ]]..c.xarg.fullname..[[::qt_metacall(call, index, args);
828 if (index < 0) return index;
829 return lqtL_qt_metacall(L, this, call, "QWidget*", index, args);
833 fmeta:close()
836 local print_merged_build = function()
837 local path = module_name.._src
838 local mergename = module_name..'_merged_build'
839 local merged = assert(io.open(path..mergename..'.cpp', 'w'))
840 for _, p in ipairs(cpp_files) do
841 merged:write('#include "'..p..'"\n')
843 local pro_file = assert(io.open(path..mergename..'.pro', 'w'))
845 local print_pro= function(...)
846 pro_file:write(...)
847 pro_file:write'\n'
849 print_pro('TEMPLATE = lib')
850 print_pro('TARGET = '..module_name)
851 print_pro('INCLUDEPATH += .')
852 print_pro('HEADERS += '..module_name..'_slot.hpp')
853 print_pro('SOURCES += ../common/lqt_common.cpp \\')
854 print_pro(' ../common/lqt_qt.cpp \\')
855 print_pro(' '..module_name..'_enum.cpp \\')
856 print_pro(' '..module_name..'_meta.cpp \\')
857 print_pro(' '..module_name..'_slot.cpp \\')
858 print_pro(' '..mergename..'.cpp')
861 local print_class_list = function(classes)
862 local qobject_present = false
863 local big_picture = {}
864 local type_list_t = {}
865 for c in pairs(classes) do
866 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
867 if n=='QObject' then qobject_present = true end
868 print_single_class(c)
869 table.insert(big_picture, 'luaopen_'..n)
870 table.insert(type_list_t, 'add_class(\''..c.xarg.fullname..'\', types)\n')
873 local type_list_f = assert(io.open(module_name.._src..module_name..'_types.lua', 'w'))
874 type_list_f:write([[
875 #!/usr/bin/lua
876 local types = (...) or {}
877 local add_class = lqt.classes.insert or error('module lqt.classes not loaded')
879 for k, v in ipairs(type_list_t) do
880 type_list_f:write(v)
882 type_list_f:write('return types\n')
883 type_list_f:close()
885 print_merged_build()
886 if fmeta then fmeta:close() end
887 fmeta = assert(io.open(module_name.._src..module_name..'_meta.cpp', 'w'))
888 local print_meta = function(...)
889 fmeta:write(...)
890 fmeta:write'\n'
892 print_meta()
893 print_meta('#include "lqt_common.hpp"')
894 print_meta('#include "'..module_name..'_slot.hpp'..'"\n\n')
895 for _, p in ipairs(big_picture) do
896 print_meta('extern "C" LQT_EXPORT int '..p..' (lua_State *);')
898 print_meta('void lqt_create_enums_'..module_name..' (lua_State *);')
899 print_meta('extern "C" LQT_EXPORT int luaopen_'..module_name..' (lua_State *L) {')
900 for _, p in ipairs(big_picture) do
901 print_meta('\t'..p..'(L);')
903 print_meta('\tlqt_create_enums_'..module_name..'(L);')
904 if qobject_present then
905 print_meta('\tlua_getfield(L, LUA_REGISTRYINDEX, "QObject*");')
906 print_meta('\tlua_pushstring(L, "__addmethod");')
907 print_meta('\tlqtL_pushaddmethod(L);')
908 print_meta('\tlua_rawset(L, -3);')
910 print_meta('\t//lua_pushlightuserdata(L, (void*)&LqtSlotAcceptor::staticMetaObject);')
911 print_meta('\t//lua_setfield(L, LUA_REGISTRYINDEX, LQT_METAOBJECT);')
912 print_meta('\tlua_pushlightuserdata(L, (void*)new LqtSlotAcceptor(L));')
913 print_meta('\tlua_setfield(L, LUA_REGISTRYINDEX, LQT_METACALLER);')
914 print_meta('\treturn 0;\n}')
915 if fmeta then fmeta:close() end
916 return classes
919 local fix_methods_wrappers = function(classes)
920 for c in pairs(classes) do
921 c.shell = (not c.abstract) and c.public_destr
922 for _, constr in ipairs(c.constructors) do
923 if c.shell then
924 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
925 constr.calling_line = '*new '..shellname..'(L'
926 if #(constr.arguments)>0 then constr.calling_line = constr.calling_line .. ', ' end
927 else
928 local shellname = c.xarg.fullname
929 constr.calling_line = '*new '..shellname..'('
931 for i=1,#(constr.arguments) do
932 constr.calling_line = constr.calling_line .. (i==1 and '' or ', ') .. 'arg' .. i
934 constr.calling_line = constr.calling_line .. ')'
935 constr.xarg.static = '1'
936 constr.return_type = constr.xarg.type_base..'&'
938 if c.destructor then
939 c.destructor.return_type = nil
942 return classes
945 local print_enum_tables = function(enums)
946 for e in pairs(enums) do
947 local table = 'static lqt_Enum lqt_enum'..e.xarg.id..'[] = {\n'
948 --io.stderr:write(e.xarg.fullname, '\t', #e.values, '\n')
949 for _,v in pairs(e.values) do
950 table = table .. ' { "' .. v.xarg.name
951 .. '", static_cast<int>('..v.xarg.fullname..') },\n'
953 table = table .. ' { 0, 0 }\n'
954 table = table .. '};\n'
955 e.enum_table = table
956 print_enum(table)
958 return enums
960 local print_enum_creator = function(enums, n)
961 local out = 'static lqt_Enumlist lqt_enum_list[] = {\n'
962 for e in pairs(enums) do
963 out = out..' { lqt_enum'..e.xarg.id..', "'..e.xarg.fullname..'" },\n'
965 out = out..' { 0, 0 },\n};\n'
966 out = out .. 'void lqt_create_enums_'..n..' (lua_State *L) {\n'
967 out = out .. ' lqtL_createenumlist(L, lqt_enum_list); return;\n}\n'
968 print_enum(out)
969 return enums
972 local copy_signals = function(functions)
973 local ret = {}
974 for f in pairs(functions) do
975 if f.xarg.signal=='1' then
976 ret[f] = 1
979 return ret
982 local slots_for_signals = function(signals, types)
983 local ret = {}
984 for sig in pairs(signals) do
985 local args, comma = '(', ''
986 for i, a in ipairs(sig.arguments) do
987 args = args .. comma .. a.xarg.type_name .. ' arg'..i
988 comma = ', '
990 args = args .. ')'
991 local pushlines, stack = make_pushlines(sig.arguments, types)
992 if not ret['void __slot '..args] and pushlines then
993 ret['void __slot '..args] = 'void LqtSlotAcceptor::__slot '..args..[[ {
994 //int oldtop = lua_gettop(L);
995 //lua_getfield(L, -1, "__slot]]..string.gsub(args, ' arg.', '')..[[");
996 //if (lua_isnil(L, -1)) {
997 //lua_pop(L, 1);
998 //lua_getfield(L, -1, "__slot");
1000 //if (!lua_isfunction(L, -1)) {
1001 //lua_settop(L, oldtop);
1002 //return;
1004 lua_pushvalue(L, -2);
1005 ]] .. pushlines .. [[
1006 if (lua_pcall(L, ]]..stack..[[+1, 0, 0)) {
1007 //lua_error(L);
1008 qDebug() << lua_tostring(L, -1);
1009 lua_pop(L, 1);
1011 //lua_settop(L, oldtop);
1016 return ret
1019 local print_slots = function(s)
1020 print_slot_h'class LqtSlotAcceptor : public QObject {'
1021 print_slot_h' Q_OBJECT'
1022 print_slot_h' lua_State *L;'
1023 print_slot_h' public:'
1024 print_slot_h' LqtSlotAcceptor(lua_State *l, QObject *p=NULL) : QObject(p), L(l) { lqtL_register(L, this); }'
1025 print_slot_h' virtual ~LqtSlotAcceptor() { lqtL_unregister(L, this); }'
1026 print_slot_h' public slots:'
1027 for p, b in pairs(s) do
1028 print_slot_h(' '..p..';')
1030 print_slot_h'};\n'
1031 for p, b in pairs(s) do
1032 print_slot_c(b)
1037 --------------------------------------------------------------------------------------
1039 local typesystem = dofile(path..'types.lua')
1041 local ts = {}
1042 for i, ft in ipairs(typefiles) do
1043 ts = assert(loadfile(ft))(ts)
1045 setmetatable(typesystem, {
1046 __newindex = function(t, k, v)
1047 --debug('added type', k)
1048 ts[k] = v
1049 end,
1050 __index = function(t, k)
1051 local ret = ts[k]
1052 --if not ret then debug("unknown type:", tostring(k), ret) end
1053 return ret
1054 end,
1058 fix_arguments(idindex) -- fixes default arguments if they are context-relative
1059 local functions = copy_functions(idindex) -- picks functions and fixes label
1060 local functions = fix_functions(functions) -- fixes name and fullname and fills arguments
1062 local enums = copy_enums(idindex) -- picks enums if public
1063 local enums = fill_enums(enums) -- fills field "values"
1065 local classes = copy_classes(idindex) -- picks classes if not private and not blacklisted
1066 local classes = fill_virtuals(classes) -- does that, destructor ("~") excluded
1067 local classes = distinguish_methods(classes) -- does that
1068 local classes = fill_public_destr(classes) -- does that: checks if destructor is public
1069 local classes = fill_copy_constructor(classes) -- does that: checks if copy contructor is public or protected
1070 local classes = fix_methods_wrappers(classes)
1071 local classes = get_qobjects(classes)
1073 for _, f in ipairs(filterfiles) do
1074 classes, enums = loadfile(f)(classes, enums)
1077 local enums = fill_typesystem_with_enums(enums, typesystem) -- does that
1078 local classes = fill_typesystem_with_classes(classes, typesystem)
1080 local functions = fill_wrappers(functions, typesystem)
1081 local classes = fill_virtual_overloads(classes, typesystem) -- does that
1082 local classes = fill_shell_classes(classes, typesystem) -- does that
1084 local signals = copy_signals(functions)
1085 local slots = slots_for_signals(signals, typesystem)
1088 ------------- BEGIN OUTPUT
1092 print_enum(output_includes)
1093 print_slot_h(output_includes)
1094 print_slot_c('#include "'..module_name..'_slot.hpp'..'"\n\n')
1097 local classes = print_shell_classes(classes) -- does that
1098 local classes = print_virtual_overloads(classes, typesystem) -- does that
1099 local enums = print_enum_tables(enums) -- does that
1100 local enums = print_enum_creator(enums, module_name) -- does that + print enum list
1101 local classes = print_wrappers(classes) -- just compiles metatable list
1102 local classes = print_metatables(classes) -- just collects the wrappers + generates dispatchers
1103 local classes = print_class_list(classes) -- does that + prints everything related to class
1105 local slots = print_slots(slots)
1107 --print_openmodule(module_name) -- does that