added function_proto, which creates argument lists
[lqt.git] / new / generator.lua
blobe7522d757b5adb4b0373c258d4f1b9bd31af083e
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 return typename..'*;', get_class(fn), push_class(fn), 1
127 else
128 error('unknown identifier type: '..identifier.label)
130 elseif t.xarg.array or t.xarg.type_name:match'%b[]' then -- FIXME: another hack
131 error'I cannot manipulate arrays'
132 elseif string.match(typename, '%(%*%)') then
133 -- function pointer type
134 -- FIXME: the XML description does not contain this info
135 error'I cannot manipulate function pointers'
136 elseif t.xarg.indirections then
137 if t.xarg.indirections=='1' then
138 local b = get_unique_fullname(t.xarg.type_base)
139 if b.label=='Class' then
140 -- TODO: check if other modifiers are in place?
141 return t.xarg.type_base..'*;',
142 get_pointer(t.xarg.type_base),
143 push_pointer(t.xarg.type_base), 1
144 else
145 error('I cannot manipulate pointers to '..t.xarg.type_base)
148 error'I cannot manipulate double pointers'
149 else
150 -- this is any combination of constant, volatile and reference
151 local ret_get, ret_push = nil, nil
152 if typename==(t.xarg.type_base..' const&') then
153 local bt = get_unique_fullname(t.xarg.type_base)
154 --assert(entities.class_is_copy_constructible(bt))
155 ret_get = get_constref(t.xarg.type_base)
156 ret_push = push_constref(t.xarg.type_base)
157 elseif typename==(t.xarg.type_base..'&') then
158 ret_get = get_ref(t.xarg.type_base)
159 ret_push = push_ref(t.xarg.type_base)
161 assert(ret_get, 'cannot get non-base type '..typename..' from stack')
162 return type_properties(t.xarg.type_base), ret_get, ret_push, 1
166 entities.return_type = function(f)
167 assert_function(f)
168 if entities.is_destructor(f) then
169 return nil
170 elseif entities.is_constructor(f) then
171 -- FIXME: hack follows!
172 assert(f.xarg.type_name==f.xarg.type_base, 'return type of constructor is strange')
173 f.xarg.type_name = f.xarg.type_base..'&'
174 f.xarg.reference='1'
175 return f
176 elseif f.xarg.type_name=='' or f.xarg.type_name=='void' then
177 return nil
178 else
179 return f
183 function_description = function(f)
184 assert_function(f)
185 local args_on_stack = '' -- arguments_on_stack(f) -- FIXME: use another method
186 return f.xarg.type_name .. ' ' .. f.xarg.fullname .. ' (' .. args_on_stack .. ')'..
187 (f.xarg.static=='1' and ' [static]' or '')..
188 (f.xarg.virtual=='1' and ' [virtual]' or '')..
189 (entities.is_constructor(f) and ' [constructor]' or '')..
190 (entities.is_destructor(f) and ' [destructor]' or '')..
191 ' [in ' .. tostring(f.xarg.member_of) .. ']'
194 local argument_number = function(f)
195 assert_function(f)
196 local narg = #f
197 if entities.is_destructor(f) then
198 narg = 1
199 elseif entities.is_constructor(f) then
200 elseif entities.takes_this_pointer then
201 narg = narg + 1
203 return narg
206 local argument_assert = function(f)
207 assert_function(f)
208 local narg = argument_number(f)
209 return 'luaL_checkany(L, '..tostring(narg)..')'
212 local argument = function(n)
213 return 'arg'..tostring(n)
216 local get_args = function(f, indent)
217 assert_function(f)
218 indent = indent or ' '
219 local ret, argi, shift = '', 0, 0
220 if entities.takes_this_pointer(f) then
221 shift = 1
222 ret = ret .. indent .. f.xarg.member_of_class .. '* self = '
223 ret = ret .. get_pointer(f.xarg.member_of_class)(1) .. ';\n' -- (void)self;\n'
225 for _,a in ipairs(f) do if a.label=='Argument' then
226 argi = argi + 1
227 local _d, g, _p, _n = type_properties(a)
228 ret = ret .. indent .. a.xarg.type_name .. ' '..argument(argi) .. ' = '
229 ret = ret .. g(argi + shift) .. ';\n' -- .. '(void) '..argument(argi)..';\n'
230 else error'element in function is not argument'
231 end end
232 return ret
235 local arg_list = function(f)
236 assert_function(f)
237 if entities.is_destructor(f) then
238 return '(self)'
239 else
240 local ret = ''
241 for i = 1, #f do
242 ret = ret .. (i>1 and ', ' and '') .. argument(i)
244 return '('..ret..')'
248 local function_static_call = function(f)
249 assert_function(f)
250 if entities.is_destructor(f) then
251 return 'delete (self)'
252 elseif entities.is_constructor(f) then
253 return '*new '..f.xarg.fullname..arg_list(f)
254 elseif entities.takes_this_pointer(f) then
255 return 'self->'..f.xarg.fullname..arg_list(f)
256 else
257 return f.xarg.fullname..arg_list(f)
261 local function_shell_call = function(f)
262 assert_function(f)
263 assert(f.xarg.member_of_class, 'not a shell class member')
264 if entities.is_destructor(f) then
265 return 'delete (self)'
266 elseif entities.is_constructor(f) then
267 return '*new '..f.xarg.fullname..arg_list(f)
268 elseif f.xarg.access=='public' then
269 return function_static_call(f)
270 elseif entities.takes_this_pointer(f) then
271 return 'self->'..f.xarg.fullname..arg_list(f)
272 else
273 return f.xarg.fullname..arg_list(f)
277 local collect_return = function(f)
278 assert_function(f)
279 local ret_t = entities.return_type(f)
280 if not ret_t then
281 return ''
282 else
283 return ret_t.xarg.type_name .. ' ret = '
287 local give_back_return = function(f)
288 assert_function(f)
289 local ret_t = entities.return_type(f)
290 if not ret_t then
291 return ''
292 else
293 local _d, _g, p, _n = type_properties(ret_t)
294 return p'ret'
298 local return_statement = function(f)
299 assert_function(f)
300 local ret_t = entities.return_type(f)
301 if not ret_t then
302 return 'return 0'
303 else
304 local _d, _g, _p, n = type_properties(ret_t)
305 return 'return '..tostring(n)
309 -- TODO: constructors wait for deciding if shell class is needed
310 local calling_code = function(f)
311 assert_function(f)
312 local ret, indent = '', ' '
313 local argi = 0
315 ret = ret..indent..argument_assert(f)..';\n'
317 ret = ret..get_args(f, indent)
319 --if entities.is_constructor(f) then
320 --elseif entities.is_destructor(f) then
321 --else
323 --[[
324 local args = ''
325 for i = 1,#f do
326 args = args .. (i > 1 and ', ' or '') .. argument(i)
328 args = '('..args..')';
329 local call_line = f.xarg.fullname .. args .. ';\n'
330 local ret_type = entities.return_type(f)
331 if ret_type then
332 call_line = ret_type.xarg.type_name .. ' ret = ' .. call_line
333 local _d, _g, p, n = type_properties(ret_type)
334 call_line = call_line .. indent .. p'ret' .. '\n'
335 call_line = call_line .. indent .. 'return ' .. tostring(n) .. ';\n'
337 --]]
338 local call_line = function_static_call(f)
339 ret = ret .. indent .. collect_return(f) .. call_line .. ';\n'
340 local treat_return = give_back_return(f)
341 ret = treat_return and (ret..indent..treat_return..';\n') or ret
342 ret = ret .. indent .. return_statement(f) .. ';\n'
344 return ret
348 io.write[[
349 extern "C" {
350 #include <lua.h>
351 #include <lualib.h>
352 #include <lauxlib.h>
355 #include "lqt_common.hpp"
356 #include <QtGui>
358 #define lqtL_getinteger lua_tointeger
359 #define lqtL_getstring lua_tostring
360 #define lqtL_getnumber lua_tonumber
364 local CLASS_FILTERS = {
365 function(c) return c.xarg.fullname:match'%b<>' end,
366 function(c) return c.xarg.name:match'_' end,
367 --function(c) return c.xarg.fullname:match'Q.-Data' end,
368 function(c) return c.xarg.class_type=='struct' end,
369 function(c) return c.xarg.fullname=='QVariant::Private::Data' end,
370 function(c) return c.xarg.fullname=='QTextStreamManipulator' end,
372 local FUNCTIONS_FILTERS = {
373 function(f) return f.xarg.name:match'^[_%w]*'=='operator' end,
374 function(f) return f.xarg.fullname:match'%b<>' end,
375 function(f) return f.xarg.name:match'_' end,
376 function(f) return f.xarg.fullname:match'QInternal' end,
377 function(f) return f.xarg.access~='public' end,
379 local filter_out = function(f, t)
380 local ret, msg, F = nil, next(t, nil)
381 while (not ret) and F do
382 ret = F(f) and msg
383 msg, F = next(t, msg)
385 return ret
388 local choose_function = function(f1, f2)
389 assert_function(f1)
390 assert_function(f2)
394 local function_proto = function(f)
395 assert_function(f)
396 local larg1, larg2 = '', ''
397 for i = 1, #f do
398 local a = f[i]
399 if a.label~='Argument' then error(a.label) end
400 if a.xarg.type_name=='void' then
401 larg1, larg2 = nil, nil
402 break
404 if string.match(a.xarg.type_name, '%(%*%)') then
405 larg1 = larg1 .. ', ' .. a.xarg.type_name:gsub('%(%*%)', '(*'..argument(i)..')')
406 elseif string.match(a.xarg.type_name, '%[.*%]') then
407 larg1 = larg1 .. ', ' .. a.xarg.type_name:gsub('(%[.*%])', argument(i)..'%1')
408 else
409 larg1 = larg1 .. ', ' .. a.xarg.type_name .. ' ' .. argument(i)
411 larg2 = larg2 .. (i>1 and ', ' or '') .. argument(i)
413 return larg1, larg2
416 local examine_class = function(c)
417 assert(entities.is_class(c), 'not a class')
418 local constr, destr = {}, nil
419 for _, f in ipairs(c) do
420 if entities.is_function(f) then
421 if entities.is_constructor(f) then
422 table.insert(constr, f)
423 elseif entities.is_destructor(f) then
424 assert(not destr, 'cannot have more than one destructor!')
425 destr = f
429 --[[
430 local public_f, protected_f, virtual_f, virt_prot_f, abstract_f = {}, {}, {}, {}, {}
431 for _, f in ipairs(c) do
432 if entities.is_function(f) then
433 if f.xarg.abstract=='1' then
434 table.insert(abstract_f, f)
435 elseif f.xarg.virtual=='1' and f.xarg.access=='protected' then
436 table.insert(virt_prot_f, f)
437 elseif f.xarg.virtual=='1' and f.xarg.access=='public' then
438 table.insert(virtual_f, f)
439 elseif f.xarg.virtual~='1' and f.xarg.access=='protected' then
440 table.insert(protected_f, f)
441 elseif f.xarg.virtual~='1' and f.xarg.access=='public' then
442 table.insert(public_f, f)
446 --]]
447 local cname = 'lqt_shell_class'..c.xarg.id
448 local ret = 'class '..cname..' : public '..c.xarg.fullname..' {\npublic:\n'
449 ret = ret .. 'lua_State *L;\n'
450 local onlyprivate = true
451 for _, f in ipairs(constr) do
452 if f.xarg.access~='private' then
453 local oop = onlyprivate
454 onlyprivate = false
455 local larg1, larg2 = function_proto(f)
456 ret = ret .. cname .. '(lua_State *l'..larg1..'):'..c.xarg.fullname..'('
457 ret = ret .. larg2 .. '), L(l) {} // '..f.xarg.id..'\n'
460 if #constr==0 then
461 ret = ret .. cname .. '(lua_State *l):L(l) {} // automatic \n'
462 elseif onlyprivate then
463 error('cannot bind class: '..c.xarg.fullname..': it has only private constructors')
465 ret = ret .. 'virtual ~'..cname..'() { lqtL_unregister(L, this); }\n'
466 ret = ret .. '};\n'
467 return ret
470 for _, v in pairs(xmlstream.byid) do
471 if false and string.find(v.label, 'Function')==1 and (not filter_out(v, FUNCTIONS_FILTERS)) then
472 local status, err = pcall(function_description, v)
473 --io[status and 'stdout' or 'stderr']:write((status and '' or v.xarg.fullname..': ')..err..'\n')
474 if true or status then
475 local s, e = pcall(calling_code, v)
476 --io[s and 'stdout' or 'stderr']:write((s and ''
477 --or ('error calling '..v.xarg.fullname..': '))..e..(s and '' or '\n'))
478 if s then
479 io.stdout:write('extern "C" int bound_function'..v.xarg.id..' (lua_State *L) {\n')
480 io.stdout:write(e)
481 io.stdout:write('}\n') -- FIXME
483 else
484 print(err)
486 --io[status and 'stdout' or 'stderr']:write((status and '' or v.xarg.fullname..': ')..err..'\n')
487 elseif v.label=='Class' and not filter_out(v, CLASS_FILTERS) then -- do not support templates yet
488 local st, ret = pcall(examine_class, v)
489 if st then print(ret) else io.stderr:write(ret, '\n') end
492 --table.foreach(name_list, print)