various changes to the generator
[lqt.git] / new / generator.lua
blobea07dcd9b27cd2f4a1970637e7220efe913c162d
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 collect_return = function(f)
262 assert_function(f)
263 local ret_t = entities.return_type(f)
264 if not ret_t then
265 return ''
266 else
267 return ret_t.xarg.type_name .. ' ret = '
271 local give_back_return = function(f)
272 assert_function(f)
273 local ret_t = entities.return_type(f)
274 if not ret_t then
275 return ''
276 else
277 local _d, _g, p, _n = type_properties(ret_t)
278 return p'ret'
282 local return_statement = function(f)
283 assert_function(f)
284 local ret_t = entities.return_type(f)
285 if not ret_t then
286 return 'return 0'
287 else
288 local _d, _g, _p, n = type_properties(ret_t)
289 return 'return '..tostring(n)
293 -- TODO: constructors wait for deciding if shell class is needed
294 local calling_code = function(f)
295 assert_function(f)
296 local ret, indent = '', ' '
297 local argi = 0
299 ret = ret..indent..argument_assert(f)..';\n'
301 ret = ret..get_args(f, indent)
303 --if entities.is_constructor(f) then
304 --elseif entities.is_destructor(f) then
305 --else
307 --[[
308 local args = ''
309 for i = 1,#f do
310 args = args .. (i > 1 and ', ' or '') .. argument(i)
312 args = '('..args..')';
313 local call_line = f.xarg.fullname .. args .. ';\n'
314 local ret_type = entities.return_type(f)
315 if ret_type then
316 call_line = ret_type.xarg.type_name .. ' ret = ' .. call_line
317 local _d, _g, p, n = type_properties(ret_type)
318 call_line = call_line .. indent .. p'ret' .. '\n'
319 call_line = call_line .. indent .. 'return ' .. tostring(n) .. ';\n'
321 --]]
322 local call_line = function_static_call(f)
323 ret = ret .. indent .. collect_return(f) .. call_line .. ';\n'
324 local treat_return = give_back_return(f)
325 ret = treat_return and (ret..indent..treat_return..';\n') or ret
326 ret = ret .. indent .. return_statement(f) .. ';\n'
328 return ret
332 io.write[[
333 extern "C" {
334 #include <lua.h>
335 #include <lualib.h>
336 #include <lauxlib.h>
339 #include "lqt_common.hpp"
340 #include <QtGui>
342 #define lqtL_getinteger lua_tointeger
343 #define lqtL_getstring lua_tostring
344 #define lqtL_getnumber lua_tonumber
348 local FILTERS = {
349 function(f) return f.xarg.name:match'^[_%w]*'=='operator' end,
350 function(f) return f.xarg.fullname:match'%b<>' end,
351 function(f) return f.xarg.name:match'_cast' end,
352 function(f) return f.xarg.fullname:match'QInternal' end,
353 function(f) return f.xarg.access~='public' end,
355 local filter_out = function(f)
356 local ret, msg, F = nil, next(FILTERS, nil)
357 while (not ret) and F do
358 ret = F(f) and msg
359 msg, F = next(FILTERS, msg)
361 return ret
364 for _, v in pairs(xmlstream.byid) do
365 if string.find(v.label, 'Function')==1 and (not filter_out(v)) then
366 local status, err = pcall(function_description, v)
367 --io[status and 'stdout' or 'stderr']:write((status and '' or v.xarg.fullname..': ')..err..'\n')
368 if status then
369 local s, e = pcall(calling_code, v)
370 --io[s and 'stdout' or 'stderr']:write((s and ''
371 --or ('error calling '..v.xarg.fullname..': '))..e..(s and '' or '\n'))
372 if s then
373 io.stdout:write('extern "C" int bound_function'..v.xarg.id..' (lua_State *L) {\n')
374 io.stdout:write(e)
375 io.stdout:write('}\n') -- FIXME
377 else
378 print(err)
380 --io[status and 'stdout' or 'stderr']:write((status and '' or v.xarg.fullname..': ')..err..'\n')
383 --table.foreach(name_list, print)