basic operator support
[lqt/mk.git] / generator / generator.lua
blob8df957472fd195f2b23bbd9f24a5a010a0618ce6
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 warn = false
107 local function ignore(name, cause)
108 if warn then
109 io.stderr:write('Ignoring: ', name, '(', cause, ')')
113 local xmlstream, idindex = dofile(path..'xml.lua')(readfile(filename))
115 dofile(path..'classes.lua') -- this should become a require at some point
117 ----------------------------------------------------------------------------------
119 local copy_functions = function(index)
120 local ret = {}
121 for e in pairs(index) do
122 if e.label:match'^Function' then
123 e.label = 'Function'
124 ret[e] = true
127 return ret
131 local fix_arguments = function(all)
132 local fullnames = {}
133 for e in pairs(all or {}) do
134 if e.xarg.fullname then fullnames[e.xarg.fullname] = true end
136 for a in pairs(all) do
137 if a.label=='Argument'
138 and a.xarg.default=='1'
139 and (not string.match(a.xarg.defaultvalue, '^[-+]?%d+%.?%d*[L]?$'))
140 and (not string.match(a.xarg.defaultvalue, '^".*"$'))
141 and a.xarg.defaultvalue~='true'
142 and a.xarg.defaultvalue~='false'
143 and (not string.match(a.xarg.defaultvalue, '^0[xX]%d+$')) then
144 local dv, call = string.match(a.xarg.defaultvalue, '(.-)(%(%))')
145 dv = dv or a.xarg.defaultvalue
146 call = call or ''
147 local context = a.xarg.context
148 while not fullnames[context..'::'..dv] and context~='' do
149 context = string.match(context, '^(.*)::') or ''
151 if fullnames[context..'::'..dv] then
152 a.xarg.defaultvalue = context..'::'..dv..call
153 elseif fullnames[dv] then
154 a.xarg.defaultvalue = dv..call
155 else
156 a.xarg.default = nil
157 a.xarg.defaultvalue = nil
161 return all
164 local operatorTrans = {
165 ['<<'] = 'IN',
166 ['>>'] = 'OUT',
167 ['+='] = 'PEQ',
168 ['-='] = 'MEQ',
169 ['++'] = 'INC',
170 ['--'] = 'DEC',
173 local get_operator = function(name)
174 return name:match('^operator(.+)$')
177 local is_operator = function(name)
178 return name:match('^operator.')
181 local rename_operator = function(name)
182 local trans = operatorTrans[get_operator(name)]
183 if is_operator(name) and trans then
184 return 'op'..trans
186 return name
189 local fix_functions = function(index)
190 for f in pairs(index) do
191 local args = {}
192 for i, a in ipairs(f) do
193 -- avoid bogus 'void' arguments
194 if a.xarg.type_name=='void' and i==1 and f[2]==nil then break end
195 if a.label=='Argument' then
196 table.insert(args, a)
199 f.arguments = args
200 f.return_type = f.xarg.type_name
201 if f.xarg.type_name=='void' then
202 f.return_type = nil
205 return index
208 local copy_enums = function(index)
209 local ret = {}
210 for e in pairs(index) do
211 if e.label=='Enum'
212 and not string.match(e.xarg.fullname, '%b<>')
213 and e.xarg.access=='public' then
214 ret[e] = true
217 return ret
220 local fill_enums = function(index)
221 for e in pairs(index) do
222 local values = {}
223 for _, v in ipairs(e) do
224 if v.label=='Enumerator' then
225 table.insert(values, v)
228 e.values = values
230 return index
233 local class_is_public = function (fullnames, c)
234 repeat
235 if c.xarg.access~='public' then return false end
236 if c.xarg.member_of_class then
237 local p = fullnames[c.xarg.member_of_class]
238 assert(p, 'member_of_class should exist')
239 assert(p.label=='Class', 'member_of_class should be a class')
240 c = fullnames[c.xarg.member_of_class]
241 else
242 break
244 until true
245 return true
248 local copy_classes = function(index)
249 local ret = {}
250 local fullnames = {}
251 for k,v in pairs(index) do
252 if k.label=='Class' then fullnames[k.xarg.fullname] = k end
254 for e in pairs(index) do
255 if e.label=='Class' then
256 if class_is_public(fullnames, e)
257 and not e.xarg.fullname:match'%b<>' then
258 ret[e] = true
259 elseif not e.xarg.fullname:match'%b<>' then
260 ignore(e.xarg.fullname, 'not public')
261 else
262 ignore(e.xarg.fullname, 'template')
266 return ret
269 local get_qobjects = function(index)
270 local classes = {}
271 for c in pairs(index) do
272 classes[c.xarg.fullname] = c
274 local is_qobject
275 is_qobject = function(c)
276 if c==nil then return false end
277 if c.qobject then return true end
278 if c.xarg.fullname=='QObject' then
279 c.qobject = true
280 return true
282 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
283 local base = classes[b]
284 if is_qobject(base) then
285 --debug(c.xarg.fullname, "is a QObject")
286 c.qobject = true
287 return true
290 return false
292 for c in pairs(index) do
293 local qobj = is_qobject(c)
295 return index
298 local fill_virtuals = function(index)
299 local classes = {}
300 for c in pairs(index) do
301 classes[c.xarg.fullname] = c
303 local get_virtuals
304 get_virtuals = function(c)
305 local ret = {}
306 for _, f in ipairs(c) do
307 if f.label=='Function' and f.xarg.virtual=='1' then
308 local n = string.match(f.xarg.name, '~') or f.xarg.name
309 if n~='~' and n~='metaObject' then ret[n] = f end
312 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
313 local base = classes[b]
314 if type(base)=='table' then
315 local bv = get_virtuals(base)
316 for n, f in pairs(bv) do
317 if not ret[n] then ret[n] = f end
321 for _, f in ipairs(c) do
322 if f.label=='Function'
323 and f.xarg.access~='private'
324 and (ret[string.match(f.xarg.name, '~') or f.xarg.name]) then
325 f.xarg.virtual = '1'
326 local n = string.match(f.xarg.name, '~')or f.xarg.name
327 ret[n] = f
330 return ret
332 for c in pairs(index) do
333 c.virtuals = get_virtuals(c)
334 for _, f in pairs(c.virtuals) do
335 if f.xarg.abstract=='1' then c.abstract=true break end
338 return index
341 local should_wrap = function(f)
342 local name = f.xarg.name
343 -- FIXME: operator and friend, causes trouble with QDataStream
344 if f.xarg.friend then return false end
345 -- not an operator - accept
346 if not name:match('^operator') then return true end
347 -- accept supported operators
348 if is_operator(name) and operatorTrans[get_operator(name)] then return true end
349 return false
352 local distinguish_methods = function(index)
353 for c in pairs(index) do
354 local construct, destruct, normal = {}, nil, {}
355 local n = c.xarg.name
356 local copy = nil
357 for _, f in ipairs(c) do
358 if n==f.xarg.name then
359 table.insert(construct, f)
360 elseif f.xarg.name:match'~' then
361 destruct = f
362 else
363 if should_wrap(f)
364 and (not f.xarg.member_template_parameters)
365 and (not f.xarg.friend) then
366 table.insert(normal, f)
367 else
368 ignore(f.xarg.name, 'operator/template/friend')
372 c.constructors = construct
373 c.destructor = destruct
374 c.methods = normal
376 return index
379 local fill_public_destr = function(index)
380 local classes = {}
381 for c in pairs(index) do
382 classes[c.xarg.fullname] = c
384 local destr_is_public
385 destr_is_public = function(c)
386 if c.destructor then
387 return c.destructor.xarg.access=='public'
388 else
389 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
390 local base = classes[b]
391 if base and not destr_is_public(base) then
392 return false
395 return true
398 for c in pairs(index) do
399 c.public_destr = destr_is_public(c)
401 return index
404 local fill_copy_constructor = function(index)
405 local classes = {}
406 for c in pairs(index) do
407 classes[c.xarg.name] = c
409 for c in pairs(index) do
410 local copy = nil
411 for _, f in ipairs(c.constructors) do
412 if #(f.arguments)==1
413 and f.arguments[1].xarg.type_name==c.xarg.fullname..' const&' then
414 copy = f
415 break
418 c.copy_constructor = copy
420 local copy_constr_is_public
421 copy_constr_is_public = function(c)
422 if c.copy_constructor then
423 return (c.copy_constructor.xarg.access=='public')
424 or (c.copy_constructor.xarg.access=='protected')
425 else
426 local ret = nil
427 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
428 local base = classes[b]
429 if base and not copy_constr_is_public(base) then
430 return false
433 return true
436 for c in pairs(index) do
437 c.public_constr = copy_constr_is_public(c)
439 return index
442 local fill_typesystem_with_enums = function(enums, types)
443 local etype = function(en)
444 return {
445 push = function(n)
446 return 'lqtL_pushenum(L, '..n..', "'..string.gsub(en, '::', '.')..'")', 1
447 end,
448 get = function(n)
449 return 'static_cast<'..en..'>'
450 ..'(lqtL_toenum(L, '..n..', "'..string.gsub(en, '::', '.')..'"))', 1
451 end,
452 test = function(n)
453 return 'lqtL_isenum(L, '..n..', "'..string.gsub(en, '::', '.')..'")', 1
454 end,
455 onstack = string.gsub(en, '::', '.')..',',
458 local ret = {}
459 for e in pairs(enums) do
460 if types[e.xarg.fullname]==nil then
461 ret[e] = true
462 types[e.xarg.fullname] = etype(e.xarg.fullname)
463 else
464 --io.stderr:write(e.xarg.fullname, ': already present\n')
467 return ret
470 local put_class_in_filesystem = lqt.classes.insert
471 local fill_typesystem_with_classes = function(classes, types)
472 local ret = {}
473 for c in pairs(classes) do
474 ret[c] = put_class_in_filesystem(c.xarg.fullname, types) --, true)
476 return ret
479 local argument_name = function(tn, an)
480 local ret
481 if string.match(tn, '%(%*%)') then
482 ret = string.gsub(tn, '%(%*%)', '(*'..an..')', 1)
483 elseif string.match(tn, '%[.*%]') then
484 ret = string.gsub(tn, '%s*(%[.*%])', ' '..an..'%1')
485 else
486 ret = tn .. ' ' .. an
488 return ret
491 local fill_wrapper_code = function(f, types)
492 if f.wrapper_code then return f end
493 local stackn, argn = 1, 1
494 local stack_args, defects = '', 0
495 local wrap, line = ' int oldtop = lua_gettop(L);\n', ''
496 if f.xarg.abstract then return nil end
497 if f.xarg.member_of_class and f.xarg.static~='1' then
498 if not types[f.xarg.member_of_class..'*'] then
499 ignore(f.xarg.member_of_class, 'not a member of selected class')
500 return nil
502 stack_args = stack_args .. types[f.xarg.member_of_class..'*'].onstack
503 defects = defects + 7 -- FIXME: arbitrary
504 if f.xarg.constant=='1' then
505 defects = defects + 8 -- FIXME: arbitrary
507 local sget, sn = types[f.xarg.member_of_class..'*'].get(stackn)
508 wrap = wrap .. ' ' .. f.xarg.member_of_class .. '* self = ' .. sget .. ';\n'
509 stackn = stackn + sn
510 wrap = wrap .. [[
511 if (NULL==self) {
512 lua_pushstring(L, "this pointer is NULL");
513 lua_error(L);
516 --print(sget, sn)
517 if is_operator(f.xarg.name) then
518 local op = get_operator(f.xarg.name)
519 line = '*self '..op
520 -- the ++ and -- operators don't line () at the end, like: *self ++()
521 if op ~= '++' and op ~= '--' then
522 line = line .. '('
524 else
525 line = 'self->'..f.xarg.fullname..'('
527 else
528 line = f.xarg.fullname..'('
530 for i, a in ipairs(f.arguments) do
531 if not types[a.xarg.type_name] then
532 ignore(f.xarg.fullname, 'unkown argument type: '..a.xarg.type_name)
533 return nil
535 local aget, an, arg_as = types[a.xarg.type_name].get(stackn)
536 stack_args = stack_args .. types[a.xarg.type_name].onstack
537 if types[a.xarg.type_name].defect then defects = defects + types[a.xarg.type_name].defect end
538 wrap = wrap .. ' ' .. argument_name(arg_as or a.xarg.type_name, 'arg'..argn) .. ' = '
539 if a.xarg.default=='1' and an>0 then
540 wrap = wrap .. 'lua_isnoneornil(L, '..stackn..')'
541 for j = stackn+1,stackn+an-1 do
542 wrap = wrap .. ' && lua_isnoneornil(L, '..j..')'
544 local dv = a.xarg.defaultvalue
545 wrap = wrap .. ' ? static_cast< ' .. a.xarg.type_name .. ' >(' .. dv .. ') : '
547 wrap = wrap .. aget .. ';\n'
548 line = line .. (argn==1 and 'arg' or ', arg') .. argn
549 stackn = stackn + an
550 argn = argn + 1
552 if not f.xarg.name:match('%+%+') and not f.xarg.name:match('%-%-') then
553 line = line .. ')'
555 -- FIXME: hack follows for constructors
556 if f.calling_line then line = f.calling_line end
557 if f.return_type then line = f.return_type .. ' ret = ' .. line end
558 wrap = wrap .. ' ' .. line .. ';\n lua_settop(L, oldtop);\n' -- lua_pop(L, '..stackn..');\n'
559 if f.return_type then
560 if not types[f.return_type] then return nil end
561 local rput, rn = types[f.return_type].push'ret'
562 wrap = wrap .. ' luaL_checkstack(L, '..rn..', "cannot grow stack for return value");\n'
563 wrap = wrap .. ' '..rput..';\n return '..rn..';\n'
564 else
565 wrap = wrap .. ' return 0;\n'
567 f.wrapper_code = wrap
568 f.stack_arguments = stack_args
569 f.defects = defects
570 return f
573 local fill_test_code = function(f, types)
574 local stackn = 1
575 local test = ''
576 if f.xarg.member_of_class and f.xarg.static~='1' then
577 if not types[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
578 local stest, sn = types[f.xarg.member_of_class..'*'].test(stackn)
579 test = test .. ' && ' .. stest
580 stackn = stackn + sn
582 for i, a in ipairs(f.arguments) do
583 if not types[a.xarg.type_name] then return nil end -- print(a.xarg.type_name) return nil end
584 local atest, an = types[a.xarg.type_name].test(stackn)
585 if a.xarg.default=='1' and an>0 then
586 test = test .. ' && (lqtL_missarg(L, ' .. stackn .. ', ' .. an .. ') || '
587 test = test .. atest .. ')'
588 else
589 test = test .. ' && ' .. atest
591 stackn = stackn + an
593 -- can't make use of default values if I fix number of args
594 test = '(lua_gettop(L)<' .. stackn .. ')' .. test
595 f.test_code = test
596 return f
599 local fill_wrappers = function(functions, types)
600 local ret = {}
601 for f in pairs(functions) do
602 f = fill_wrapper_code(f, types)
603 if f then
604 f = assert(fill_test_code(f, types), f.xarg.fullname) -- MUST pass
605 ret[f] = true
606 --local out = 'extern "C" LQT_EXPORT int lqt_bind'..f.xarg.id..' (lua_State *L) {\n'
607 --.. f.wrapper_code .. '}\n'
608 --print(out)
611 return ret
614 local make_pushlines = function(args, types)
615 local pushlines, stack = '', 0
616 for i, a in ipairs(args) do
617 if not types[a.xarg.type_name] then return nil end
618 local apush, an = types[a.xarg.type_name].push('arg'..i)
619 pushlines = pushlines .. ' ' .. apush .. ';\n'
620 stack = stack + an
622 return pushlines, stack
625 local virtual_overload = function(v, types)
626 local ret = ''
627 if v.virtual_overload then return v end
628 -- make return type
629 if v.return_type and not types[v.return_type] then return nil end
630 local rget, rn = '', 0
631 if v.return_type then rget, rn, ret_as = types[v.return_type].get'oldtop+1' end
632 local retget = (v.return_type and argument_name(ret_as or v.return_type, 'ret')
633 .. ' = ' .. rget .. ';' or '') .. 'lua_settop(L, oldtop);return'
634 .. (v.return_type and ' ret' or '')
635 -- make argument push
636 local pushlines, stack = make_pushlines(v.arguments, types)
637 if not pushlines then return nil end
638 -- make lua call
639 local luacall = 'lqtL_pcall(L, '..(stack+1)..', '..rn..', 0)'
640 -- make prototype and fallback
641 local proto = (v.return_type or 'void')..' ;;'..v.xarg.name..' ('
642 local fallback = ''
643 for i, a in ipairs(v.arguments) do
644 proto = proto .. (i>1 and ', ' or '')
645 .. argument_name(a.xarg.type_name, 'arg'..i)
646 fallback = fallback .. (i>1 and ', arg' or 'arg') .. i
648 proto = proto .. ')' .. (v.xarg.constant=='1' and ' const' or '')
649 fallback = (v.return_type and 'return this->' or 'this->')
650 .. v.xarg.fullname .. '(' .. fallback .. ');\n}\n'
651 ret = proto .. [[ {
652 int oldtop = lua_gettop(L);
653 lqtL_pushudata(L, this, "]]..string.gsub(v.xarg.member_of_class, '::', '.')..[[*");
654 lqtL_getoverload(L, -1, "]]..v.xarg.name..[[");
655 if (lua_isfunction(L, -1)) {
656 lua_insert(L, -2);
657 ]] .. pushlines .. [[
658 if (!]]..luacall..[[) {
659 ]]..retget..[[;
662 lua_settop(L, oldtop);
663 ]] .. fallback
664 v.virtual_overload = ret
665 v.virtual_proto = string.gsub(proto, ';;', '', 1)
666 return v
669 local fill_shell_class = function(c, types)
670 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
671 local shell = 'class LQT_EXPORT ' .. shellname .. ' : public ' .. c.xarg.fullname .. ' {\npublic:\n'
672 shell = shell .. ' lua_State *L;\n'
673 for _, constr in ipairs(c.constructors) do
674 if constr.xarg.access~='private' then
675 local cline = ' '..shellname..' (lua_State *l'
676 local argline = ''
677 for i, a in ipairs(constr.arguments) do
678 cline = cline .. ', ' .. argument_name(a.xarg.type_name, 'arg'..i)
679 argline = argline .. (i>1 and ', arg' or 'arg') .. i
681 cline = cline .. ') : ' .. c.xarg.fullname
682 .. '(' .. argline .. '), L(l) '
683 .. '{ lqtL_register(L, this); }\n'
684 shell = shell .. cline
687 if c.copy_constructor==nil and c.public_constr then
688 local cline = ' '..shellname..' (lua_State *l, '..c.xarg.fullname..' const& arg1)'
689 cline = cline .. ' : ' .. c.xarg.fullname .. '(arg1), L(l) {}\n'
690 shell = shell .. cline
692 for i, v in pairs(c.virtuals) do
693 if v.xarg.access~='private' then
694 if v.virtual_proto then shell = shell .. ' virtual ' .. v.virtual_proto .. ';\n' end
697 shell = shell .. ' ~'..shellname..'() { lqtL_unregister(L, this); }\n'
698 if c.shell and c.qobject then
699 shell = shell .. ' static QMetaObject staticMetaObject;\n'
700 shell = shell .. ' virtual const QMetaObject *metaObject() const;\n'
701 shell = shell .. ' virtual int qt_metacall(QMetaObject::Call, int, void **);\n'
702 shell = shell .. 'private:\n'
703 shell = shell .. ' Q_DISABLE_COPY('..shellname..');\n'
705 shell = shell .. '};\n'
706 c.shell_class = shell
707 return c
710 local fill_virtual_overloads = function(classes, types)
711 for c in pairs(classes) do
712 for i, v in pairs(c.virtuals) do
713 if v.xarg.access~='private' then
714 local vret = virtual_overload(v, types)
718 return classes
721 local fill_shell_classes = function(classes, types)
722 local ret = {}
723 for c in pairs(classes) do
724 if c.shell then
725 c = fill_shell_class(c, types)
726 if c then ret[c] = true end
728 ret[c] = true
730 return ret
733 local print_shell_classes = function(classes)
734 local fhead = nil
735 for c in pairs(classes) do
736 if fhead then fhead:close() end
737 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
738 fhead = assert(io.open(module_name.._src..module_name..'_head_'..n..'.hpp', 'w'))
739 local print_head = function(...)
740 fhead:write(...)
741 fhead:write'\n'
743 print_head('#ifndef LQT_HEAD_'..n)
744 print_head('#define LQT_HEAD_'..n)
745 print_head(output_includes)
746 --print_head('#include <'..string.match(c.xarg.fullname, '^[^:]+')..'>')
747 print_head''
748 if c.shell then
749 if c then
750 print_head(c.shell_class)
751 else
752 --io.stderr:write(c.fullname, '\n')
755 print_head('#endif // LQT_HEAD_'..n)
757 if fhead then fhead:close() end
758 return classes
761 local print_virtual_overloads = function(classes)
762 for c in pairs(classes) do
763 if c.shell then
764 local vo = ''
765 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
766 for _,v in pairs(c.virtuals) do
767 if v.virtual_overload then
768 vo = vo .. string.gsub(v.virtual_overload, ';;', shellname..'::', 1)
771 c.virtual_overloads = vo
774 return classes
777 local print_wrappers = function(index)
778 for c in pairs(index) do
779 local meta = {}
780 local wrappers = ''
781 for _, f in ipairs(c.methods) do
782 -- FIXME: should we really discard virtual functions?
783 -- if the virtual overload in the shell uses rawget
784 -- on the environment then we can leave these in the
785 -- metatable
786 if f.wrapper_code and f.xarg.abstract~='1' then
787 local out = 'static int lqt_bind'..f.xarg.id
788 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
789 if f.xarg.access=='public' then
790 --print_meta(out)
791 wrappers = wrappers .. out .. '\n'
792 meta[f] = f.xarg.name
796 if not c.abstract then
797 for _, f in ipairs(c.constructors) do
798 if f.wrapper_code then
799 local out = 'static int lqt_bind'..f.xarg.id
800 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
801 if f.xarg.access=='public' then
802 --print_meta(out)
803 wrappers = wrappers .. out .. '\n'
804 meta[f] = 'new'
809 --local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
810 local lua_name = string.gsub(c.xarg.fullname, '::', '.')
811 local out = 'static int lqt_delete'..c.xarg.id..' (lua_State *L) {\n'
812 out = out ..' '..c.xarg.fullname..' *p = static_cast<'
813 ..c.xarg.fullname..'*>(lqtL_toudata(L, 1, "'..lua_name..'*"));\n'
814 if c.public_destr then
815 out = out .. ' if (p) delete p;\n'
817 out = out .. ' lqtL_eraseudata(L, 1, "'..lua_name..'*");\n return 0;\n}\n'
818 --print_meta(out)
819 wrappers = wrappers .. out .. '\n'
820 c.meta = meta
821 c.wrappers = wrappers
823 return index
826 local print_metatable = function(c)
827 local methods = {}
828 local wrappers = c.wrappers
829 for m, n in pairs(c.meta) do
830 methods[n] = methods[n] or {}
831 table.insert(methods[n], m)
833 for n, l in pairs(methods) do
834 local duplicates = {}
835 for _, f in ipairs(l) do
836 local itisnew = true
837 for sa, g in pairs(duplicates) do
838 if sa==f.stack_arguments then
839 --debug("function equal: ", f.xarg.fullname, f.stack_arguments, sa, f.defects, g.defects)
840 if f.defects<g.defects then
841 else
842 itisnew = false
844 elseif string.match(sa, "^"..f.stack_arguments) then -- there is already a version with more arguments
845 --debug("function superseded: ", f.xarg.fullname, f.stack_arguments, sa, f.defects, g.defects)
846 elseif string.match(f.stack_arguments, '^'..sa) then -- there is already a version with less arguments
847 --debug("function superseding: ", f.xarg.fullname, f.stack_arguments, sa, f.defects, g.defects)
850 if itisnew then
851 duplicates[f.stack_arguments] = f
854 --[[
855 local numinitial = 0
856 local numfinal = 0
857 for sa, f in pairs(l) do
858 numinitial = numinitial + 1
860 for sa, f in pairs(duplicates) do
861 numfinal = numfinal + 1
863 if numinitial-numfinal>0 then debug(c.xarg.fullname, "suppressed:", numinitial-numfinal) end
864 --]]
865 methods[n] = duplicates
867 for n, l in pairs(methods) do
868 local name = rename_operator(n)
869 local disp = 'static int lqt_dispatcher_'..name..c.xarg.id..' (lua_State *L) {\n'
870 for _, f in pairs(l) do
871 disp = disp..' if ('..f.test_code..') return lqt_bind'..f.xarg.id..'(L);\n'
873 disp = disp .. ' lua_settop(L, 0);\n'
874 disp = disp .. ' lua_pushstring(L, "'..c.xarg.fullname..'::'..n..': incorrect or extra arguments");\n'
875 disp = disp .. ' return lua_error(L);\n}\n'
876 --print_meta(disp)
877 wrappers = wrappers .. disp .. '\n'
879 local metatable = 'static luaL_Reg lqt_metatable'..c.xarg.id..'[] = {\n'
880 for n, l in pairs(methods) do
881 metatable = metatable .. ' { "'..rename_operator(n)..'", lqt_dispatcher_'..rename_operator(n)..c.xarg.id..' },\n'
883 metatable = metatable .. ' { "delete", lqt_delete'..c.xarg.id..' },\n'
884 metatable = metatable .. ' { 0, 0 },\n};\n'
885 --print_meta(metatable)
886 wrappers = wrappers .. metatable .. '\n'
887 local bases = ''
888 for b in string.gmatch(c.xarg.bases_with_attributes or '', '([^;]*);') do
889 if not string.match(b, '^virtual') then
890 b = string.gsub(b, '^[^%s]* ', '')
891 bases = bases .. ' {"'..string.gsub(b,'::','.')..'*", (char*)(void*)static_cast<'..b..'*>(('..c.xarg.fullname..'*)1)-(char*)1},\n'
894 bases = 'static lqt_Base lqt_base'..c.xarg.id..'[] = {\n'..bases..' {NULL, 0}\n};\n'
895 --print_meta(bases)
896 wrappers = wrappers .. bases .. '\n'
897 c.wrappers = wrappers
898 return c
901 local print_metatables = function(classes)
902 for c in pairs(classes) do
903 print_metatable(c)
905 return classes
908 local cpp_files = {}
910 local print_single_class = function(c)
911 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
912 local lua_name = string.gsub(c.xarg.fullname, '::', '.')
913 local cppname = module_name..'_meta_'..n..'.cpp'
914 table.insert(cpp_files, cppname);
915 local fmeta = assert(io.open(module_name.._src..cppname, 'w'))
916 local print_meta = function(...)
917 fmeta:write(...)
918 fmeta:write'\n'
920 print_meta('#include "'..module_name..'_head_'..n..'.hpp'..'"\n\n')
921 print_meta(c.wrappers)
922 if c.virtual_overloads then
923 print_meta(c.virtual_overloads)
925 print_meta('extern "C" LQT_EXPORT int luaopen_'..n..' (lua_State *L) {')
926 print_meta('\tlqtL_createclass(L, "'
927 ..lua_name..'*", lqt_metatable'
928 ..c.xarg.id..', lqt_base'
929 ..c.xarg.id..');')
930 print_meta'\treturn 0;'
931 print_meta'}'
932 print_meta''
933 if c.shell and c.qobject then
934 print_meta([[
935 #include <QDebug>
937 QMetaObject lqt_shell_]]..n..[[::staticMetaObject;
939 const QMetaObject *lqt_shell_]]..n..[[::metaObject() const {
940 //int oldtop = lua_gettop(L);
941 lqtL_pushudata(L, this, "]]..c.xarg.fullname..[[*");
942 lua_getfield(L, -1, LQT_OBJMETASTRING);
943 if (lua_isnil(L, -1)) {
944 lua_pop(L, 2);
945 return &]]..c.xarg.fullname..[[::staticMetaObject;
947 lua_getfield(L, -2, LQT_OBJMETADATA);
948 lqtL_touintarray(L);
949 //qDebug() << "copying qmeta object for slots in ]]..c.xarg.fullname..[[";
950 lqt_shell_]]..n..[[::staticMetaObject.d.superdata = &]]..c.xarg.fullname..[[::staticMetaObject;
951 lqt_shell_]]..n..[[::staticMetaObject.d.stringdata = lua_tostring(L, -2);
952 lqt_shell_]]..n..[[::staticMetaObject.d.data = (uint*)lua_touserdata(L, -1);
953 lqt_shell_]]..n..[[::staticMetaObject.d.extradata = 0; // slot_metaobj->d.extradata;
954 lua_setfield(L, LUA_REGISTRYINDEX, LQT_OBJMETADATA);
955 lua_setfield(L, LUA_REGISTRYINDEX, LQT_OBJMETASTRING);
956 lua_pop(L, 1);
957 //qDebug() << (lua_gettop(L) - oldtop);
958 return &lqt_shell_]]..n..[[::staticMetaObject;
961 int lqt_shell_]]..n..[[::qt_metacall(QMetaObject::Call call, int index, void **args) {
962 //qDebug() << "fake calling!";
963 index = ]]..c.xarg.fullname..[[::qt_metacall(call, index, args);
964 if (index < 0) return index;
965 return lqtL_qt_metacall(L, this, call, "QWidget*", index, args);
969 fmeta:close()
972 local print_merged_build = function()
973 local path = module_name.._src
974 local mergename = module_name..'_merged_build'
975 local merged = assert(io.open(path..mergename..'.cpp', 'w'))
976 for _, p in ipairs(cpp_files) do
977 merged:write('#include "'..p..'"\n')
979 local pro_file = assert(io.open(path..mergename..'.pro', 'w'))
981 local print_pro= function(...)
982 pro_file:write(...)
983 pro_file:write'\n'
985 print_pro('TEMPLATE = lib')
986 print_pro('TARGET = '..module_name)
987 print_pro('INCLUDEPATH += .')
988 print_pro('HEADERS += '..module_name..'_slot.hpp')
989 print_pro('SOURCES += ../common/lqt_common.cpp \\')
990 print_pro(' ../common/lqt_qt.cpp \\')
991 print_pro(' '..module_name..'_enum.cpp \\')
992 print_pro(' '..module_name..'_meta.cpp \\')
993 print_pro(' '..module_name..'_slot.cpp \\')
994 print_pro(' '..mergename..'.cpp')
997 local print_class_list = function(classes)
998 local qobject_present = false
999 local big_picture = {}
1000 local type_list_t = {}
1001 for c in pairs(classes) do
1002 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
1003 if n=='QObject' then qobject_present = true end
1004 print_single_class(c)
1005 table.insert(big_picture, 'luaopen_'..n)
1006 table.insert(type_list_t, 'add_class(\''..c.xarg.fullname..'\', types)\n')
1009 local type_list_f = assert(io.open(module_name.._src..module_name..'_types.lua', 'w'))
1010 type_list_f:write([[
1011 #!/usr/bin/lua
1012 local types = (...) or {}
1013 local add_class = lqt.classes.insert or error('module lqt.classes not loaded')
1015 for k, v in ipairs(type_list_t) do
1016 type_list_f:write(v)
1018 type_list_f:write('return types\n')
1019 type_list_f:close()
1021 print_merged_build()
1022 if fmeta then fmeta:close() end
1023 fmeta = assert(io.open(module_name.._src..module_name..'_meta.cpp', 'w'))
1024 local print_meta = function(...)
1025 fmeta:write(...)
1026 fmeta:write'\n'
1028 print_meta()
1029 print_meta('#include "lqt_common.hpp"')
1030 print_meta('#include "'..module_name..'_slot.hpp'..'"\n\n')
1031 for _, p in ipairs(big_picture) do
1032 print_meta('extern "C" LQT_EXPORT int '..p..' (lua_State *);')
1034 print_meta('void lqt_create_enums_'..module_name..' (lua_State *);')
1035 print_meta('extern "C" LQT_EXPORT int luaopen_'..module_name..' (lua_State *L) {')
1036 for _, p in ipairs(big_picture) do
1037 print_meta('\t'..p..'(L);')
1039 print_meta('\tlqt_create_enums_'..module_name..'(L);')
1040 if qobject_present then
1041 print_meta('\tlua_getfield(L, LUA_REGISTRYINDEX, "QObject*");')
1042 print_meta('\tlua_pushstring(L, "__addmethod");')
1043 print_meta('\tlqtL_pushaddmethod(L);')
1044 print_meta('\tlua_rawset(L, -3);')
1046 print_meta('\t//lua_pushlightuserdata(L, (void*)&LqtSlotAcceptor::staticMetaObject);')
1047 print_meta('\t//lua_setfield(L, LUA_REGISTRYINDEX, LQT_METAOBJECT);')
1048 print_meta('\tlqtL_passudata(L, (void*)(new LqtSlotAcceptor(L)), "QObject*");')
1049 print_meta('\tlua_setfield(L, LUA_REGISTRYINDEX, LQT_METACALLER);')
1050 print_meta('\treturn 0;\n}')
1051 if fmeta then fmeta:close() end
1052 return classes
1055 local fix_methods_wrappers = function(classes)
1056 for c in pairs(classes) do
1057 c.shell = (not c.abstract) and c.public_destr
1058 c.shell = c.shell and (next(c.virtuals)~=nil)
1059 for _, constr in ipairs(c.constructors) do
1060 if c.shell then
1061 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
1062 constr.calling_line = 'new '..shellname..'(L'
1063 if #(constr.arguments)>0 then constr.calling_line = constr.calling_line .. ', ' end
1064 else
1065 local shellname = c.xarg.fullname
1066 constr.calling_line = 'new '..shellname..'('
1068 for i=1,#(constr.arguments) do
1069 constr.calling_line = constr.calling_line .. (i==1 and '' or ', ') .. 'arg' .. i
1071 constr.calling_line = '*('..constr.calling_line .. '))'
1072 constr.xarg.static = '1'
1073 constr.return_type = constr.xarg.type_base..'&'
1075 if c.destructor then
1076 c.destructor.return_type = nil
1079 return classes
1082 local print_enum_tables = function(enums)
1083 for e in pairs(enums) do
1084 local table = 'static lqt_Enum lqt_enum'..e.xarg.id..'[] = {\n'
1085 --io.stderr:write(e.xarg.fullname, '\t', #e.values, '\n')
1086 for _,v in pairs(e.values) do
1087 table = table .. ' { "' .. v.xarg.name
1088 .. '", static_cast<int>('..v.xarg.fullname..') },\n'
1090 table = table .. ' { 0, 0 }\n'
1091 table = table .. '};\n'
1092 e.enum_table = table
1093 print_enum(table)
1095 return enums
1097 local print_enum_creator = function(enums, n)
1098 local out = 'static lqt_Enumlist lqt_enum_list[] = {\n'
1099 for e in pairs(enums) do
1100 out = out..' { lqt_enum'..e.xarg.id..', "'..string.gsub(e.xarg.fullname, "::", ".")..'" },\n'
1102 out = out..' { 0, 0 },\n};\n'
1103 out = out .. 'void lqt_create_enums_'..n..' (lua_State *L) {\n'
1104 out = out .. ' lqtL_createenumlist(L, lqt_enum_list); return;\n}\n'
1105 print_enum(out)
1106 return enums
1109 local copy_signals = function(functions)
1110 local ret = {}
1111 for f in pairs(functions) do
1112 if f.xarg.signal=='1' then
1113 ret[f] = 1
1116 return ret
1119 local slots_for_signals = function(signals, types)
1120 local ret = {}
1121 for sig in pairs(signals) do
1122 local args, comma = '(', ''
1123 for i, a in ipairs(sig.arguments) do
1124 args = args .. comma .. a.xarg.type_name .. ' arg'..i
1125 comma = ', '
1127 args = args .. ')'
1128 local pushlines, stack = make_pushlines(sig.arguments, types)
1129 if not ret['void __slot '..args] and pushlines then
1130 ret['void __slot '..args] = 'void LqtSlotAcceptor::__slot '..args..[[ {
1131 //int oldtop = lua_gettop(L);
1132 //lua_getfield(L, -1, "__slot]]..string.gsub(args, ' arg.', '')..[[");
1133 //if (lua_isnil(L, -1)) {
1134 //lua_pop(L, 1);
1135 //lua_getfield(L, -1, "__slot");
1137 //if (!lua_isfunction(L, -1)) {
1138 //lua_settop(L, oldtop);
1139 //return;
1141 lua_pushvalue(L, -2);
1142 ]] .. pushlines .. [[
1143 if (lqtL_pcall(L, ]]..stack..[[+1, 0, 0)) {
1144 //lua_error(L);
1145 qDebug() << lua_tostring(L, -1);
1146 lua_pop(L, 1);
1148 //lua_settop(L, oldtop);
1153 return ret
1156 local print_slots = function(s)
1157 print_slot_h'class LqtSlotAcceptor : public QObject {'
1158 print_slot_h' Q_OBJECT'
1159 print_slot_h' lua_State *L;'
1160 print_slot_h' public:'
1161 print_slot_h' LqtSlotAcceptor(lua_State *l, QObject *p=NULL) : QObject(p), L(l) { lqtL_register(L, this); }'
1162 print_slot_h' virtual ~LqtSlotAcceptor() { lqtL_unregister(L, this); }'
1163 print_slot_h' public slots:'
1164 for p, b in pairs(s) do
1165 print_slot_h(' '..p..';')
1167 print_slot_h'};\n'
1168 for p, b in pairs(s) do
1169 print_slot_c(b)
1174 --------------------------------------------------------------------------------------
1176 local typesystem = dofile(path..'types.lua')
1178 local ts = {}
1179 for i, ft in ipairs(typefiles) do
1180 ts = assert(loadfile(ft))(ts)
1182 setmetatable(typesystem, {
1183 __newindex = function(t, k, v)
1184 --debug('added type', k)
1185 ts[k] = v
1186 end,
1187 __index = function(t, k)
1188 local ret = ts[k]
1189 --if not ret then debug("unknown type:", tostring(k), ret) end
1190 return ret
1191 end,
1195 fix_arguments(idindex) -- fixes default arguments if they are context-relative
1196 local functions = copy_functions(idindex) -- picks functions and fixes label
1197 local functions = fix_functions(functions) -- fixes name and fullname and fills arguments
1199 local enums = copy_enums(idindex) -- picks enums if public
1200 local enums = fill_enums(enums) -- fills field "values"
1202 local classes = copy_classes(idindex) -- picks classes if not private and not blacklisted
1203 local classes = fill_virtuals(classes) -- does that, destructor ("~") excluded
1204 local classes = distinguish_methods(classes) -- does that
1205 local classes = fill_public_destr(classes) -- does that: checks if destructor is public
1206 local classes = fill_copy_constructor(classes) -- does that: checks if copy contructor is public or protected
1207 local classes = fix_methods_wrappers(classes)
1208 local classes = get_qobjects(classes)
1210 for _, f in ipairs(filterfiles) do
1211 classes, enums = loadfile(f)(classes, enums)
1214 local enums = fill_typesystem_with_enums(enums, typesystem) -- does that
1215 local classes = fill_typesystem_with_classes(classes, typesystem)
1217 local functions = fill_wrappers(functions, typesystem)
1218 local classes = fill_virtual_overloads(classes, typesystem) -- does that
1219 local classes = fill_shell_classes(classes, typesystem) -- does that
1221 local signals = copy_signals(functions)
1222 local slots = slots_for_signals(signals, typesystem)
1225 ------------- BEGIN OUTPUT
1229 print_enum(output_includes)
1230 print_slot_h(output_includes)
1231 print_slot_c('#include "'..module_name..'_slot.hpp'..'"\n\n')
1234 local classes = print_shell_classes(classes) -- does that
1235 local classes = print_virtual_overloads(classes, typesystem) -- does that
1236 local enums = print_enum_tables(enums) -- does that
1237 local enums = print_enum_creator(enums, module_name) -- does that + print enum list
1238 local classes = print_wrappers(classes) -- just compiles metatable list
1239 local classes = print_metatables(classes) -- just collects the wrappers + generates dispatchers
1240 local classes = print_class_list(classes) -- does that + prints everything related to class
1242 local slots = print_slots(slots)
1244 --print_openmodule(module_name) -- does that