compute the offset of base classes
[lqt.git] / generator / generator.lua
blob39078f5a01aff8833a647395c900a0499fe4024b
1 #!/usr/bin/lua
3 --[[
5 Copyright (c) 2007-2008 Mauro Iazzi
7 Permission is hereby granted, free of charge, to any person
8 obtaining a copy of this software and associated documentation
9 files (the "Software"), to deal in the Software without
10 restriction, including without limitation the rights to use,
11 copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the
13 Software is furnished to do so, subject to the following
14 conditions:
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
21 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
23 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
24 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
26 OTHER DEALINGS IN THE SOFTWARE.
28 --]]
30 local path = string.match(arg[0], '(.*/)[^%/]+') or ''
31 local filename = nil
32 local dirname = nil
33 local module_name = nil
34 local typefiles = {}
35 local filterfiles = {}
36 local output_includes = {
37 '"lqt_common.hpp"',
41 local i = 1
42 while select(i, ...) do
43 local argi = select(i, ...)
44 if argi=='-n' then
45 i = i + 1
46 module_name = select(i, ...)
47 elseif argi=='-i' then
48 i = i + 1
49 table.insert(output_includes, (select(i, ...)))
50 elseif argi=='-t' then
51 i = i + 1
52 table.insert(typefiles, (select(i, ...)))
53 elseif argi=='-f' then
54 i = i + 1
55 table.insert(filterfiles, (select(i, ...)))
56 else
57 filename = filename and error'duplicate filename' or argi
58 end
59 i = i + 1
60 end
61 end
63 local my_includes = ''
64 for _, i in ipairs(output_includes) do
65 my_includes = my_includes .. '#include '..i..'\n'
66 end
67 output_includes = my_includes .. '\n'
69 local readfile = function(fn)
70 local f = assert(io.open(fn))
71 local s = f:read'*a'
72 f:close()
73 return s
74 end
76 local fprint = function(f)
77 return function(...)
78 for i = 1, select('#',...) do
79 f:write((i==1) and '' or '\t', tostring(select(i,...)))
80 end
81 f:write'\n'
82 f:flush()
83 end
84 end
86 local debug = fprint(io.stderr)
87 local print_enum = fprint(assert(io.open(module_name..'_src/'..module_name..'_enum.cpp', 'w')))
88 local print_slot_h = fprint(assert(io.open(module_name..'_src/'..module_name..'_slot.hpp', 'w')))
89 local print_slot_c = fprint(assert(io.open(module_name..'_src/'..module_name..'_slot.cpp', 'w')))
91 local xmlstream, idindex = dofile(path..'xml.lua')(readfile(filename))
93 ----------------------------------------------------------------------------------
95 local copy_functions = function(index)
96 local ret = {}
97 for e in pairs(index) do
98 if e.label:match'^Function' then
99 e.label = 'Function'
100 ret[e] = true
103 return ret
107 local fix_arguments = function(all)
108 local fullnames = {}
109 for e in pairs(all or {}) do
110 if e.xarg.fullname then fullnames[e.xarg.fullname] = true end
112 for a in pairs(all) do
113 if a.label=='Argument'
114 and a.xarg.default=='1'
115 and (not string.match(a.xarg.defaultvalue, '^[-+]?%d+%.?%d*$'))
116 and a.xarg.defaultvalue~='true'
117 and a.xarg.defaultvalue~='false'
118 and (not string.match(a.xarg.defaultvalue, '^0[xX]%d+$')) then
119 local dv, call = string.match(a.xarg.defaultvalue, '(.-)(%b())')
120 dv = dv or a.xarg.defaultvalue
121 call = call or ''
122 if not fullnames[dv] then
123 dv = a.xarg.context..'::'..dv..call
125 if fullnames[dv] then
126 a.xarg.defaultvalue = dv..call
127 else
128 a.xarg.default = nil
129 a.xarg.defaultvalue = nil
133 return all
136 local fix_functions = function(index)
137 for f in pairs(index) do
138 local args = {}
139 for i, a in ipairs(f) do
140 -- avoid bogus 'void' arguments
141 if a.xarg.type_name=='void' and i==1 and f[2]==nil then break end
142 if a.label=='Argument' then
143 table.insert(args, a)
146 f.arguments = args
147 f.return_type = f.xarg.type_name
148 if f.xarg.type_name=='void' then
149 f.return_type = nil
152 return index
155 local copy_enums = function(index)
156 local ret = {}
157 for e in pairs(index) do
158 if e.label=='Enum'
159 and not string.match(e.xarg.fullname, '%b<>')
160 and e.xarg.access=='public' then
161 ret[e] = true
164 return ret
167 local fill_enums = function(index)
168 for e in pairs(index) do
169 local values = {}
170 for _, v in ipairs(e) do
171 if v.label=='Enumerator' then
172 table.insert(values, v)
175 e.values = values
177 return index
180 local copy_classes = function(index)
181 local ret = {}
182 for e in pairs(index) do
183 if e.label=='Class'
184 and e.xarg.access~='private'
185 and not e.xarg.fullname:match'%b<>' then
186 ret[e] = true
189 return ret
192 local fill_virtuals = function(index)
193 local classes = {}
194 for c in pairs(index) do
195 classes[c.xarg.fullname] = c
197 local get_virtuals
198 get_virtuals = function(c)
199 local ret = {}
200 for _, f in ipairs(c) do
201 if f.label=='Function' and f.xarg.virtual=='1' then
202 local n = string.match(f.xarg.name, '~') or f.xarg.name
203 if n~='~' then ret[n] = f end
206 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
207 local base = classes[b]
208 if type(base)=='table' then
209 local bv = get_virtuals(base)
210 for n, f in pairs(bv) do
211 if not ret[n] then ret[n] = f end
215 for _, f in ipairs(c) do
216 if f.label=='Function'
217 and f.xarg.access~='private'
218 and (ret[string.match(f.xarg.name, '~') or f.xarg.name]) then
219 f.xarg.virtual = '1'
220 local n = string.match(f.xarg.name, '~')or f.xarg.name
221 ret[n] = f
224 return ret
226 for c in pairs(index) do
227 c.virtuals = get_virtuals(c)
228 for _, f in pairs(c.virtuals) do
229 if f.xarg.abstract=='1' then c.abstract=true break end
232 return index
235 local distinguish_methods = function(index)
236 for c in pairs(index) do
237 local construct, destruct, normal = {}, nil, {}
238 local n = c.xarg.name
239 local copy = nil
240 for _, f in ipairs(c) do
241 if n==f.xarg.name then
242 table.insert(construct, f)
243 elseif f.xarg.name:match'~' then
244 destruct = f
245 else
246 if (not string.match(f.xarg.name, '^operator%W'))
247 and (not f.xarg.member_template_parameters) then
248 table.insert(normal, f)
252 c.constructors = construct
253 c.destructor = destruct
254 c.methods = normal
256 return index
259 local fill_public_destr = function(index)
260 local classes = {}
261 for c in pairs(index) do
262 classes[c.xarg.fullname] = c
264 local destr_is_public
265 destr_is_public = function(c)
266 if c.destructor then
267 return c.destructor.xarg.access=='public'
268 else
269 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
270 local base = classes[b]
271 if base and not destr_is_public(base) then
272 return false
275 return true
278 for c in pairs(index) do
279 c.public_destr = destr_is_public(c)
281 return index
284 local fill_copy_constructor = function(index)
285 local classes = {}
286 for c in pairs(index) do
287 classes[c.xarg.name] = c
289 for c in pairs(index) do
290 local copy = nil
291 for _, f in ipairs(c.constructors) do
292 if #(f.arguments)==1
293 and f.arguments[1].xarg.type_name==c.xarg.fullname..' const&' then
294 copy = f
295 break
298 c.copy_constructor = copy
300 local copy_constr_is_public
301 copy_constr_is_public = function(c)
302 if c.copy_constructor then
303 return (c.copy_constructor.xarg.access=='public')
304 or (c.copy_constructor.xarg.access=='protected')
305 else
306 local ret = nil
307 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
308 local base = classes[b]
309 if base and not copy_constr_is_public(base) then
310 return false
313 return true
316 for c in pairs(index) do
317 c.public_constr = copy_constr_is_public(c)
319 return index
322 local fill_typesystem_with_enums = function(enums, types)
323 local etype = function(en)
324 return {
325 push = function(n)
326 return 'lqtL_pushenum(L, '..n..', "'..en..'")', 1
327 end,
328 get = function(n)
329 return 'static_cast<'..en..'>'
330 ..'(lqtL_toenum(L, '..n..', "'..en..'"))', 1
331 end,
332 test = function(n)
333 return 'lqtL_isenum(L, '..n..', "'..en..'")', 1
334 end,
337 local ret = {}
338 for e in pairs(enums) do
339 if types[e.xarg.fullname]==nil then
340 ret[e] = true
341 types[e.xarg.fullname] = etype(e.xarg.fullname)
342 else
343 --io.stderr:write(e.xarg.fullname, ': already present\n')
346 return ret
349 local fill_typesystem_with_classes = function(classes, types)
350 local pointer_t = function(fn)
351 return {
352 -- the argument is a pointer to class
353 push = function(n)
354 return 'lqtL_passudata(L, '..n..', "'..fn..'*")', 1
355 end,
356 get = function(n)
357 return 'static_cast<'..fn..'*>'
358 ..'(lqtL_toudata(L, '..n..', "'..fn..'*"))', 1
359 end,
360 test = function(n)
361 return 'lqtL_isudata(L, '..n..', "'..fn..'*")', 1
362 end,
365 local pointer_const_t = function(fn)
366 return {
367 -- the argument is a pointer to constant class instance
368 push = function(n)
369 return 'lqtL_passudata(L, '..n..', "'..fn..'*")', 1
370 end,
371 get = function(n)
372 return 'static_cast<'..fn..'*>'
373 ..'(lqtL_toudata(L, '..n..', "'..fn..'*"))', 1
374 end,
375 test = function(n)
376 return 'lqtL_isudata(L, '..n..', "'..fn..'*")', 1
377 end,
380 local ref_t = function(fn)
381 return {
382 -- the argument is a reference to class
383 push = function(n)
384 return 'lqtL_passudata(L, &'..n..', "'..fn..'*")', 1
385 end,
386 get = function(n)
387 return '*static_cast<'..fn..'*>'
388 ..'(lqtL_toudata(L, '..n..', "'..fn..'*"))', 1
389 end,
390 test = function(n)
391 return 'lqtL_isudata(L, '..n..', "'..fn..'*")', 1
392 end,
395 local instance_t = function(fn)
396 return {
397 -- the argument is the class itself
398 push = function(n)
399 return 'lqtL_copyudata(L, &'..n..', "'..fn..'*")', 1
400 end,
401 get = function(n)
402 return '*static_cast<'..fn..'*>'
403 ..'(lqtL_toudata(L, '..n..', "'..fn..'*"))', 1
404 end,
405 test = function(n)
406 return 'lqtL_isudata(L, '..n..', "'..fn..'*")', 1
407 end,
410 local const_ref_t = function(fn)
411 return {
412 -- the argument is a pointer to class
413 push = function(n)
414 return 'lqtL_copyudata(L, &'..n..', "'..fn..'*")', 1, string.gsub(fn, ' const&$', '')
415 end,
416 get = function(n)
417 return '*static_cast<'..fn..'*>'
418 ..'(lqtL_toudata(L, '..n..', "'..fn..'*"))', 1
419 end,
420 test = function(n)
421 return 'lqtL_isudata(L, '..n..', "'..fn..'*")', 1
422 end,
425 local ret = {}
426 for c in pairs(classes) do
427 if types[c.xarg.fullname]==nil then
428 ret[c] = true
429 types[c.xarg.fullname..'*'] = pointer_t(c.xarg.fullname)
430 types[c.xarg.fullname..' const*'] = pointer_const_t(c.xarg.fullname)
431 types[c.xarg.fullname..'&'] = ref_t(c.xarg.fullname)
432 if c.public_constr and c.shell then
433 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
434 types[c.xarg.fullname] = instance_t(c.xarg.fullname, shellname)
435 types[c.xarg.fullname..' const&'] = const_ref_t(c.xarg.fullname, shellname)
439 return ret
442 local argument_name = function(tn, an)
443 local ret
444 if string.match(tn, '%(%*%)') then
445 ret = string.gsub(tn, '%(%*%)', '(*'..an..')', 1)
446 elseif string.match(tn, '%[.*%]') then
447 ret = string.gsub(tn, '(%[.*%])', an..'%1')
448 else
449 ret = tn .. ' ' .. an
451 return ret
454 local fill_wrapper_code = function(f, types)
455 if f.wrapper_code then return f end
456 local stackn, argn = 1, 1
457 local wrap, line = ' int oldtop = lua_gettop(L);\n', ''
458 if f.xarg.abstract then return nil end
459 if f.xarg.member_of_class and f.xarg.static~='1' then
460 if not types[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
461 local sget, sn = types[f.xarg.member_of_class..'*'].get(stackn)
462 wrap = wrap .. ' ' .. f.xarg.member_of_class .. '* self = ' .. sget .. ';\n'
463 stackn = stackn + sn
464 wrap = wrap .. [[
465 if (NULL==self) {
466 lua_pushstring(L, "this pointer is NULL");
467 lua_error(L);
470 --print(sget, sn)
471 line = 'self->'..f.xarg.fullname..'('
472 else
473 line = f.xarg.fullname..'('
475 for i, a in ipairs(f.arguments) do
476 if not types[a.xarg.type_name] then return nil end
477 local aget, an, arg_as = types[a.xarg.type_name].get(stackn)
478 wrap = wrap .. ' ' .. argument_name(arg_as or a.xarg.type_name, 'arg'..argn) .. ' = '
479 if a.xarg.default=='1' and an>0 then
480 wrap = wrap .. 'lua_isnoneornil(L, '..stackn..')'
481 for j = stackn+1,stackn+an-1 do
482 wrap = wrap .. ' && lua_isnoneornil(L, '..j..')'
484 local dv = a.xarg.defaultvalue
485 wrap = wrap .. ' ? static_cast< ' .. a.xarg.type_name .. ' >(' .. dv .. ') : '
487 wrap = wrap .. aget .. ';\n'
488 line = line .. (argn==1 and 'arg' or ', arg') .. argn
489 stackn = stackn + an
490 argn = argn + 1
492 line = line .. ')'
493 -- FIXME: hack follows for constructors
494 if f.calling_line then line = f.calling_line end
495 if f.return_type then line = f.return_type .. ' ret = ' .. line end
496 wrap = wrap .. ' ' .. line .. ';\n lua_settop(L, oldtop);\n' -- lua_pop(L, '..stackn..');\n'
497 if f.return_type then
498 if not types[f.return_type] then return nil end
499 local rput, rn = types[f.return_type].push'ret'
500 wrap = wrap .. ' luaL_checkstack(L, '..rn..', "cannot grow stack for return value");\n'
501 wrap = wrap .. ' '..rput..';\n return '..rn..';\n'
502 else
503 wrap = wrap .. ' return 0;\n'
505 f.wrapper_code = wrap
506 return f
509 local fill_test_code = function(f, types)
510 local stackn = 1
511 local test = ''
512 if f.xarg.member_of_class and f.xarg.static~='1' then
513 if not types[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
514 local stest, sn = types[f.xarg.member_of_class..'*'].test(stackn)
515 test = test .. ' && ' .. stest
516 stackn = stackn + sn
518 for i, a in ipairs(f.arguments) do
519 if not types[a.xarg.type_name] then return nil end -- print(a.xarg.type_name) return nil end
520 local atest, an = types[a.xarg.type_name].test(stackn)
521 if a.xarg.default=='1' and an>0 then
522 test = test .. ' && (lqtL_missarg(L, ' .. stackn .. ', ' .. an .. ') || '
523 test = test .. atest .. ')'
524 else
525 test = test .. ' && ' .. atest
527 stackn = stackn + an
529 -- can't make use of default values if I fix number of args
530 test = '(lua_gettop(L)<' .. stackn .. ')' .. test
531 f.test_code = test
532 return f
535 local fill_wrappers = function(functions, types)
536 local ret = {}
537 for f in pairs(functions) do
538 f = fill_wrapper_code(f, types)
539 if f then
540 f = assert(fill_test_code(f, types), f.xarg.fullname) -- MUST pass
541 ret[f] = true
542 --local out = 'extern "C" int lqt_bind'..f.xarg.id..' (lua_State *L) {\n'
543 --.. f.wrapper_code .. '}\n'
544 --print(out)
547 return ret
550 local make_pushlines = function(args, types)
551 local pushlines, stack = '', 0
552 for i, a in ipairs(args) do
553 if not types[a.xarg.type_name] then return nil end
554 local apush, an = types[a.xarg.type_name].push('arg'..i)
555 pushlines = pushlines .. ' ' .. apush .. ';\n'
556 stack = stack + an
558 return pushlines, stack
561 local virtual_overload = function(v, types)
562 local ret = ''
563 if v.virtual_overload then return v end
564 -- make return type
565 if v.return_type and not types[v.return_type] then return nil end
566 local rget, rn = '', 0
567 if v.return_type then rget, rn, ret_as = types[v.return_type].get'oldtop+1' end
568 local retget = (v.return_type and argument_name(ret_as or v.return_type, 'ret')
569 .. ' = ' .. rget .. ';' or '') .. 'lua_settop(L, oldtop);return'
570 .. (v.return_type and ' ret' or '')
571 -- make argument push
572 local pushlines, stack = make_pushlines(v.arguments, types)
573 if not pushlines then return nil end
574 -- make lua call
575 local luacall = 'lua_pcall(L, '..(stack+1)..', '..rn..', 0)'
576 -- make prototype and fallback
577 local proto = (v.return_type or 'void')..' ;;'..v.xarg.name..' ('
578 local fallback = ''
579 for i, a in ipairs(v.arguments) do
580 proto = proto .. (i>1 and ', ' or '')
581 .. argument_name(a.xarg.type_name, 'arg'..i)
582 fallback = fallback .. (i>1 and ', arg' or 'arg') .. i
584 proto = proto .. ')' .. (v.xarg.constant=='1' and ' const' or '')
585 fallback = (v.return_type and 'return this->' or 'this->')
586 .. v.xarg.fullname .. '(' .. fallback .. ');\n}\n'
587 ret = proto .. [[ {
588 int oldtop = lua_gettop(L);
589 lqtL_pushudata(L, this, "]]..v.xarg.member_of_class..[[*");
590 lua_getfield(L, -1, "]]..v.xarg.name..[[");
591 if (lua_isfunction(L, -1)) {
592 lua_insert(L, -2);
593 ]] .. pushlines .. [[
594 if (!]]..luacall..[[) {
595 ]]..retget..[[;
598 lua_settop(L, oldtop);
599 ]] .. fallback
600 v.virtual_overload = ret
601 v.virtual_proto = string.gsub(proto, ';;', '', 1)
602 return v
605 local fill_shell_class = function(c, types)
606 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
607 local shell = 'class ' .. shellname .. ' : public ' .. c.xarg.fullname .. ' {\npublic:\n'
608 shell = shell .. ' lua_State *L;\n'
609 for _, constr in ipairs(c.constructors) do
610 if constr.xarg.access~='private' then
611 local cline = ' '..shellname..' (lua_State *l'
612 local argline = ''
613 for i, a in ipairs(constr.arguments) do
614 cline = cline .. ', ' .. argument_name(a.xarg.type_name, 'arg'..i)
615 argline = argline .. (i>1 and ', arg' or 'arg') .. i
617 cline = cline .. ') : ' .. c.xarg.fullname
618 .. '(' .. argline .. '), L(l) '
619 .. '{ lqtL_register(L, this); }\n'
620 shell = shell .. cline
623 if c.copy_constructor==nil and c.public_constr then
624 local cline = ' '..shellname..' (lua_State *l, '..c.xarg.fullname..' const& arg1)'
625 cline = cline .. ' : ' .. c.xarg.fullname .. '(arg1), L(l) {}\n'
626 shell = shell .. cline
628 for i, v in pairs(c.virtuals) do
629 if v.xarg.access~='private' then
630 if v.virtual_proto then shell = shell .. ' virtual ' .. v.virtual_proto .. ';\n' end
633 shell = shell .. ' ~'..shellname..'() { lqtL_unregister(L, this); }\n'
634 shell = shell .. '};\n'
635 c.shell_class = shell
636 return c
639 local fill_virtual_overloads = function(classes, types)
640 for c in pairs(classes) do
641 for i, v in pairs(c.virtuals) do
642 if v.xarg.access~='private' then
643 local vret = virtual_overload(v, types)
647 return classes
650 local fill_shell_classes = function(classes, types)
651 local ret = {}
652 for c in pairs(classes) do
653 if c.shell then
654 c = fill_shell_class(c, types)
655 if c then ret[c] = true end
657 ret[c] = true
659 return ret
662 local print_shell_classes = function(classes)
663 local fhead = nil
664 for c in pairs(classes) do
665 if fhead then fhead:close() end
666 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
667 fhead = assert(io.open(module_name..'_src/'..module_name..'_head_'..n..'.hpp', 'w'))
668 local print_head = function(...)
669 fhead:write(...)
670 fhead:write'\n'
672 print_head('#ifndef LQT_HEAD_'..n)
673 print_head('#define LQT_HEAD_'..n)
674 print_head(output_includes)
675 print_head('#include <'..string.match(c.xarg.fullname, '^[^:]+')..'>')
676 print_head''
677 if c.shell then
678 if c then
679 print_head(c.shell_class)
680 else
681 --io.stderr:write(c.fullname, '\n')
684 print_head('#endif // LQT_HEAD_'..n)
686 if fhead then fhead:close() end
687 return classes
690 local print_virtual_overloads = function(classes)
691 for c in pairs(classes) do
692 if c.shell then
693 local vo = ''
694 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
695 for _,v in pairs(c.virtuals) do
696 if v.virtual_overload then
697 vo = vo .. string.gsub(v.virtual_overload, ';;', shellname..'::', 1)
700 c.virtual_overloads = vo
703 return classes
706 local print_wrappers = function(index)
707 for c in pairs(index) do
708 local meta = {}
709 local wrappers = ''
710 for _, f in ipairs(c.methods) do
711 if f.wrapper_code and f.xarg.virtual~='1' then
712 local out = 'static int lqt_bind'..f.xarg.id
713 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
714 if f.xarg.access=='public' then
715 --print_meta(out)
716 wrappers = wrappers .. out .. '\n'
717 meta[f] = f.xarg.name
721 if c.shell then
722 for _, f in ipairs(c.constructors) do
723 if f.wrapper_code then
724 local out = 'static int lqt_bind'..f.xarg.id
725 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
726 if f.xarg.access=='public' then
727 --print_meta(out)
728 wrappers = wrappers .. out .. '\n'
729 meta[f] = 'new'
733 --local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
734 local out = 'static int lqt_delete'..c.xarg.id..' (lua_State *L) {\n'
735 out = out ..' '..c.xarg.fullname..' *p = static_cast<'
736 ..c.xarg.fullname..'*>(lqtL_toudata(L, 1, "'..c.xarg.fullname..'*"));\n'
737 out = out .. ' if (p) delete p;\n return 0;\n}\n'
738 --print_meta(out)
739 wrappers = wrappers .. out .. '\n'
741 c.meta = meta
742 c.wrappers = wrappers
744 return index
747 local print_metatable = function(c)
748 local methods = {}
749 local wrappers = c.wrappers
750 for m, n in pairs(c.meta) do
751 methods[n] = methods[n] or {}
752 table.insert(methods[n], m)
754 for n, l in pairs(methods) do
755 local disp = 'static int lqt_dispatcher_'..n..c.xarg.id..' (lua_State *L) {\n'
756 for _, f in ipairs(l) do
757 disp = disp..' if ('..f.test_code..') return lqt_bind'..f.xarg.id..'(L);\n'
759 disp = disp .. ' lua_settop(L, 0);\n'
760 disp = disp .. ' lua_pushstring(L, "incorrect or extra arguments");\n'
761 disp = disp .. ' return lua_error(L);\n}\n'
762 --print_meta(disp)
763 wrappers = wrappers .. disp .. '\n'
765 local metatable = 'static luaL_Reg lqt_metatable'..c.xarg.id..'[] = {\n'
766 for n, l in pairs(methods) do
767 metatable = metatable .. ' { "'..n..'", lqt_dispatcher_'..n..c.xarg.id..' },\n'
769 if c.shell then
770 metatable = metatable .. ' { "delete", lqt_delete'..c.xarg.id..' },\n'
772 metatable = metatable .. ' { 0, 0 },\n};\n'
773 --print_meta(metatable)
774 wrappers = wrappers .. metatable .. '\n'
775 local bases = ''
776 for b in string.gmatch(c.xarg.bases or '', '([^;]*);') do
777 bases = bases .. '{"' .. b .. '*", (char*)(void*)static_cast<'..b..'*>(static_cast<'..c.xarg.fullname..'*>(NULL))-(char*)NULL}, '
779 bases = 'static lqt_Base lqt_base'..c.xarg.id..'[] = { '..bases..'{NULL, NULL} };\n'
780 --print_meta(bases)
781 wrappers = wrappers .. bases .. '\n'
782 c.wrappers = wrappers
783 return c
786 local print_metatables = function(classes)
787 for c in pairs(classes) do
788 print_metatable(c)
790 return classes
793 local print_single_class = function(c)
794 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
795 local fmeta = assert(io.open(module_name..'_src/'..module_name..'_meta_'..n..'.cpp', 'w'))
796 local print_meta = function(...)
797 fmeta:write(...)
798 fmeta:write'\n'
800 print_meta('#include "'..module_name..'_head_'..n..'.hpp'..'"\n\n')
801 print_meta(c.wrappers)
802 if c.virtual_overloads then
803 print_meta(c.virtual_overloads)
805 print_meta('extern "C" int luaopen_'..n..' (lua_State *L) {')
806 print_meta('\tlqtL_createclass(L, "'
807 ..n..'*", lqt_metatable'
808 ..c.xarg.id..', lqt_base'
809 ..c.xarg.id..');')
810 print_meta'\treturn 0;'
811 print_meta'}'
812 print_meta''
813 fmeta:close()
816 local print_class_list = function(classes)
817 local big_picture = {}
818 for c in pairs(classes) do
819 local n = string.gsub(c.xarg.fullname, '::', '_LQT_')
820 print_single_class(c)
821 table.insert(big_picture, 'luaopen_'..n)
823 if fmeta then fmeta:close() end
824 fmeta = assert(io.open(module_name..'_src/'..module_name..'_meta.cpp', 'w'))
825 local print_meta = function(...)
826 fmeta:write(...)
827 fmeta:write'\n'
829 print_meta('#include "lqt_common.hpp"')
830 for _, p in ipairs(big_picture) do
831 print_meta('extern "C" int '..p..' (lua_State *);')
833 print_meta('extern "C" int lqt_slot (lua_State *);')
834 print_meta('int lqt_create_enums_'..module_name..' (lua_State *);')
835 print_meta('extern "C" int luaopen_'..module_name..' (lua_State *L) {')
836 for _, p in ipairs(big_picture) do
837 print_meta('\t'..p..'(L);')
839 print_meta('\tlqt_create_enums_'..module_name..'(L);')
840 print_meta('\tlua_pushcfunction(L, lqt_slot);')
841 print_meta('\tlua_setglobal(L, "newslot");')
842 print_meta('\treturn 0;\n}')
843 if fmeta then fmeta:close() end
844 return classes
847 local fix_methods_wrappers = function(classes)
848 for c in pairs(classes) do
849 c.shell = (not c.abstract) and c.public_destr
850 for _, constr in ipairs(c.constructors) do
851 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
852 constr.calling_line = '*new '..shellname..'(L'
853 for i=1,#(constr.arguments) do
854 constr.calling_line = constr.calling_line .. ', arg' .. i
856 constr.calling_line = constr.calling_line .. ')'
857 constr.xarg.static = '1'
858 constr.return_type = constr.xarg.type_base..'&'
860 if c.destructor then
861 c.destructor.return_type = nil
864 return classes
867 local print_enum_tables = function(enums)
868 for e in pairs(enums) do
869 local table = 'static lqt_Enum lqt_enum'..e.xarg.id..'[] = {\n'
870 --io.stderr:write(e.xarg.fullname, '\t', #e.values, '\n')
871 for _,v in pairs(e.values) do
872 table = table .. ' { "' .. v.xarg.name
873 .. '", static_cast<int>('..v.xarg.fullname..') },\n'
875 table = table .. ' { 0, 0 }\n'
876 table = table .. '};\n'
877 e.enum_table = table
878 print_enum(table)
880 return enums
882 local print_enum_creator = function(enums, n)
883 local out = 'static lqt_Enumlist lqt_enum_list[] = {\n'
884 for e in pairs(enums) do
885 out = out..' { lqt_enum'..e.xarg.id..', "'..e.xarg.fullname..'" },\n'
887 out = out..' { 0, 0 },\n};\n'
888 out = out .. 'void lqt_create_enums_'..n..' (lua_State *L) {\n'
889 out = out .. ' lqtL_createenumlist(L, lqt_enum_list); return;\n}\n'
890 print_enum(out)
891 return enums
894 local copy_signals = function(functions)
895 local ret = {}
896 for f in pairs(functions) do
897 if f.xarg.signal=='1' then
898 ret[f] = 1
901 return ret
904 local slots_for_signals = function(signals, types)
905 local ret = {}
906 for sig in pairs(signals) do
907 local args, comma = '(', ''
908 for i, a in ipairs(sig.arguments) do
909 args = args .. comma .. a.xarg.type_name .. ' arg'..i
910 comma = ', '
912 args = args .. ')'
913 local pushlines, stack = make_pushlines(sig.arguments, types)
914 if not ret['void slot '..args] and pushlines then
915 ret['void slot '..args] = 'void LqtSlotAcceptor::slot '..args..[[ {
916 int oldtop = lua_gettop(L);
917 lqtL_pushudata(L, this, "QObject*");
918 lua_getfield(L, -1, "slot]]..string.gsub(args, ' arg.', '')..[[");
919 if (lua_isnil(L, -1)) {
920 lua_pop(L, 1);
921 lua_getfield(L, -1, "slot");
923 if (!lua_isfunction(L, -1)) {
924 lua_settop(L, oldtop);
925 return;
927 lua_insert(L, -2);
928 ]] .. pushlines .. [[
929 if (lua_pcall(L, ]]..stack..[[+1, 0, 0)) {
930 lua_error(L);
932 lua_settop(L, oldtop);
937 return ret
940 local print_slots = function(s)
941 print_slot_h'class LqtSlotAcceptor : public QObject {'
942 print_slot_h' Q_OBJECT'
943 print_slot_h' lua_State *L;'
944 print_slot_h' public:'
945 print_slot_h' LqtSlotAcceptor(lua_State *l, QObject *p=NULL) : QObject(p), L(l) { lqtL_register(L, this); }'
946 print_slot_h' virtual ~LqtSlotAcceptor() { lqtL_unregister(L, this); }'
947 print_slot_h' public slots:'
948 for p, b in pairs(s) do
949 print_slot_h(' '..p..';')
951 print_slot_h'};\n'
952 for p, b in pairs(s) do
953 print_slot_c(b)
955 print_slot_c[[
957 extern "C" int lqt_slot (lua_State *L) {
958 QObject *parent = static_cast<QObject*>(lqtL_toudata(L, 1, "QObject*"));
959 lqtL_passudata(L, new LqtSlotAcceptor(L, parent), "QObject*");
960 return 1;
966 --------------------------------------------------------------------------------------
968 local typesystem = {}
970 local ts = {}
971 for i, ft in ipairs(typefiles) do
972 ts = loadfile(ft)(ts)
974 setmetatable(typesystem, {
975 __newindex = function(t, k, v)
976 --debug('added type', k)
977 ts[k] = v
978 end,
979 __index = function(t, k)
980 local ret = ts[k]
981 --if not ret then debug("unknown type:", tostring(k), ret) end
982 return ret
983 end,
987 fix_arguments(idindex) -- fixes default arguments if they are context-relative
988 local functions = copy_functions(idindex) -- picks functions and fixes label
989 local functions = fix_functions(functions) -- fixes name and fullname and fills arguments
991 local enums = copy_enums(idindex) -- picks enums if public
992 local enums = fill_enums(enums) -- fills field "values"
994 local classes = copy_classes(idindex) -- picks classes if not private and not blacklisted
995 local classes = fill_virtuals(classes) -- does that, destructor ("~") excluded
996 local classes = distinguish_methods(classes) -- does that
997 local classes = fill_public_destr(classes) -- does that: checks if destructor is public
998 local classes = fill_copy_constructor(classes) -- does that: checks if copy contructor is public or protected
999 local classes = fix_methods_wrappers(classes)
1001 for _, f in ipairs(filterfiles) do
1002 classes, enums = loadfile(f)(classes, enums)
1005 local enums = fill_typesystem_with_enums(enums, typesystem) -- does that
1006 local classes = fill_typesystem_with_classes(classes, typesystem)
1008 local functions = fill_wrappers(functions, typesystem)
1009 local classes = fill_virtual_overloads(classes, typesystem) -- does that
1010 local classes = fill_shell_classes(classes, typesystem) -- does that
1012 local signals = copy_signals(functions)
1013 local slots = slots_for_signals(signals, typesystem)
1016 ------------- BEGIN OUTPUT
1020 print_enum(output_includes)
1021 print_slot_h(output_includes)
1022 print_slot_c('#include "'..module_name..'_slot.hpp'..'"\n\n')
1025 local classes = print_shell_classes(classes) -- does that
1026 local classes = print_virtual_overloads(classes, typesystem) -- does that
1027 local enums = print_enum_tables(enums) -- does that
1028 local enums = print_enum_creator(enums, module_name) -- does that + print enum list
1029 local classes = print_wrappers(classes) -- just compiles metatable list
1030 local classes = print_metatables(classes) -- just collects the wrappers + generates dispatchers
1031 local classes = print_class_list(classes) -- does that + prints everything related to class
1033 local slots = print_slots(slots)
1035 --print_openmodule(module_name) -- does that