added function for iterating over arguments
[lqt.git] / new / generator.lua
blob4179d51bab4c788bc177f98b460e81216be3f1ed
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 arg_iter = function(f)
29 local i = 0
30 local stackn = 1
31 local onlyargs = 0
32 return function()
33 local a, retn = {}, 0
34 while a and a.label~='Argument' do
35 i = i + 1
36 a = f[i]
37 end
38 retn = stackn
39 onlyargs = onlyargs + 1
40 if a then
41 local d, g, p, n = type_properties(a)
42 stackn = stackn + n
43 end
44 return (a and onlyargs), a, (a and retn)
45 end
46 end
48 local base_types = {}
49 assert(loadfile'types.lua')(base_types)
52 local t = {}
53 for _, v in pairs(xmlstream.byid) do if v.xarg.fullname then
54 local o = t[v.xarg.fullname] or {}
55 table.insert(o, v)
56 t[v.xarg.fullname] = o
57 end end
58 get_from_fullname = function(n)
59 local ret = t[n]
60 assert(ret, 'unknown identifier: '..n)
61 return ret
62 end
63 get_unique_fullname = function(n)
64 n = tostring(n)
65 local ret = t[n]
66 assert(ret, 'unknown identifier: '..n)
67 assert(type(ret)=='table' and #ret==1, 'ambiguous identifier: '..n)
68 return ret[1]
69 end
70 --name_list = t
71 end
74 local push_enum = function(fullname)
75 return function(j)
76 return 'lqtL_pushenum(L, '..tostring(j)..', "'..fullname..'");'
77 end
78 end
79 local push_pointer = function(fullname)
80 return function(j)
81 return 'lqtL_pushudata(L, '..tostring(j)..', "' .. fullname .. '*");'
82 end
83 end
84 local push_class = function(fullname)
85 return function(j)
86 return 'lqtL_passudata(L, new '..fullname..'('..tostring(j)..'), "' .. fullname .. '*");'
87 end
88 end
89 local push_constref = function(fullname) -- FIXME: is it correct?
90 return function(j)
91 return 'lqtL_passudata(L, new '..fullname..'('..tostring(j)..'), "' .. fullname .. '*");'
92 end
93 end
94 local push_ref = function(fullname)
95 return function(j)
96 return 'lqtL_passudata(L, &'..tostring(j)..', "' .. fullname .. '*");'
97 end
98 end
100 local get_enum = function(fullname)
101 return function(i)
102 return 'static_cast< ' ..
103 fullname .. ' >(lqtL_toenum(L, '..tostring(i)..', "' .. fullname .. '"));'
106 local get_pointer = function(fullname)
107 return function(i)
108 return 'static_cast< ' ..
109 fullname .. ' *>(lqtL_toudata(L, '..tostring(i)..', "' .. fullname .. '*"));'
112 local get_class = function(fullname)
113 return function(i)
114 return '*static_cast< ' ..
115 fullname .. ' *>(lqtL_toudata(L, '..tostring(i)..', "' .. fullname .. '*"));'
118 local get_constref = function(fullname)
119 return function(i)
120 return '*static_cast< ' ..
121 fullname .. ' *>(lqtL_toudata(L, '..tostring(i)..', "' .. fullname .. '*"));'
124 local get_ref = function(fullname)
125 return function(i)
126 return '*static_cast< ' ..
127 fullname .. ' *>(lqtL_toudata(L, '..tostring(i)..', "' .. fullname .. '*"));'
131 type_properties = function(t)
132 local typename = type(t)=='string' and t or t.xarg.type_name
134 if base_types[typename] then
135 local ret = base_types[typename]
136 return ret.desc, ret.get, ret.push, ret.num
139 -- not a base type
140 if type(t)=='string' or t.xarg.type_base==typename then
141 local identifier = get_unique_fullname(typename)
142 local fn = identifier.xarg.fullname
143 if identifier.label=='Enum' then
144 return 'string;', get_enum(fn), push_enum(fn), 1
145 elseif identifier.label=='Class' then
146 --assert(entities.class_is_copy_constructible(bt))
147 return typename..'*;', get_class(fn), push_class(fn), 1
148 else
149 error('unknown identifier type: '..identifier.label)
151 elseif t.xarg.array or t.xarg.type_name:match'%b[]' then -- FIXME: another hack
152 error'I cannot manipulate arrays'
153 elseif string.match(typename, '%(%*%)') then
154 -- function pointer type
155 -- FIXME: the XML description does not contain this info
156 error'I cannot manipulate function pointers'
157 elseif t.xarg.indirections then
158 if t.xarg.indirections=='1' then
159 local b = get_unique_fullname(t.xarg.type_base)
160 if b.label=='Class' then
161 -- TODO: check if other modifiers are in place?
162 return t.xarg.type_base..'*;',
163 get_pointer(t.xarg.type_base),
164 push_pointer(t.xarg.type_base), 1
165 else
166 error('I cannot manipulate pointers to '..t.xarg.type_base)
169 error'I cannot manipulate double pointers'
170 else
171 -- this is any combination of constant, volatile and reference
172 local ret_get, ret_push = nil, nil
173 if typename==(t.xarg.type_base..' const&') then
174 local bt = get_unique_fullname(t.xarg.type_base)
175 --assert(entities.class_is_copy_constructible(bt))
176 ret_get = get_constref(t.xarg.type_base)
177 ret_push = entities.class_is_copy_constructible(bt) and push_constref(t.xarg.type_base) or nil
178 elseif typename==(t.xarg.type_base..'&') then
179 ret_get = get_ref(t.xarg.type_base)
180 ret_push = push_ref(t.xarg.type_base)
182 assert(ret_get, 'cannot get non-base type '..typename..' from stack')
183 return type_properties(t.xarg.type_base), ret_get, ret_push, 1
187 entities.return_type = function(f)
188 assert_function(f)
189 if entities.is_destructor(f) then
190 return nil
191 elseif entities.is_constructor(f) then
192 -- FIXME: hack follows!
193 assert((f.xarg.type_name==f.xarg.type_base)
194 or (f.xarg.type_name==f.xarg.type_base..'&'), 'return type of constructor is strange')
195 f.xarg.type_name = f.xarg.type_base..'&'
196 f.xarg.reference='1'
197 return f
198 elseif f.xarg.type_name=='' or f.xarg.type_name=='void' then
199 return nil
200 else
201 return f
205 function_description = function(f)
206 assert_function(f)
207 local args_on_stack = '' -- arguments_on_stack(f) -- FIXME: use another method
208 return f.xarg.type_name .. ' ' .. f.xarg.fullname .. ' (' .. args_on_stack .. ')'..
209 (f.xarg.static=='1' and ' [static]' or '')..
210 (f.xarg.virtual=='1' and ' [virtual]' or '')..
211 (entities.is_constructor(f) and ' [constructor]' or '')..
212 (entities.is_destructor(f) and ' [destructor]' or '')..
213 ' [in ' .. tostring(f.xarg.member_of) .. ']'
216 local argument_number = function(f)
217 assert_function(f)
218 local narg = #f
219 if entities.is_destructor(f) then
220 narg = 1
221 elseif entities.is_constructor(f) then
222 elseif entities.takes_this_pointer then
223 narg = narg + 1
225 return narg
228 local argument_assert = function(f)
229 assert_function(f)
230 local narg = argument_number(f)
231 return 'luaL_checkany(L, '..tostring(narg)..')'
234 local argument = function(n)
235 return 'arg'..tostring(n)
238 local get_args = function(f, indent)
239 assert_function(f)
240 indent = indent or ' '
241 local ret, shift = '', 0
242 if entities.takes_this_pointer(f) then
243 shift = 1
244 ret = ret .. indent .. f.xarg.member_of_class .. '* self = '
245 ret = ret .. get_pointer(f.xarg.member_of_class)(1) .. ';\n' -- (void)self;\n'
247 for argi, a, stacki in arg_iter(f) do
248 local _d, g, _p, _n = type_properties(a)
249 ret = ret .. indent .. a.xarg.type_name .. ' ' .. argument(argi) .. ' = '
250 ret = ret .. g(stacki + shift) .. ';\n' -- .. '(void) '..argument(argi)..';\n'
252 return ret
255 local arg_list = function(f, pre)
256 assert_function(f)
257 if entities.is_destructor(f) then
258 return '(self)'
259 else
260 local ret = ''
261 for i in arg_iter(f) do
262 ret = ret .. ((i>1 or pre) and ', ' or '') .. argument(i)
264 pre = pre or ''
265 return '('..pre..ret..')'
269 local function_static_call = function(f)
270 assert_function(f)
271 if entities.is_destructor(f) then
272 return 'delete (self)'
273 elseif entities.is_constructor(f) then
274 return '*new lqt_shell_class' .. f.parent.xarg.id .. arg_list(f, 'L')
275 -- f.xarg.fullname..arg_list(f)
276 elseif entities.takes_this_pointer(f) then
277 return 'self->'..f.xarg.fullname..arg_list(f)
278 else
279 return f.xarg.fullname..arg_list(f)
283 local function_shell_call = function(f)
284 assert_function(f)
285 assert(f.xarg.member_of_class, 'not a shell class member')
286 if entities.is_destructor(f) then
287 return 'delete (self)'
288 elseif entities.is_constructor(f) then
289 return '*new lqt_shell_class' .. f.parent.xarg.id .. arg_list(f)
290 -- f.xarg.fullname..arg_list(f)
291 elseif f.xarg.access=='public' then
292 return function_static_call(f)
293 elseif entities.takes_this_pointer(f) then
294 return 'self->'..f.xarg.fullname..arg_list(f)
295 else
296 return f.xarg.fullname..arg_list(f)
300 local collect_return = function(f)
301 assert_function(f)
302 local ret_t = entities.return_type(f)
303 if not ret_t then
304 return ''
305 else
306 return ret_t.xarg.type_name .. ' ret = '
310 local give_back_return = function(f)
311 assert_function(f)
312 local ret_t = entities.return_type(f)
313 if not ret_t then
314 return ''
315 else
316 local _d, _g, p, _n = type_properties(ret_t)
317 return p'ret'
321 local return_statement = function(f)
322 assert_function(f)
323 local ret_t = entities.return_type(f)
324 if not ret_t then
325 return 'return 0'
326 else
327 local _d, _g, _p, n = type_properties(ret_t)
328 return 'return '..tostring(n)
332 -- TODO: constructors wait for deciding if shell class is needed
333 local calling_code = function(f)
334 assert_function(f)
335 local ret, indent = '', ' '
336 local argi = 0
338 ret = ret..indent..argument_assert(f)..';\n'
340 ret = ret..get_args(f, indent)
342 --if entities.is_constructor(f) then
343 --elseif entities.is_destructor(f) then
344 --else
346 --[[
347 local args = ''
348 for i = 1,#f do
349 args = args .. (i > 1 and ', ' or '') .. argument(i)
351 args = '('..args..')';
352 local call_line = f.xarg.fullname .. args .. ';\n'
353 local ret_type = entities.return_type(f)
354 if ret_type then
355 call_line = ret_type.xarg.type_name .. ' ret = ' .. call_line
356 local _d, _g, p, n = type_properties(ret_type)
357 call_line = call_line .. indent .. p'ret' .. '\n'
358 call_line = call_line .. indent .. 'return ' .. tostring(n) .. ';\n'
360 --]]
361 local call_line = function_static_call(f)
362 ret = ret .. indent .. collect_return(f) .. call_line .. ';\n'
363 local treat_return = give_back_return(f)
364 ret = treat_return and (ret..indent..treat_return..';\n') or ret
365 ret = ret .. indent .. return_statement(f) .. ';\n'
367 return ret
371 io.write[[
372 extern "C" {
373 #include <lua.h>
374 #include <lualib.h>
375 #include <lauxlib.h>
378 #include "lqt_common.hpp"
379 #include <QtGui>
381 #define lqtL_getinteger lua_tointeger
382 #define lqtL_getstring lua_tostring
383 #define lqtL_getnumber lua_tonumber
387 local CLASS_FILTERS = {
388 function(c) return c.xarg.fullname:match'%b<>' end,
389 function(c) return c.xarg.name:match'_' end,
390 --function(c) return c.xarg.fullname:match'Q.-Data' end,
391 function(c) return c.xarg.class_type=='struct' end,
392 function(c) return c.xarg.fullname=='QVariant::Private::Data' end,
393 function(c) return c.xarg.fullname=='QTextStreamManipulator' end,
395 local FUNCTIONS_FILTERS = {
396 function(f) return f.xarg.name:match'^[_%w]*'=='operator' end,
397 function(f) return f.xarg.fullname:match'%b<>' end,
398 function(f) return f.xarg.name:match'_' end,
399 function(f) return f.xarg.fullname:match'QInternal' end,
400 function(f) return f.xarg.access~='public' end,
401 function(f) return f.xarg.fullname=='QVariant::canConvert' end,
403 local filter_out = function(f, t)
404 local ret, msg, F = nil, next(t, nil)
405 while (not ret) and F do
406 ret = F(f) and msg
407 msg, F = next(t, msg)
409 return ret
412 local choose_function = function(f1, f2)
413 assert_function(f1)
414 assert_function(f2)
418 local function_proto = function(f)
419 assert_function(f)
420 local larg1, larg2 = '', ''
421 for i, a in arg_iter(f) do
422 if a.xarg.type_name=='void' then
423 larg1, larg2 = '', ''
424 break
426 larg1 = larg1 .. (i>1 and ', ' or '')
427 if string.match(a.xarg.type_name, '%(%*%)') then
428 larg1 = larg1 .. a.xarg.type_name:gsub('%(%*%)', '(*'..argument(i)..')')
429 elseif string.match(a.xarg.type_name, '%[.*%]') then
430 larg1 = larg1 .. a.xarg.type_name:gsub('(%[.*%])', argument(i)..'%1')
431 else
432 larg1 = larg1 .. a.xarg.type_name .. ' ' .. argument(i)
434 larg2 = larg2 .. (i>1 and ', ' or '') .. argument(i)
436 return larg1, larg2
439 local get_virtuals
440 get_virtuals = function(c)
441 assert(entities.is_class(c), 'not a class')
442 local ret, impl = {}, {}
443 for _, f in ipairs(c) do
444 if entities.is_function(f) and f.xarg.virtual=='1'
445 and not string.match(f.xarg.name, '~') then
446 table.insert(ret, f)
447 impl[f.xarg.name] = #ret
450 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
451 local bvirt = get_virtuals(get_unique_fullname(b))
452 for _, v in ipairs(bvirt) do
453 if not impl[v.xarg.name] then
454 table.insert(ret, v)
455 impl[v.xarg.name] = #ret
459 -- [[
460 for _, f in ipairs(c) do
461 if impl[f.xarg.name] and f.xarg.access~='private' then
462 ret[ impl[f.xarg.name] ] = f
465 --]]
466 return ret
469 local virtual_proto = function(f)
470 assert_function(f)
471 local ret = 'virtual '..f.xarg.type_name..' '..f.xarg.name..'('
472 local larg1, larg2 = function_proto(f)
473 ret = ret .. larg1 .. ')'
474 return ret
477 local virtual_body = function(f, n)
478 assert_function(f)
479 local ret = f.xarg.type_name..' '..n..'::'..f.xarg.name..'('
480 local larg1, larg2 = function_proto(f)
481 ret = ret .. larg1 .. [[) {
482 int oldtop = lua_gettop(L);
483 lqtL_pushudata(L, this, "]]..f.parent.xarg.fullname..[[*");
484 lua_getfield(L, -1, "]]..f.xarg.name..[[");
485 lua_insert(L, -2);
486 if (!lua_isnil(L, -2)) {
488 for i, a in arg_iter(f) do
489 local _d, _g, p, _n = type_properties(a)
490 ret = ret .. ' ' .. p(argument(i)) .. ';\n'
492 ret = ret .. [[
493 if (!lua_pcall(L, lua_gettop(L)-oldtop+1, LUA_MULTRET, 0)) {
495 if f.xarg.type_name=='void' then
496 ret = ret .. 'return;\n'
497 else
498 local _d, g, _p, _n = type_properties(f)
499 ret = ret .. g('oldtop+1') .. ';\n'
501 ret = ret .. [[
504 lua_settop(L, oldtop);
506 if f.xarg.abstract then
507 if f.xarg.type_name~='void' then
508 local dc
509 if f.xarg.type_name~=f.xarg.type_base then
510 dc = entities.default_constructor(f)
511 else
512 local st, err = pcall(get_unique_fullname, f.xarg.type_base)
513 dc = entities.default_constructor(st and err or f)
515 if not dc then return nil end
516 ret = ret .. 'return ' .. dc .. ';\n'
517 else
518 ret = ret .. 'return;\n'
520 else
521 if f.type_name~='void' then
522 ret = ret .. 'return this->' .. f.xarg.fullname .. '(' .. larg2 .. ');\n'
523 else
524 ret = ret .. 'this->' .. f.xarg.fullname .. '(' .. larg2 .. ');\n'
527 ret = ret .. '}\n'
528 return ret
531 local examine_class = function(c)
532 assert(entities.is_class(c), 'not a class')
533 local constr, destr = {}, nil
534 for _, f in ipairs(c) do
535 if entities.is_function(f) then
536 if entities.is_constructor(f) then
537 table.insert(constr, f)
538 elseif entities.is_destructor(f) then
539 assert(not destr, 'cannot have more than one destructor!')
540 destr = f
544 --[[
545 local public_f, protected_f, virtual_f, virt_prot_f, abstract_f = {}, {}, {}, {}, {}
546 for _, f in ipairs(c) do
547 if entities.is_function(f) then
548 if f.xarg.abstract=='1' then
549 table.insert(abstract_f, f)
550 elseif f.xarg.virtual=='1' and f.xarg.access=='protected' then
551 table.insert(virt_prot_f, f)
552 elseif f.xarg.virtual=='1' and f.xarg.access=='public' then
553 table.insert(virtual_f, f)
554 elseif f.xarg.virtual~='1' and f.xarg.access=='protected' then
555 table.insert(protected_f, f)
556 elseif f.xarg.virtual~='1' and f.xarg.access=='public' then
557 table.insert(public_f, f)
561 --]]
562 local cname = 'lqt_shell_class'..c.xarg.id
563 local ret = 'class '..cname..' : public '..c.xarg.fullname..' {\npublic:\n'
564 ret = ret .. 'lua_State *L;\n'
565 local onlyprivate = true
566 for _, f in ipairs(constr) do
567 if f.xarg.access~='private' then
568 local st, larg1, larg2 = pcall(function_proto, f)
569 --assert(larg1 and larg2, 'cannot reproduce prototype of function')
570 if st then
571 onlyprivate = false
572 larg1 = (larg1=='') and '' or (', '..larg1)
573 ret = ret .. cname .. '(lua_State *l'..larg1..'):'..c.xarg.fullname..'('
574 ret = ret .. larg2 .. '), L(l) {} // '..f.xarg.id..'\n'
578 if #constr==0 then
579 ret = ret .. cname .. '(lua_State *l):L(l) {} // automatic \n'
580 elseif onlyprivate then
581 error('cannot bind class: '..c.xarg.fullname..': it has only private constructors')
583 ret = ret .. 'virtual ~'..cname..'() { lqtL_unregister(L, this); }\n'
585 local virtuals = get_virtuals(c)
586 local ret2 = ''
587 for _, f in ipairs(virtuals) do
588 local st, bd = pcall(virtual_body, f, cname)
589 if st then
590 ret = ret .. virtual_proto(f) .. ';\n'
591 ret2 = ret2 .. bd .. '\n'
595 ret = ret .. '};\n' .. ret2
596 return ret
599 --[==[ ]=]
600 for _, v in pairs(xmlstream.byid) do
601 --if string.find(v.label, 'Function')==1 and v.xarg.virtual and v.xarg.abstract then io.stderr:write(v.xarg.fullname, '\n') end
602 if string.find(v.label, 'Function')==1 and (not filter_out(v, FUNCTIONS_FILTERS)) then
603 local status, err = pcall(function_description, v)
604 --io[status and 'stdout' or 'stderr']:write((status and '' or v.xarg.fullname..': ')..err..'\n')
605 if true or status then
606 local s, e = pcall(calling_code, v)
607 --io[s and 'stdout' or 'stderr']:write((s and ''
608 --or ('error calling '..v.xarg.fullname..': '))..e..(s and '' or '\n'))
609 if s then
610 io.stdout:write('extern "C" int bound_function'..v.xarg.id..' (lua_State *L) {\n')
611 io.stdout:write(e)
612 io.stdout:write('}\n') -- FIXME
613 else
614 io.stderr:write(e, '\n')
616 else
617 print(err)
619 --io[status and 'stdout' or 'stderr']:write((status and '' or v.xarg.fullname..': ')..err..'\n')
620 elseif false and v.label=='Class' and not filter_out(v, CLASS_FILTERS) then -- do not support templates yet
621 local st, ret = pcall(examine_class, v)
622 if st then print(ret) else io.stderr:write(ret, '\n') end
625 --table.foreach(name_list, print)
626 --]==]
629 local do_class = function(fn)
630 local c = get_unique_fullname(fn)
631 local ret = ''
632 ret = ret .. examine_class(c)
634 for _, f in pairs(c) do
635 local fret, s, e = '', pcall(calling_code, f)
636 if s and not filter_out(f, FUNCTIONS_FILTERS) then
637 fret = fret .. 'extern "C" int bound_function'..f.xarg.id..' (lua_State *L) {\n'
638 fret = fret .. e
639 fret = fret .. '}\n'
641 ret = ret .. fret
644 io.write(ret)
647 do_class'QObject'