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