virtual (public) functions get implemented
[lqt.git] / new / generator.lua
blobd5a5d36cff6e81e75f99a7e4dd6307dc6860e12e
1 #!/usr/bin/lua
3 local my = {
4 readfile = function(fn) local f = assert(io.open(fn)) local s = f:read'*a' f:close() return s end
7 local entities = dofile'entities.lua'
8 assert_function = function(f)
9 assert(entities.is_function(f), 'argument is not a function')
10 end
12 local filename = ...
13 local path = string.match(arg[0], '(.*/)[^%/]+') or ''
14 local xmlstream = dofile(path..'xml.lua')(my.readfile(filename))
15 local code = xmlstream[1]
17 local decompound = function(n)
18 -- test function pointer
19 local r, a = string.match(n, '(.-) %(%*%) (%b())')
20 if r and a then
21 -- only single arguments are supported
22 return 'function', r, string.match(a, '%(([^,]*)%))')
23 end
24 return nil
25 end
28 local base_types = {}
29 assert(loadfile'types.lua')(base_types)
32 local t = {}
33 for _, v in pairs(xmlstream.byid) do if v.xarg.fullname then
34 local o = t[v.xarg.fullname] or {}
35 table.insert(o, v)
36 t[v.xarg.fullname] = o
37 end end
38 get_from_fullname = function(n)
39 local ret = t[n]
40 assert(ret, 'unknown identifier: '..n)
41 return ret
42 end
43 get_unique_fullname = function(n)
44 n = tostring(n)
45 local ret = t[n]
46 assert(ret, 'unknown identifier: '..n)
47 assert(type(ret)=='table' and #ret==1, 'ambiguous identifier: '..n)
48 return ret[1]
49 end
50 --name_list = t
51 end
54 local push_enum = function(fullname)
55 return function(j)
56 return 'lqtL_pushenum(L, '..tostring(j)..', "'..fullname..'");'
57 end
58 end
59 local push_pointer = function(fullname)
60 return function(j)
61 return 'lqtL_pushudata(L, '..tostring(j)..', "' .. fullname .. '*");'
62 end
63 end
64 local push_class = function(fullname)
65 return function(j)
66 return 'lqtL_passudata(L, new '..fullname..'('..tostring(j)..'), "' .. fullname .. '*");'
67 end
68 end
69 local push_constref = function(fullname) -- FIXME: is it correct?
70 return function(j)
71 return 'lqtL_passudata(L, new '..fullname..'('..tostring(j)..'), "' .. fullname .. '*");'
72 end
73 end
74 local push_ref = function(fullname)
75 return function(j)
76 return 'lqtL_passudata(L, &'..tostring(j)..', "' .. fullname .. '*");'
77 end
78 end
80 local get_enum = function(fullname)
81 return function(i)
82 return 'static_cast< ' ..
83 fullname .. ' >(lqtL_toenum(L, '..tostring(i)..', "' .. fullname .. '"));'
84 end
85 end
86 local get_pointer = function(fullname)
87 return function(i)
88 return 'static_cast< ' ..
89 fullname .. ' *>(lqtL_toudata(L, '..tostring(i)..', "' .. fullname .. '*"));'
90 end
91 end
92 local get_class = function(fullname)
93 return function(i)
94 return '*static_cast< ' ..
95 fullname .. ' *>(lqtL_toudata(L, '..tostring(i)..', "' .. fullname .. '*"));'
96 end
97 end
98 local get_constref = function(fullname)
99 return function(i)
100 return '*static_cast< ' ..
101 fullname .. ' *>(lqtL_toudata(L, '..tostring(i)..', "' .. fullname .. '*"));'
104 local get_ref = function(fullname)
105 return function(i)
106 return '*static_cast< ' ..
107 fullname .. ' *>(lqtL_toudata(L, '..tostring(i)..', "' .. fullname .. '*"));'
111 type_properties = function(t)
112 local typename = type(t)=='string' and t or t.xarg.type_name
114 if base_types[typename] then
115 local ret = base_types[typename]
116 return ret.desc, ret.get, ret.push, ret.num
119 -- not a base type
120 if type(t)=='string' or t.xarg.type_base==typename then
121 local identifier = get_unique_fullname(typename)
122 local fn = identifier.xarg.fullname
123 if identifier.label=='Enum' then
124 return 'string;', get_enum(fn), push_enum(fn), 1
125 elseif identifier.label=='Class' then
126 --assert(entities.class_is_copy_constructible(bt))
127 return typename..'*;', get_class(fn), push_class(fn), 1
128 else
129 error('unknown identifier type: '..identifier.label)
131 elseif t.xarg.array or t.xarg.type_name:match'%b[]' then -- FIXME: another hack
132 error'I cannot manipulate arrays'
133 elseif string.match(typename, '%(%*%)') then
134 -- function pointer type
135 -- FIXME: the XML description does not contain this info
136 error'I cannot manipulate function pointers'
137 elseif t.xarg.indirections then
138 if t.xarg.indirections=='1' then
139 local b = get_unique_fullname(t.xarg.type_base)
140 if b.label=='Class' then
141 -- TODO: check if other modifiers are in place?
142 return t.xarg.type_base..'*;',
143 get_pointer(t.xarg.type_base),
144 push_pointer(t.xarg.type_base), 1
145 else
146 error('I cannot manipulate pointers to '..t.xarg.type_base)
149 error'I cannot manipulate double pointers'
150 else
151 -- this is any combination of constant, volatile and reference
152 local ret_get, ret_push = nil, nil
153 if typename==(t.xarg.type_base..' const&') then
154 local bt = get_unique_fullname(t.xarg.type_base)
155 --assert(entities.class_is_copy_constructible(bt))
156 ret_get = get_constref(t.xarg.type_base)
157 ret_push = entities.class_is_copy_constructible(bt) and push_constref(t.xarg.type_base) or nil
158 elseif typename==(t.xarg.type_base..'&') then
159 ret_get = get_ref(t.xarg.type_base)
160 ret_push = push_ref(t.xarg.type_base)
162 assert(ret_get, 'cannot get non-base type '..typename..' from stack')
163 return type_properties(t.xarg.type_base), ret_get, ret_push, 1
167 entities.return_type = function(f)
168 assert_function(f)
169 if entities.is_destructor(f) then
170 return nil
171 elseif entities.is_constructor(f) then
172 -- FIXME: hack follows!
173 assert(f.xarg.type_name==f.xarg.type_base, 'return type of constructor is strange')
174 f.xarg.type_name = f.xarg.type_base..'&'
175 f.xarg.reference='1'
176 return f
177 elseif f.xarg.type_name=='' or f.xarg.type_name=='void' then
178 return nil
179 else
180 return f
184 function_description = function(f)
185 assert_function(f)
186 local args_on_stack = '' -- arguments_on_stack(f) -- FIXME: use another method
187 return f.xarg.type_name .. ' ' .. f.xarg.fullname .. ' (' .. args_on_stack .. ')'..
188 (f.xarg.static=='1' and ' [static]' or '')..
189 (f.xarg.virtual=='1' and ' [virtual]' or '')..
190 (entities.is_constructor(f) and ' [constructor]' or '')..
191 (entities.is_destructor(f) and ' [destructor]' or '')..
192 ' [in ' .. tostring(f.xarg.member_of) .. ']'
195 local argument_number = function(f)
196 assert_function(f)
197 local narg = #f
198 if entities.is_destructor(f) then
199 narg = 1
200 elseif entities.is_constructor(f) then
201 elseif entities.takes_this_pointer then
202 narg = narg + 1
204 return narg
207 local argument_assert = function(f)
208 assert_function(f)
209 local narg = argument_number(f)
210 return 'luaL_checkany(L, '..tostring(narg)..')'
213 local argument = function(n)
214 return 'arg'..tostring(n)
217 local get_args = function(f, indent)
218 assert_function(f)
219 indent = indent or ' '
220 local ret, argi, shift = '', 0, 0
221 if entities.takes_this_pointer(f) then
222 shift = 1
223 ret = ret .. indent .. f.xarg.member_of_class .. '* self = '
224 ret = ret .. get_pointer(f.xarg.member_of_class)(1) .. ';\n' -- (void)self;\n'
226 for _,a in ipairs(f) do if a.label=='Argument' then
227 argi = argi + 1
228 local _d, g, _p, _n = type_properties(a)
229 ret = ret .. indent .. a.xarg.type_name .. ' '..argument(argi) .. ' = '
230 ret = ret .. g(argi + shift) .. ';\n' -- .. '(void) '..argument(argi)..';\n'
231 else error'element in function is not argument'
232 end end
233 return ret
236 local arg_list = function(f)
237 assert_function(f)
238 if entities.is_destructor(f) then
239 return '(self)'
240 else
241 local ret = ''
242 for i = 1, #f do
243 ret = ret .. (i>1 and ', ' and '') .. argument(i)
245 return '('..ret..')'
249 local function_static_call = function(f)
250 assert_function(f)
251 if entities.is_destructor(f) then
252 return 'delete (self)'
253 elseif entities.is_constructor(f) then
254 return '*new '..f.xarg.fullname..arg_list(f)
255 elseif entities.takes_this_pointer(f) then
256 return 'self->'..f.xarg.fullname..arg_list(f)
257 else
258 return f.xarg.fullname..arg_list(f)
262 local function_shell_call = function(f)
263 assert_function(f)
264 assert(f.xarg.member_of_class, 'not a shell class member')
265 if entities.is_destructor(f) then
266 return 'delete (self)'
267 elseif entities.is_constructor(f) then
268 return '*new '..f.xarg.fullname..arg_list(f)
269 elseif f.xarg.access=='public' then
270 return function_static_call(f)
271 elseif entities.takes_this_pointer(f) then
272 return 'self->'..f.xarg.fullname..arg_list(f)
273 else
274 return f.xarg.fullname..arg_list(f)
278 local collect_return = function(f)
279 assert_function(f)
280 local ret_t = entities.return_type(f)
281 if not ret_t then
282 return ''
283 else
284 return ret_t.xarg.type_name .. ' ret = '
288 local give_back_return = function(f)
289 assert_function(f)
290 local ret_t = entities.return_type(f)
291 if not ret_t then
292 return ''
293 else
294 local _d, _g, p, _n = type_properties(ret_t)
295 return p'ret'
299 local return_statement = function(f)
300 assert_function(f)
301 local ret_t = entities.return_type(f)
302 if not ret_t then
303 return 'return 0'
304 else
305 local _d, _g, _p, n = type_properties(ret_t)
306 return 'return '..tostring(n)
310 -- TODO: constructors wait for deciding if shell class is needed
311 local calling_code = function(f)
312 assert_function(f)
313 local ret, indent = '', ' '
314 local argi = 0
316 ret = ret..indent..argument_assert(f)..';\n'
318 ret = ret..get_args(f, indent)
320 --if entities.is_constructor(f) then
321 --elseif entities.is_destructor(f) then
322 --else
324 --[[
325 local args = ''
326 for i = 1,#f do
327 args = args .. (i > 1 and ', ' or '') .. argument(i)
329 args = '('..args..')';
330 local call_line = f.xarg.fullname .. args .. ';\n'
331 local ret_type = entities.return_type(f)
332 if ret_type then
333 call_line = ret_type.xarg.type_name .. ' ret = ' .. call_line
334 local _d, _g, p, n = type_properties(ret_type)
335 call_line = call_line .. indent .. p'ret' .. '\n'
336 call_line = call_line .. indent .. 'return ' .. tostring(n) .. ';\n'
338 --]]
339 local call_line = function_static_call(f)
340 ret = ret .. indent .. collect_return(f) .. call_line .. ';\n'
341 local treat_return = give_back_return(f)
342 ret = treat_return and (ret..indent..treat_return..';\n') or ret
343 ret = ret .. indent .. return_statement(f) .. ';\n'
345 return ret
349 io.write[[
350 extern "C" {
351 #include <lua.h>
352 #include <lualib.h>
353 #include <lauxlib.h>
356 #include "lqt_common.hpp"
357 #include <QtGui>
359 #define lqtL_getinteger lua_tointeger
360 #define lqtL_getstring lua_tostring
361 #define lqtL_getnumber lua_tonumber
365 local CLASS_FILTERS = {
366 function(c) return c.xarg.fullname:match'%b<>' end,
367 function(c) return c.xarg.name:match'_' end,
368 --function(c) return c.xarg.fullname:match'Q.-Data' end,
369 function(c) return c.xarg.class_type=='struct' end,
370 function(c) return c.xarg.fullname=='QVariant::Private::Data' end,
371 function(c) return c.xarg.fullname=='QTextStreamManipulator' end,
373 local FUNCTIONS_FILTERS = {
374 function(f) return f.xarg.name:match'^[_%w]*'=='operator' end,
375 function(f) return f.xarg.fullname:match'%b<>' end,
376 function(f) return f.xarg.name:match'_' end,
377 function(f) return f.xarg.fullname:match'QInternal' end,
378 function(f) return f.xarg.access~='public' end,
380 local filter_out = function(f, t)
381 local ret, msg, F = nil, next(t, nil)
382 while (not ret) and F do
383 ret = F(f) and msg
384 msg, F = next(t, msg)
386 return ret
389 local choose_function = function(f1, f2)
390 assert_function(f1)
391 assert_function(f2)
395 local function_proto = function(f)
396 assert_function(f)
397 local larg1, larg2 = '', ''
398 for i = 1, #f do
399 local a = f[i]
400 if a.label~='Argument' then error(a.label) end
401 if a.xarg.type_name=='void' then
402 larg1, larg2 = nil, nil
403 break
405 larg1 = larg1 .. (i>1 and ', ' or '')
406 if string.match(a.xarg.type_name, '%(%*%)') then
407 larg1 = larg1 .. a.xarg.type_name:gsub('%(%*%)', '(*'..argument(i)..')')
408 elseif string.match(a.xarg.type_name, '%[.*%]') then
409 larg1 = larg1 .. a.xarg.type_name:gsub('(%[.*%])', argument(i)..'%1')
410 else
411 larg1 = larg1 .. a.xarg.type_name .. ' ' .. argument(i)
413 larg2 = larg2 .. (i>1 and ', ' or '') .. argument(i)
415 return larg1, larg2
418 local get_virtuals
419 get_virtuals = function(c)
420 assert(entities.is_class(c), 'not a class')
421 local ret, impl = {}, {}
422 for _, f in ipairs(c) do
423 if entities.is_function(f) and f.xarg.virtual=='1'
424 and not string.match(f.xarg.name, '~') then
425 table.insert(ret, f)
426 impl[f.xarg.name] = #ret
429 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
430 local bvirt = get_virtuals(get_unique_fullname(b))
431 for _, v in ipairs(bvirt) do
432 if not impl[v.xarg.name] then
433 table.insert(ret, v)
434 impl[v.xarg.name] = #ret
438 -- [[
439 for _, f in ipairs(c) do
440 if impl[f.xarg.name] and f.xarg.access~='private' then
441 ret[ impl[f.xarg.name] ] = f
444 --]]
445 return ret
448 local virtual_proto = function(f)
449 assert_function(f)
450 local ret = 'virtual '..f.xarg.type_name..' '..f.xarg.name..'('
451 local larg1, larg2 = function_proto(f)
452 ret = ret .. larg1 .. ')'
453 return ret
456 local virtual_body = function(f, n)
457 assert_function(f)
458 local ret = f.xarg.type_name..' '..n..'::'..f.xarg.name..'('
459 local larg1, larg2 = function_proto(f)
460 ret = ret .. larg1 .. [[) {
461 int oldtop = lua_gettop(L);
462 lqtL_pushudata(L, this, "]]..f.parent.xarg.fullname..[[*");
463 lua_getfield(L, -1, "]]..f.xarg.name..[[");
464 lua_insert(L, -2);
465 if (!lua_isnil(L, -2)) {
467 for i, a in ipairs(f) do
468 local _d, _g, p, _n = type_properties(a)
469 ret = ret .. ' ' .. p(argument(i)) .. ';\n'
471 ret = ret .. [[
472 if (!lua_pcall(L, lua_gettop(L)-oldtop+1, LUA_MULTRET, 0)) {
474 if f.xarg.type_name=='void' then
475 ret = ret .. 'return;'
476 else
477 local _d, g, _p, _n = type_properties(f)
478 ret = ret .. g('oldtop+1') .. ';\n'
480 ret = ret .. [[
483 lua_settop(L, oldtop);
485 if f.xarg.abstract then
486 if f.xarg.type_name~='void' then
487 local dc
488 if f.xarg.type_name~=f.xarg.type_base then
489 dc = entities.default_constructor(f)
490 else
491 local st, err = pcall(get_unique_fullname, f.xarg.type_base)
492 dc = entities.default_constructor(st and err or f)
494 if not dc then return nil end
495 ret = ret .. 'return ' .. dc .. ';\n'
496 else
497 ret = ret .. 'return;\n'
499 else
500 if f.type_name~='void' then
501 ret = ret .. 'return this->' .. f.xarg.fullname .. '(' .. larg2 .. ');\n'
502 else
503 ret = ret .. 'this->' .. f.xarg.fullname .. '(' .. larg2 .. ');\n'
506 ret = ret .. '}\n'
507 return ret
510 local examine_class = function(c)
511 assert(entities.is_class(c), 'not a class')
512 local constr, destr = {}, nil
513 for _, f in ipairs(c) do
514 if entities.is_function(f) then
515 if entities.is_constructor(f) then
516 table.insert(constr, f)
517 elseif entities.is_destructor(f) then
518 assert(not destr, 'cannot have more than one destructor!')
519 destr = f
523 --[[
524 local public_f, protected_f, virtual_f, virt_prot_f, abstract_f = {}, {}, {}, {}, {}
525 for _, f in ipairs(c) do
526 if entities.is_function(f) then
527 if f.xarg.abstract=='1' then
528 table.insert(abstract_f, f)
529 elseif f.xarg.virtual=='1' and f.xarg.access=='protected' then
530 table.insert(virt_prot_f, f)
531 elseif f.xarg.virtual=='1' and f.xarg.access=='public' then
532 table.insert(virtual_f, f)
533 elseif f.xarg.virtual~='1' and f.xarg.access=='protected' then
534 table.insert(protected_f, f)
535 elseif f.xarg.virtual~='1' and f.xarg.access=='public' then
536 table.insert(public_f, f)
540 --]]
541 local cname = 'lqt_shell_class'..c.xarg.id
542 local ret = 'class '..cname..' : public '..c.xarg.fullname..' {\npublic:\n'
543 ret = ret .. 'lua_State *L;\n'
544 local onlyprivate = true
545 for _, f in ipairs(constr) do
546 if f.xarg.access~='private' then
547 onlyprivate = false
548 local larg1, larg2 = function_proto(f)
549 assert(larg1 and larg2, 'cannot reproduce prototype of function')
550 larg1 = (larg1=='') and '' or (', '..larg1)
551 ret = ret .. cname .. '(lua_State *l'..larg1..'):'..c.xarg.fullname..'('
552 ret = ret .. larg2 .. '), L(l) {} // '..f.xarg.id..'\n'
555 if #constr==0 then
556 ret = ret .. cname .. '(lua_State *l):L(l) {} // automatic \n'
557 elseif onlyprivate then
558 error('cannot bind class: '..c.xarg.fullname..': it has only private constructors')
560 ret = ret .. 'virtual ~'..cname..'() { lqtL_unregister(L, this); }\n'
562 local virtuals = get_virtuals(c)
563 local ret2 = ''
564 for _, f in ipairs(virtuals) do
565 local st, bd = pcall(virtual_body, f, cname)
566 if st then
567 ret = ret .. virtual_proto(f) .. ';\n'
568 ret2 = ret2 .. bd .. '\n'
572 ret = ret .. '};\n' .. ret2
573 return ret
576 for _, v in pairs(xmlstream.byid) do
577 --if string.find(v.label, 'Function')==1 and v.xarg.virtual and v.xarg.abstract then io.stderr:write(v.xarg.fullname, '\n') end
578 if false and string.find(v.label, 'Function')==1 and (not filter_out(v, FUNCTIONS_FILTERS)) then
579 local status, err = pcall(function_description, v)
580 --io[status and 'stdout' or 'stderr']:write((status and '' or v.xarg.fullname..': ')..err..'\n')
581 if true or status then
582 local s, e = pcall(calling_code, v)
583 --io[s and 'stdout' or 'stderr']:write((s and ''
584 --or ('error calling '..v.xarg.fullname..': '))..e..(s and '' or '\n'))
585 if s then
586 io.stdout:write('extern "C" int bound_function'..v.xarg.id..' (lua_State *L) {\n')
587 io.stdout:write(e)
588 io.stdout:write('}\n') -- FIXME
590 else
591 print(err)
593 --io[status and 'stdout' or 'stderr']:write((status and '' or v.xarg.fullname..': ')..err..'\n')
594 elseif v.label=='Class' and not filter_out(v, CLASS_FILTERS) then -- do not support templates yet
595 local st, ret = pcall(examine_class, v)
596 if st then print(ret) else io.stderr:write(ret, '\n') end
599 --table.foreach(name_list, print)