restored old comment
[lqt.git] / generator / generator.lua
blob5be2128fec660b872002ba1ea35614be81246036
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 -- FIXME: should we really discard virtual functions?
686 -- if the virtual overload in the shell uses rawget
687 -- on the environment then we can leave these in the
688 -- metatable
689 if f.wrapper_code and f.xarg.virtual~='1' then
690 local out = 'static int lqt_bind'..f.xarg.id
691 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
692 if f.xarg.access=='public' then
693 --print_meta(out)
694 wrappers = wrappers .. out .. '\n'
695 meta[f] = f.xarg.name
699 if not c.abstract then
700 for _, f in ipairs(c.constructors) do
701 if f.wrapper_code then
702 local out = 'static int lqt_bind'..f.xarg.id
703 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
704 if f.xarg.access=='public' then
705 --print_meta(out)
706 wrappers = wrappers .. out .. '\n'
707 meta[f] = 'new'
712 --local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
713 local lua_name = string.gsub(c.xarg.fullname, '::', '.')
714 local out = 'static int lqt_delete'..c.xarg.id..' (lua_State *L) {\n'
715 out = out ..' '..c.xarg.fullname..' *p = static_cast<'
716 ..c.xarg.fullname..'*>(lqtL_toudata(L, 1, "'..lua_name..'*"));\n'
717 if c.public_destr then
718 out = out .. ' if (p) delete p;\n'
720 out = out .. ' lqtL_eraseudata(L, 1, "'..lua_name..'*");\n return 0;\n}\n'
721 --print_meta(out)
722 wrappers = wrappers .. out .. '\n'
723 c.meta = meta
724 c.wrappers = wrappers
726 return index
729 local print_metatable = function(c)
730 local methods = {}
731 local wrappers = c.wrappers
732 for m, n in pairs(c.meta) do
733 methods[n] = methods[n] or {}
734 table.insert(methods[n], m)
736 for n, l in pairs(methods) do
737 local disp = 'static int lqt_dispatcher_'..n..c.xarg.id..' (lua_State *L) {\n'
738 for _, f in ipairs(l) do
739 disp = disp..' if ('..f.test_code..') return lqt_bind'..f.xarg.id..'(L);\n'
741 disp = disp .. ' lua_settop(L, 0);\n'
742 disp = disp .. ' lua_pushstring(L, "'..c.xarg.fullname..'::'..n..': incorrect or extra arguments");\n'
743 disp = disp .. ' return lua_error(L);\n}\n'
744 --print_meta(disp)
745 wrappers = wrappers .. disp .. '\n'
747 local metatable = 'static luaL_Reg lqt_metatable'..c.xarg.id..'[] = {\n'
748 for n, l in pairs(methods) do
749 metatable = metatable .. ' { "'..n..'", lqt_dispatcher_'..n..c.xarg.id..' },\n'
751 metatable = metatable .. ' { "delete", lqt_delete'..c.xarg.id..' },\n'
752 metatable = metatable .. ' { 0, 0 },\n};\n'
753 --print_meta(metatable)
754 wrappers = wrappers .. metatable .. '\n'
755 local bases = ''
756 for b in string.gmatch(c.xarg.bases_with_attributes or '', '([^;]*);') do
757 if not string.match(b, '^virtual') then
758 b = string.gsub(b, '^[^%s]* ', '')
759 bases = bases .. ' {"'..string.gsub(b,'::','.')..'*", (char*)(void*)static_cast<'..b..'*>(('..c.xarg.fullname..'*)1)-(char*)1},\n'
762 bases = 'static lqt_Base lqt_base'..c.xarg.id..'[] = {\n'..bases..' {NULL, 0}\n};\n'
763 --print_meta(bases)
764 wrappers = wrappers .. bases .. '\n'
765 c.wrappers = wrappers
766 return c
769 local print_metatables = function(classes)
770 for c in pairs(classes) do
771 print_metatable(c)
773 return classes
776 local cpp_files = {}
778 local print_single_class = function(c)
779 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
780 local lua_name = string.gsub(c.xarg.fullname, '::', '.')
781 local cppname = module_name..'_meta_'..n..'.cpp'
782 table.insert(cpp_files, cppname);
783 local fmeta = assert(io.open(module_name.._src..cppname, 'w'))
784 local print_meta = function(...)
785 fmeta:write(...)
786 fmeta:write'\n'
788 print_meta('#include "'..module_name..'_head_'..n..'.hpp'..'"\n\n')
789 print_meta(c.wrappers)
790 if c.virtual_overloads then
791 print_meta(c.virtual_overloads)
793 print_meta('extern "C" LQT_EXPORT int luaopen_'..n..' (lua_State *L) {')
794 print_meta('\tlqtL_createclass(L, "'
795 ..lua_name..'*", lqt_metatable'
796 ..c.xarg.id..', lqt_base'
797 ..c.xarg.id..');')
798 print_meta'\treturn 0;'
799 print_meta'}'
800 print_meta''
801 if c.shell and c.qobject then
802 print_meta([[
803 #include <QDebug>
805 QMetaObject lqt_shell_]]..n..[[::staticMetaObject;
807 const QMetaObject *lqt_shell_]]..n..[[::metaObject() const {
808 //int oldtop = lua_gettop(L);
809 lqtL_pushudata(L, this, "]]..c.xarg.fullname..[[*");
810 lua_getfield(L, -1, LQT_OBJMETASTRING);
811 if (lua_isnil(L, -1)) {
812 lua_pop(L, 2);
813 return &]]..c.xarg.fullname..[[::staticMetaObject;
815 lua_getfield(L, -2, LQT_OBJMETADATA);
816 lqtL_touintarray(L);
817 //qDebug() << "copying qmeta object for slots in ]]..c.xarg.fullname..[[";
818 lqt_shell_]]..n..[[::staticMetaObject.d.superdata = &]]..c.xarg.fullname..[[::staticMetaObject;
819 lqt_shell_]]..n..[[::staticMetaObject.d.stringdata = lua_tostring(L, -2);
820 lqt_shell_]]..n..[[::staticMetaObject.d.data = (uint*)lua_touserdata(L, -1);
821 lqt_shell_]]..n..[[::staticMetaObject.d.extradata = 0; // slot_metaobj->d.extradata;
822 lua_setfield(L, LUA_REGISTRYINDEX, LQT_OBJMETADATA);
823 lua_setfield(L, LUA_REGISTRYINDEX, LQT_OBJMETASTRING);
824 lua_pop(L, 1);
825 //qDebug() << (lua_gettop(L) - oldtop);
826 return &lqt_shell_]]..n..[[::staticMetaObject;
829 int lqt_shell_]]..n..[[::qt_metacall(QMetaObject::Call call, int index, void **args) {
830 //qDebug() << "fake calling!";
831 index = ]]..c.xarg.fullname..[[::qt_metacall(call, index, args);
832 if (index < 0) return index;
833 return lqtL_qt_metacall(L, this, call, "QWidget*", index, args);
837 fmeta:close()
840 local print_merged_build = function()
841 local path = module_name.._src
842 local mergename = module_name..'_merged_build'
843 local merged = assert(io.open(path..mergename..'.cpp', 'w'))
844 for _, p in ipairs(cpp_files) do
845 merged:write('#include "'..p..'"\n')
847 local pro_file = assert(io.open(path..mergename..'.pro', 'w'))
849 local print_pro= function(...)
850 pro_file:write(...)
851 pro_file:write'\n'
853 print_pro('TEMPLATE = lib')
854 print_pro('TARGET = '..module_name)
855 print_pro('INCLUDEPATH += .')
856 print_pro('HEADERS += '..module_name..'_slot.hpp')
857 print_pro('SOURCES += ../common/lqt_common.cpp \\')
858 print_pro(' ../common/lqt_qt.cpp \\')
859 print_pro(' '..module_name..'_enum.cpp \\')
860 print_pro(' '..module_name..'_meta.cpp \\')
861 print_pro(' '..module_name..'_slot.cpp \\')
862 print_pro(' '..mergename..'.cpp')
865 local print_class_list = function(classes)
866 local qobject_present = false
867 local big_picture = {}
868 local type_list_t = {}
869 for c in pairs(classes) do
870 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
871 if n=='QObject' then qobject_present = true end
872 print_single_class(c)
873 table.insert(big_picture, 'luaopen_'..n)
874 table.insert(type_list_t, 'add_class(\''..c.xarg.fullname..'\', types)\n')
877 local type_list_f = assert(io.open(module_name.._src..module_name..'_types.lua', 'w'))
878 type_list_f:write([[
879 #!/usr/bin/lua
880 local types = (...) or {}
881 local add_class = lqt.classes.insert or error('module lqt.classes not loaded')
883 for k, v in ipairs(type_list_t) do
884 type_list_f:write(v)
886 type_list_f:write('return types\n')
887 type_list_f:close()
889 print_merged_build()
890 if fmeta then fmeta:close() end
891 fmeta = assert(io.open(module_name.._src..module_name..'_meta.cpp', 'w'))
892 local print_meta = function(...)
893 fmeta:write(...)
894 fmeta:write'\n'
896 print_meta()
897 print_meta('#include "lqt_common.hpp"')
898 print_meta('#include "'..module_name..'_slot.hpp'..'"\n\n')
899 for _, p in ipairs(big_picture) do
900 print_meta('extern "C" LQT_EXPORT int '..p..' (lua_State *);')
902 print_meta('void lqt_create_enums_'..module_name..' (lua_State *);')
903 print_meta('extern "C" LQT_EXPORT int luaopen_'..module_name..' (lua_State *L) {')
904 for _, p in ipairs(big_picture) do
905 print_meta('\t'..p..'(L);')
907 print_meta('\tlqt_create_enums_'..module_name..'(L);')
908 if qobject_present then
909 print_meta('\tlua_getfield(L, LUA_REGISTRYINDEX, "QObject*");')
910 print_meta('\tlua_pushstring(L, "__addmethod");')
911 print_meta('\tlqtL_pushaddmethod(L);')
912 print_meta('\tlua_rawset(L, -3);')
914 print_meta('\t//lua_pushlightuserdata(L, (void*)&LqtSlotAcceptor::staticMetaObject);')
915 print_meta('\t//lua_setfield(L, LUA_REGISTRYINDEX, LQT_METAOBJECT);')
916 print_meta('\tlua_pushlightuserdata(L, (void*)new LqtSlotAcceptor(L));')
917 print_meta('\tlua_setfield(L, LUA_REGISTRYINDEX, LQT_METACALLER);')
918 print_meta('\treturn 0;\n}')
919 if fmeta then fmeta:close() end
920 return classes
923 local fix_methods_wrappers = function(classes)
924 for c in pairs(classes) do
925 c.shell = (not c.abstract) and c.public_destr
926 for _, constr in ipairs(c.constructors) do
927 if c.shell then
928 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
929 constr.calling_line = '*new '..shellname..'(L'
930 if #(constr.arguments)>0 then constr.calling_line = constr.calling_line .. ', ' end
931 else
932 local shellname = c.xarg.fullname
933 constr.calling_line = '*new '..shellname..'('
935 for i=1,#(constr.arguments) do
936 constr.calling_line = constr.calling_line .. (i==1 and '' or ', ') .. 'arg' .. i
938 constr.calling_line = constr.calling_line .. ')'
939 constr.xarg.static = '1'
940 constr.return_type = constr.xarg.type_base..'&'
942 if c.destructor then
943 c.destructor.return_type = nil
946 return classes
949 local print_enum_tables = function(enums)
950 for e in pairs(enums) do
951 local table = 'static lqt_Enum lqt_enum'..e.xarg.id..'[] = {\n'
952 --io.stderr:write(e.xarg.fullname, '\t', #e.values, '\n')
953 for _,v in pairs(e.values) do
954 table = table .. ' { "' .. v.xarg.name
955 .. '", static_cast<int>('..v.xarg.fullname..') },\n'
957 table = table .. ' { 0, 0 }\n'
958 table = table .. '};\n'
959 e.enum_table = table
960 print_enum(table)
962 return enums
964 local print_enum_creator = function(enums, n)
965 local out = 'static lqt_Enumlist lqt_enum_list[] = {\n'
966 for e in pairs(enums) do
967 out = out..' { lqt_enum'..e.xarg.id..', "'..e.xarg.fullname..'" },\n'
969 out = out..' { 0, 0 },\n};\n'
970 out = out .. 'void lqt_create_enums_'..n..' (lua_State *L) {\n'
971 out = out .. ' lqtL_createenumlist(L, lqt_enum_list); return;\n}\n'
972 print_enum(out)
973 return enums
976 local copy_signals = function(functions)
977 local ret = {}
978 for f in pairs(functions) do
979 if f.xarg.signal=='1' then
980 ret[f] = 1
983 return ret
986 local slots_for_signals = function(signals, types)
987 local ret = {}
988 for sig in pairs(signals) do
989 local args, comma = '(', ''
990 for i, a in ipairs(sig.arguments) do
991 args = args .. comma .. a.xarg.type_name .. ' arg'..i
992 comma = ', '
994 args = args .. ')'
995 local pushlines, stack = make_pushlines(sig.arguments, types)
996 if not ret['void __slot '..args] and pushlines then
997 ret['void __slot '..args] = 'void LqtSlotAcceptor::__slot '..args..[[ {
998 //int oldtop = lua_gettop(L);
999 //lua_getfield(L, -1, "__slot]]..string.gsub(args, ' arg.', '')..[[");
1000 //if (lua_isnil(L, -1)) {
1001 //lua_pop(L, 1);
1002 //lua_getfield(L, -1, "__slot");
1004 //if (!lua_isfunction(L, -1)) {
1005 //lua_settop(L, oldtop);
1006 //return;
1008 lua_pushvalue(L, -2);
1009 ]] .. pushlines .. [[
1010 if (lua_pcall(L, ]]..stack..[[+1, 0, 0)) {
1011 //lua_error(L);
1012 qDebug() << lua_tostring(L, -1);
1013 lua_pop(L, 1);
1015 //lua_settop(L, oldtop);
1020 return ret
1023 local print_slots = function(s)
1024 print_slot_h'class LqtSlotAcceptor : public QObject {'
1025 print_slot_h' Q_OBJECT'
1026 print_slot_h' lua_State *L;'
1027 print_slot_h' public:'
1028 print_slot_h' LqtSlotAcceptor(lua_State *l, QObject *p=NULL) : QObject(p), L(l) { lqtL_register(L, this); }'
1029 print_slot_h' virtual ~LqtSlotAcceptor() { lqtL_unregister(L, this); }'
1030 print_slot_h' public slots:'
1031 for p, b in pairs(s) do
1032 print_slot_h(' '..p..';')
1034 print_slot_h'};\n'
1035 for p, b in pairs(s) do
1036 print_slot_c(b)
1041 --------------------------------------------------------------------------------------
1043 local typesystem = dofile(path..'types.lua')
1045 local ts = {}
1046 for i, ft in ipairs(typefiles) do
1047 ts = assert(loadfile(ft))(ts)
1049 setmetatable(typesystem, {
1050 __newindex = function(t, k, v)
1051 --debug('added type', k)
1052 ts[k] = v
1053 end,
1054 __index = function(t, k)
1055 local ret = ts[k]
1056 --if not ret then debug("unknown type:", tostring(k), ret) end
1057 return ret
1058 end,
1062 fix_arguments(idindex) -- fixes default arguments if they are context-relative
1063 local functions = copy_functions(idindex) -- picks functions and fixes label
1064 local functions = fix_functions(functions) -- fixes name and fullname and fills arguments
1066 local enums = copy_enums(idindex) -- picks enums if public
1067 local enums = fill_enums(enums) -- fills field "values"
1069 local classes = copy_classes(idindex) -- picks classes if not private and not blacklisted
1070 local classes = fill_virtuals(classes) -- does that, destructor ("~") excluded
1071 local classes = distinguish_methods(classes) -- does that
1072 local classes = fill_public_destr(classes) -- does that: checks if destructor is public
1073 local classes = fill_copy_constructor(classes) -- does that: checks if copy contructor is public or protected
1074 local classes = fix_methods_wrappers(classes)
1075 local classes = get_qobjects(classes)
1077 for _, f in ipairs(filterfiles) do
1078 classes, enums = loadfile(f)(classes, enums)
1081 local enums = fill_typesystem_with_enums(enums, typesystem) -- does that
1082 local classes = fill_typesystem_with_classes(classes, typesystem)
1084 local functions = fill_wrappers(functions, typesystem)
1085 local classes = fill_virtual_overloads(classes, typesystem) -- does that
1086 local classes = fill_shell_classes(classes, typesystem) -- does that
1088 local signals = copy_signals(functions)
1089 local slots = slots_for_signals(signals, typesystem)
1092 ------------- BEGIN OUTPUT
1096 print_enum(output_includes)
1097 print_slot_h(output_includes)
1098 print_slot_c('#include "'..module_name..'_slot.hpp'..'"\n\n')
1101 local classes = print_shell_classes(classes) -- does that
1102 local classes = print_virtual_overloads(classes, typesystem) -- does that
1103 local enums = print_enum_tables(enums) -- does that
1104 local enums = print_enum_creator(enums, module_name) -- does that + print enum list
1105 local classes = print_wrappers(classes) -- just compiles metatable list
1106 local classes = print_metatables(classes) -- just collects the wrappers + generates dispatchers
1107 local classes = print_class_list(classes) -- does that + prints everything related to class
1109 local slots = print_slots(slots)
1111 --print_openmodule(module_name) -- does that