generator passes current typesystem to typefiles
[lqt.git] / generator / generator.lua
blob96f5eea878425fea8e7a37f5ace8ca185c83a165
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 cppname = nil
33 local hppname = nil
34 local dirname = nil
35 local module_name = nil
36 local typefiles = {}
37 local output_includes = {
38 '"lqt_common.hpp"',
42 local i = 1
43 while select(i, ...) do
44 local argi = select(i, ...)
45 if argi=='-n' then
46 i = i + 1
47 module_name = select(i, ...)
48 elseif argi=='-d' then
49 i = i + 1
50 dirname = select(i, ...)
51 elseif argi=='-i' then
52 i = i + 1
53 table.insert(output_includes, (select(i, ...)))
54 elseif argi=='-t' then
55 i = i + 1
56 table.insert(typefiles, (select(i, ...)))
57 elseif argi=='-h' then
58 i = i + 1
59 hppname = select(i, ...)
60 elseif argi=='-c' then
61 i = i + 1
62 cppname = select(i, ...)
63 elseif argi=='-o' then
64 i = i + 1
65 local name = select(i, ...)
66 cppname = name..'.cpp'
67 hppname = name..'.hpp'
68 else
69 filename = filename and error'duplicate filename' or argi
70 end
71 i = i + 1
72 end
73 dirname = dirname or ''
74 end
76 local readfile = function(fn)
77 local f = assert(io.open(fn))
78 local s = f:read'*a'
79 f:close()
80 return s
81 end
83 local fprint = function(f)
84 return function(...)
85 for i = 1, select('#',...) do
86 f:write((i==1) and '' or '\t', tostring(select(i,...)))
87 end
88 f:write'\n'
89 f:flush()
90 end
91 end
93 local debug = fprint(io.stderr)
94 local cpp, hpp = nil, nil
95 if cppname then
96 local cppfile = assert(io.open(dirname .. cppname, 'w'))
97 cpp = fprint(cppfile)
98 else
99 cpp = print
101 if hppname then
102 local hppfile = assert(io.open(dirname .. hppname, 'w'))
103 hpp = fprint(hppfile)
104 else
105 hpp = print
108 local xmlstream, idindex = dofile(path..'xml.lua')(readfile(filename))
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 string.match(a.xarg.defaultvalue, '%D') then
133 local dv = a.xarg.defaultvalue
134 if not fullnames[dv] then
135 dv = a.xarg.context..'::'..dv
137 if fullnames[dv] then
138 a.xarg.defaultvalue = dv
139 else
140 a.xarg.default = nil
141 a.xarg.defaultvalue = nil
145 return all
148 local fix_functions = function(index)
149 for f in pairs(index) do
150 local args = {}
151 for i, a in ipairs(f) do
152 -- avoid bogus 'void' arguments
153 if a.xarg.type_name=='void' and i==1 and f[2]==nil then break end
154 if a.label=='Argument' then
155 table.insert(args, a)
158 f.arguments = args
159 local is_constructor = function(f)
160 return (f.xarg.member_of_class and f.xarg.member_of_class~=''
161 and f.xarg.fullname==(f.xarg.member_of_class..'::'..f.xarg.name) -- this should be always true
162 and string.match(f.xarg.member_of_class, f.xarg.name..'$'))
164 if is_constructor(f) then
165 f.xarg.fullname = '*new '..f.xarg.fullname
166 f.return_type = f.xarg.type_base..'&'
167 f.xarg.static = '1'
168 elseif string.match(f.xarg.name, '~') or f.xarg.type_name=='void' then
169 f.return_type = nil
170 else
171 if false and f.xarg.access=='protected' then
172 local shellname = 'lqt_shell_'..string.gsub(f.parent.xarg.fullname, '::', '_LQT_')
173 f.xarg.fullname = shellname..'::'..f.xarg.name
174 if f.xarg.static~='1' then
175 f.xarg.static='1'
176 local newarg = { label='Argument', xarg = {
177 type_name = f.xarg.member_of_class..'*',
178 }, }
179 table.insert(args, newarg, 1)
182 f.return_type = f.xarg.type_name
185 return index
188 local copy_enums = function(index)
189 local ret = {}
190 for e in pairs(index) do
191 if e.label=='Enum'
192 and not string.match(e.xarg.fullname, '%b<>')
193 and e.xarg.access=='public' then
194 ret[e] = true
197 return ret
200 local fill_enums = function(index)
201 for e in pairs(index) do
202 local values = {}
203 for _, v in ipairs(e) do
204 if v.label=='Enumerator' then
205 table.insert(values, v)
208 e.values = values
210 return index
213 local copy_classes = function(index)
214 local ret = {}
215 for e in pairs(index) do
216 if e.label=='Class'
217 and e.xarg.access~='private'
218 and not (e.xarg.fullname:match'%b<>'
219 or e.xarg.fullname=='QDebug::Stream'
220 or e.xarg.fullname=='QForeachContainerBase'
221 or e.xarg.fullname=='QByteArray::Data'
222 or e.xarg.fullname=='QVariant::Private::Data'
223 or e.xarg.fullname=='QRegion::QRegionData'
224 or e.xarg.fullname=='QTextStreamManipulator'
225 or e.xarg.fullname=='QString::Data'
226 or e.xarg.fullname=='QThreadStorageData'
227 ) then
228 ret[e] = true
231 return ret
234 local fill_virtuals = function(index)
235 local classes = {}
236 for c in pairs(index) do
237 classes[c.xarg.fullname] = c
239 local get_virtuals
240 get_virtuals = function(c)
241 local ret = {}
242 for _, f in ipairs(c) do
243 if f.label=='Function' and f.xarg.virtual=='1' then
244 local n = string.match(f.xarg.name, '~') or f.xarg.name
245 if n~='~' then ret[n] = f end
248 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
249 local base = classes[b]
250 if type(base)=='table' then
251 local bv = get_virtuals(base)
252 for n, f in pairs(bv) do
253 if not ret[n] then ret[n] = f end
257 for _, f in ipairs(c) do
258 if f.label=='Function'
259 and f.xarg.access~='private'
260 and (ret[string.match(f.xarg.name, '~') or f.xarg.name]) then
261 f.xarg.virtual = '1'
262 local n = string.match(f.xarg.name, '~')or f.xarg.name
263 ret[n] = f
266 return ret
268 for c in pairs(index) do
269 c.virtuals = get_virtuals(c)
270 for _, f in pairs(c.virtuals) do
271 if f.xarg.abstract=='1' then c.abstract=true break end
274 return index
277 local distinguish_methods = function(index)
278 for c in pairs(index) do
279 local construct, destruct, normal = {}, nil, {}
280 local n = c.xarg.name
281 local copy = nil
282 for _, f in ipairs(c) do
283 if n==f.xarg.name then
284 table.insert(construct, f)
285 elseif f.xarg.name:match'~' then
286 destruct = f
287 else
288 if (not string.match(f.xarg.name, '^operator%W'))
289 and (not f.xarg.member_template_parameters) then
290 table.insert(normal, f)
294 c.constructors = construct
295 c.destructor = destruct
296 c.methods = normal
298 return index
301 local fill_public_destr = function(index)
302 local classes = {}
303 for c in pairs(index) do
304 classes[c.xarg.fullname] = c
306 local destr_is_public
307 destr_is_public = function(c)
308 if c.destructor then
309 return c.destructor.xarg.access=='public'
310 else
311 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
312 local base = classes[b]
313 if base and not destr_is_public(base) then
314 return false
317 return true
320 for c in pairs(index) do
321 c.public_destr = destr_is_public(c)
323 return index
326 local fill_copy_constructor = function(index)
327 local classes = {}
328 for c in pairs(index) do
329 classes[c.xarg.name] = c
331 for c in pairs(index) do
332 local copy = nil
333 for _, f in ipairs(c.constructors) do
334 if #(f.arguments)==1
335 and f.arguments[1].xarg.type_name==c.xarg.fullname..' const&' then
336 copy = f
337 break
340 c.copy_constructor = copy
342 local copy_constr_is_public
343 copy_constr_is_public = function(c)
344 if c.copy_constructor then
345 return (c.copy_constructor.xarg.access=='public')
346 or (c.copy_constructor.xarg.access=='protected')
347 else
348 local ret = nil
349 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
350 local base = classes[b]
351 if base and not copy_constr_is_public(base) then
352 return false
355 return true
358 for c in pairs(index) do
359 c.public_constr = copy_constr_is_public(c)
361 return index
364 local fill_typesystem_with_enums = function(enums, types)
365 local ret = {}
366 for e in pairs(enums) do
367 if not types[e.xarg.fullname] then
368 ret[e] = true
369 types[e.xarg.fullname] = {
370 push = function(n)
371 return 'lqtL_pushenum(L, '..n..', "'..e.xarg.fullname..'")', 1
372 end,
373 get = function(n)
374 return 'static_cast<'..e.xarg.fullname..'>'
375 ..'(lqtL_toenum(L, '..n..', "'..e.xarg.fullname..'"))', 1
376 end,
377 test = function(n)
378 return 'lqtL_isenum(L, '..n..', "'..e.xarg.fullname..'")', 1
379 end,
381 else
382 --io.stderr:write(e.xarg.fullname, ': already present\n')
385 return ret
388 local fill_typesystem_with_classes = function(classes, types)
389 local ret = {}
390 for c in pairs(classes) do
391 if not types[c.xarg.fullname] then
392 ret[c] = true
393 types[c.xarg.fullname..'*'] = {
394 -- the argument is a pointer to class
395 push = function(n)
396 return 'lqtL_passudata(L, '..n..', "'..c.xarg.fullname..'*")', 1
397 end,
398 get = function(n)
399 return 'static_cast<'..c.xarg.fullname..'*>'
400 ..'(lqtL_toudata(L, '..n..', "'..c.xarg.fullname..'*"))', 1
401 end,
402 test = function(n)
403 return 'lqtL_isudata(L, '..n..', "'..c.xarg.fullname..'*")', 1
404 end,
406 types[c.xarg.fullname..' const*'] = {
407 -- the argument is a pointer to constant class instance
408 push = function(n)
409 return 'lqtL_passudata(L, '..n..', "'..c.xarg.fullname..'*")', 1
410 end,
411 get = function(n)
412 return 'static_cast<'..c.xarg.fullname..'*>'
413 ..'(lqtL_toudata(L, '..n..', "'..c.xarg.fullname..'*"))', 1
414 end,
415 test = function(n)
416 return 'lqtL_isudata(L, '..n..', "'..c.xarg.fullname..'*")', 1
417 end,
419 types[c.xarg.fullname..'&'] = {
420 -- the argument is a reference to class
421 push = function(n)
422 return 'lqtL_passudata(L, &'..n..', "'..c.xarg.fullname..'*")', 1
423 end,
424 get = function(n)
425 return '*static_cast<'..c.xarg.fullname..'*>'
426 ..'(lqtL_toudata(L, '..n..', "'..c.xarg.fullname..'*"))', 1
427 end,
428 test = function(n)
429 return 'lqtL_isudata(L, '..n..', "'..c.xarg.fullname..'*")', 1
430 end,
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] = {
435 -- the argument is the class itself
436 push = function(n)
437 return 'lqtL_passudata(L, new '..shellname
438 ..'(L, '..n..'), "'..c.xarg.fullname..'*")', 1
439 end,
440 get = function(n)
441 return '*static_cast<'..c.xarg.fullname..'*>'
442 ..'(lqtL_toudata(L, '..n..', "'..c.xarg.fullname..'*"))', 1
443 end,
444 test = function(n)
445 return 'lqtL_isudata(L, '..n..', "'..c.xarg.fullname..'*")', 1
446 end,
448 types[c.xarg.fullname..' const&'] = {
449 -- the argument is a pointer to class
450 push = function(n)
451 return 'lqtL_passudata(L, new '..shellname
452 ..'(L, '..n..'), "'..c.xarg.fullname..'*")', 1
453 end,
454 get = function(n)
455 return '*static_cast<'..c.xarg.fullname..'*>'
456 ..'(lqtL_toudata(L, '..n..', "'..c.xarg.fullname..'*"))', 1
457 end,
458 test = function(n)
459 return 'lqtL_isudata(L, '..n..', "'..c.xarg.fullname..'*")', 1
460 end,
465 return ret
468 local fill_wrapper_code = function(f, types)
469 if f.wrapper_code then return f end
470 local stackn, argn = 1, 1
471 local wrap, line = '', ''
472 if f.xarg.member_of_class and f.xarg.static~='1' then
473 if not types[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
474 local sget, sn = types[f.xarg.member_of_class..'*'].get(stackn)
475 wrap = wrap .. ' ' .. f.xarg.member_of_class .. '* self = ' .. sget .. ';\n'
476 stackn = stackn + sn
477 wrap = wrap .. [[
478 if (NULL==self) {
479 lua_pushstring(L, "this pointer is NULL");
480 lua_error(L);
483 --print(sget, sn)
484 line = 'self->'..f.xarg.fullname..'('
485 else
486 line = f.xarg.fullname..'('
488 for i, a in ipairs(f.arguments) do
489 if not types[a.xarg.type_name] then return nil end
490 local aget, an = types[a.xarg.type_name].get(stackn)
491 wrap = wrap .. ' ' .. a.xarg.type_name .. ' arg' .. tostring(argn) .. ' = '
492 if a.xarg.default=='1' and an>0 then
493 wrap = wrap .. 'lua_isnoneornil(L, '..stackn..')'
494 for j = stackn+1,stackn+an-1 do
495 wrap = wrap .. ' && lua_isnoneornil(L, '..j..')'
497 local dv = a.xarg.defaultvalue
498 wrap = wrap .. ' ? static_cast< ' .. a.xarg.type_name .. ' >(' .. dv .. ') : '
500 wrap = wrap .. aget .. ';\n'
501 line = line .. (argn==1 and 'arg' or ', arg') .. argn
502 stackn = stackn + an
503 argn = argn + 1
505 line = line .. ')'
506 -- FIXME: hack follows for constructors
507 if f.calling_line then line = f.calling_line end
508 if f.return_type then line = f.return_type .. ' ret = ' .. line end
509 wrap = wrap .. ' ' .. line .. ';\n lua_settop(L, 0);\n' -- lua_pop(L, '..stackn..');\n'
510 if f.return_type then
511 if not types[f.return_type] then return nil end
512 local rput, rn = types[f.return_type].push'ret'
513 wrap = wrap .. ' luaL_checkstack(L, '..rn..', "cannot grow stack for return value");\n'
514 wrap = wrap .. ' '..rput..';\n return '..rn..';\n'
515 else
516 wrap = wrap .. ' return 0;\n'
518 f.wrapper_code = wrap
519 return f
522 local fill_test_code = function(f, types)
523 local stackn = 1
524 local test = ''
525 if f.xarg.member_of_class and f.xarg.static~='1' then
526 if not types[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
527 local stest, sn = types[f.xarg.member_of_class..'*'].test(stackn)
528 test = test .. ' && ' .. stest
529 stackn = stackn + sn
531 for i, a in ipairs(f.arguments) do
532 if not types[a.xarg.type_name] then return nil end -- print(a.xarg.type_name) return nil end
533 local atest, an = types[a.xarg.type_name].test(stackn)
534 if a.xarg.default=='1' and an>0 then
535 test = test .. ' && (lqtL_missarg(L, ' .. stackn .. ', ' .. an .. ') || '
536 test = test .. atest .. ')'
537 else
538 test = test .. ' && ' .. atest
540 stackn = stackn + an
542 -- can't make use of default values if I fix number of args
543 test = '(lua_gettop(L)<' .. stackn .. ')' .. test
544 f.test_code = test
545 return f
548 local fill_wrappers = function(functions, types)
549 local ret = {}
550 for f in pairs(functions) do
551 f = fill_wrapper_code(f, types)
552 if f then
553 f = assert(fill_test_code(f, types), f.xarg.fullname) -- MUST pass
554 ret[f] = true
555 local out = 'extern "C" int lqt_bind'..f.xarg.id..' (lua_State *L) {\n'
556 .. f.wrapper_code .. '}\n'
557 --print(out)
560 return ret
563 local argument_name = function(tn, an)
564 local ret
565 if string.match(tn, '%(%*%)') then
566 ret = string.gsub(tn, '%(%*%)', '(*'..an..')', 1)
567 elseif string.match(tn, '%[.*%]') then
568 ret = string.gsub(tn, '(%[.*%])', an..'%1')
569 else
570 ret = tn .. ' ' .. an
572 return ret
575 local virtual_overload = function(v, types)
576 local ret = ''
577 if v.virtual_overload then return v end
578 -- make return type
579 if v.return_type and not types[v.return_type] then return nil end
580 local rget, rn = '', 0
581 if v.return_type then rget, rn = types[v.return_type].get'oldtop+1' end
582 local retget = (v.return_type and argument_name(v.return_type, 'ret')
583 .. ' = ' .. rget .. ';' or '') .. 'lua_settop(L, oldtop);return'
584 .. (v.return_type and ' ret' or '')
585 -- make argument push
586 local pushlines, stack = '', 0
587 for i, a in ipairs(v.arguments) do
588 if not types[a.xarg.type_name] then return nil end
589 local apush, an = types[a.xarg.type_name].push('arg'..i)
590 pushlines = pushlines .. ' ' .. apush .. ';\n'
591 stack = stack + an
593 -- make lua call
594 local luacall = 'lua_pcall(L, '..stack..', '..rn..', 0)'
595 -- make prototype and fallback
596 local proto = (v.return_type or 'void')..' ;;'..v.xarg.name..' ('
597 local fallback = ''
598 for i, a in ipairs(v.arguments) do
599 proto = proto .. (i>1 and ', ' or '')
600 .. argument_name(a.xarg.type_name, 'arg'..i)
601 fallback = fallback .. (i>1 and ', arg' or 'arg') .. i
603 proto = proto .. ')' .. (v.xarg.constant=='1' and ' const' or '')
604 fallback = (v.return_type and 'return this->' or 'this->')
605 .. v.xarg.fullname .. '(' .. fallback .. ');\n}\n'
606 ret = proto .. [[ {
607 int oldtop = lua_gettop(L);
608 lqtL_pushudata(L, this, "]]..v.xarg.member_of_class..[[*");
609 lua_getfield(L, -1, "]]..v.xarg.name..[[");
610 if (lua_isfunction(L, -1)) {
611 lua_insert(L, -2);
612 ]] .. pushlines .. [[
613 if (]]..luacall..[[) {
614 ]]..retget..[[;
617 lua_settop(L, oldtop);
618 ]] .. fallback
619 v.virtual_overload = ret
620 v.virtual_proto = string.gsub(proto, ';;', '', 1)
621 return v
624 local fill_shell_class = function(c, types)
625 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
626 local shell = 'class ' .. shellname .. ' : public ' .. c.xarg.fullname .. ' {\npublic:\n'
627 shell = shell .. ' lua_State *L;\n'
628 for _, constr in ipairs(c.constructors) do
629 if constr.xarg.access~='private' then
630 local cline = ' '..shellname..' (lua_State *l'
631 local argline = ''
632 for i, a in ipairs(constr.arguments) do
633 cline = cline .. ', ' .. argument_name(a.xarg.type_name, 'arg'..i)
634 argline = argline .. (i>1 and ', arg' or 'arg') .. i
636 cline = cline .. ') : ' .. c.xarg.fullname
637 .. '(' .. argline .. '), L(l) '
638 .. '{ lqtL_register(L, this); }\n'
639 shell = shell .. cline
642 if c.copy_constructor==nil and c.public_constr then
643 local cline = ' '..shellname..' (lua_State *l, '..c.xarg.fullname..' const& arg1)'
644 cline = cline .. ' : ' .. c.xarg.fullname .. '(arg1), L(l) {}\n'
645 shell = shell .. cline
647 for i, v in pairs(c.virtuals) do
648 if v.xarg.access~='private' then
649 local vret = virtual_overload(v, types)
650 if v.virtual_proto then shell = shell .. ' virtual ' .. v.virtual_proto .. ';\n' end
653 shell = shell .. ' ~'..shellname..'() { lqtL_unregister(L, this); }\n'
654 shell = shell .. '};\n'
655 c.shell_class = shell
656 return c
659 local fill_shell_classes = function(classes, types)
660 local ret = {}
661 for c in pairs(classes) do
662 if c.shell then
663 c = fill_shell_class(c, types)
664 if c then ret[c] = true else error(c.xarg.fullname) end
667 return ret
670 local print_shell_classes = function(classes)
671 for c in pairs(classes) do
672 if c.shell then
673 if c then
674 hpp(c.shell_class)
675 else
676 --io.stderr:write(c.fullname, '\n')
680 return classes
683 local print_virtual_overloads = function(classes)
684 for c in pairs(classes) do
685 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
686 for _,v in pairs(c.virtuals) do
687 if v.virtual_overload then
688 cpp((string.gsub(v.virtual_overload, ';;', shellname..'::', 1)))
692 return classes
695 local print_wrappers = function(index)
696 for c in pairs(index) do
697 local meta = {}
698 for _, f in ipairs(c.methods) do
699 if f.wrapper_code then
700 local out = 'extern "C" int lqt_bind'..f.xarg.id
701 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
702 if f.xarg.access=='public' then
703 cpp(out)
704 meta[f] = f.xarg.name
708 if c.shell then
709 for _, f in ipairs(c.constructors) do
710 if f.wrapper_code then
711 local out = 'extern "C" int lqt_bind'..f.xarg.id
712 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
713 if f.xarg.access=='public' then
714 cpp(out)
715 meta[f] = 'new'
719 --local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
720 local out = 'extern "C" int lqt_delete'..c.xarg.id..' (lua_State *L) {\n'
721 out = out ..' '..c.xarg.fullname..' *p = static_cast<'
722 ..c.xarg.fullname..'*>(lqtL_toudata(L, 1, "'..c.xarg.fullname..'*"));\n'
723 out = out .. ' if (p) delete p;\n return 0;\n}\n'
724 cpp(out)
726 c.meta = meta
728 return index
731 local print_metatable = function(c)
732 local methods = {}
733 for m, n in pairs(c.meta) do
734 methods[n] = methods[n] or {}
735 table.insert(methods[n], m)
737 for n, l in pairs(methods) do
738 local disp = 'extern "C" int lqt_dispatcher_'..n..c.xarg.id..' (lua_State *L) {\n'
739 for _, f in ipairs(l) do
740 disp = disp..' if ('..f.test_code..') return lqt_bind'..f.xarg.id..'(L);\n'
742 disp = disp .. ' lua_settop(L, 0);\n'
743 disp = disp .. ' lua_pushstring(L, "incorrect or extra arguments");\n'
744 disp = disp .. ' return lua_error(L);\n}\n'
745 cpp(disp)
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 if c.shell then
752 metatable = metatable .. ' { "delete", lqt_delete'..c.xarg.id..' },\n'
754 metatable = metatable .. ' { 0, 0 },\n};\n'
755 cpp(metatable)
756 local bases = ''
757 for b in string.gmatch(c.xarg.bases or '', '([^;]*);') do
758 bases = bases .. '{"' .. b .. '*"}, '
760 bases = 'static lqt_Base lqt_base'..c.xarg.id..'[] = { '..bases..'{NULL} };\n'
761 cpp(bases)
762 return c
765 local print_metatables = function(classes)
766 for c in pairs(classes) do
767 print_metatable(c)
769 return classes
772 local print_class_list = function(classes)
773 local list = 'static lqt_Class lqt_class_list[] = {\n'
774 for c in pairs(classes) do
775 class = '{ lqt_metatable'..c.xarg.id..', lqt_base'..c.xarg.id..', "'..c.xarg.fullname..'*" },\n'
776 list = list .. ' ' .. class
778 list = list .. ' { 0, 0, 0 },\n};\n'
779 cpp(list)
780 return classes
783 local fix_methods_wrappers = function(classes)
784 for c in pairs(classes) do
785 -- if class seems abstract but has a shell class
786 if c.abstract then
787 -- is it really abstract?
788 local a = false
789 for _, f in pairs(c.virtuals) do
790 -- if it is abstract but we cannot overload
791 -- FIXME: this always fails: f.virtual_overload is not filled yet
792 -- maybe this check must be moved later:
793 -- we don't use shell class to move instances to Lua
794 -- but we want to instantiate if we can wrap all virtuals...
795 if f.xarg.abstract=='1' and not f.virtual_overload then a = true break end
797 c.abstract = a
799 c.shell = (not c.abstract) and c.public_destr
800 for _, constr in ipairs(c.constructors) do
801 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
802 constr.calling_line = '*new '..shellname..'(L'
803 for i=1,#(constr.arguments) do
804 constr.calling_line = constr.calling_line .. ', arg' .. i
806 constr.calling_line = constr.calling_line .. ')'
809 return classes
812 local print_enum_tables = function(enums)
813 for e in pairs(enums) do
814 local table = 'static lqt_Enum lqt_enum'..e.xarg.id..'[] = {\n'
815 --io.stderr:write(e.xarg.fullname, '\t', #e.values, '\n')
816 for _,v in pairs(e.values) do
817 table = table .. ' { "' .. v.xarg.name
818 .. '", static_cast<int>('..v.xarg.fullname..') },\n'
820 table = table .. ' { 0, 0 }\n'
821 table = table .. '};\n'
822 e.enum_table = table
823 cpp(table)
825 return enums
827 local print_enum_creator = function(enums)
828 local out = 'static lqt_Enumlist lqt_enum_list[] = {\n'
829 for e in pairs(enums) do
830 out = out..' { lqt_enum'..e.xarg.id..', "'..e.xarg.fullname..'" },\n'
832 out = out..' { 0, 0 },\n};\n'
833 out = out .. 'extern "C" int lqt_create_enums (lua_State *L) {\n'
834 out = out .. ' lqtL_createenumlist(L, lqt_enum_list); return 0;\n}\n'
835 cpp(out)
836 return enums
839 local print_openmodule = function(n)
840 cpp([[
842 extern "C" int luaopen_]]..n..[[ (lua_State *L) {
843 lqt_create_enums(L);
844 lqtL_createclasses(L, lqt_class_list);
845 return 0;
850 --------------------------------------------------------------------------------------
852 local typesystem = {}
854 local ts = {}
855 for i, ft in ipairs(typefiles) do
856 ts = loadfile(ft)(ts)
858 setmetatable(typesystem, {
859 __newindex = function(t, k, v)
860 --debug('added type', k)
861 ts[k] = v
862 end,
863 __index = function(t, k)
864 local ret = ts[k]
865 --if not ret then debug("unknown type:", tostring(k), ret) end
866 return ret
867 end,
871 fix_arguments(idindex) -- fixes default arguments if they are context-relative
872 local functions = copy_functions(idindex) -- picks functions and fixes label
873 local functions = fix_functions(functions) -- fixes name and fullname and fills arguments
875 local enums = copy_enums(idindex) -- picks enums if public
876 local enums = fill_enums(enums) -- fills field "values"
878 local classes = copy_classes(idindex) -- picks classes if not private and not blacklisted
879 local classes = fill_virtuals(classes) -- does that, destructor ("~") excluded
880 local classes = distinguish_methods(classes) -- does that
881 local classes = fill_public_destr(classes) -- does that: checks if destructor is public
882 local classes = fill_copy_constructor(classes) -- does that: checks if copy contructor is public or protected
883 local classes = fix_methods_wrappers(classes)
885 local enums = fill_typesystem_with_enums(enums, typesystem) -- does that
886 local classes = fill_typesystem_with_classes(classes, typesystem)
888 local functions = fill_wrappers(functions, typesystem)
889 local classes = fill_shell_classes(classes, typesystem) -- does that, also only selects those with a shell class
891 ------------- BEGIN OUTPUT
894 hpp('#ifndef LQT_BIND_'..module_name)
895 hpp('#define LQT_BIND_'..module_name)
896 hpp()
897 hpp()
898 for _, i in ipairs(output_includes) do
899 hpp('#include '..i)
901 hpp()
903 cpp('#include "'..tostring(hppname)..'"')
904 cpp()
905 cpp()
907 local classes = print_shell_classes(classes) -- does that
908 local classes = print_virtual_overloads(classes, typesystem) -- does that
909 local classes = print_wrappers(classes) -- does that + FIXME: checks if has shell for constr/destr and compiles metatable list
910 local enums = print_enum_tables(enums) -- does that
911 local enums = print_enum_creator(enums) -- does that + print enum list
912 local classes = print_metatables(classes) -- does that + print dispatchers
913 local classes = print_class_list(classes) -- does that
915 print_openmodule(module_name) -- does that
917 hpp('#endif // LQT_BIND_'..module_name)