various fixes
[lqt.git] / new / generator.lua
blob7ca9513e63c80a29f59cfbe3ba8292c10edf1332
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 local elements = entities
9 assert_function = function(f)
10 assert(entities.is_function(f), 'argument is not a function')
11 end
13 local filename = ...
14 local path = string.match(arg[0], '(.*/)[^%/]+') or ''
15 local xmlstream, idindex = dofile(path..'xml.lua')(my.readfile(filename))
16 io.stderr:write'parsed XML\n'
17 local code = xmlstream[1]
19 ----------------------------------------------------------------------------------
21 local copy_functions = function(index)
22 local ret, copied = {}, 0
23 for e in pairs(index) do
24 if e.label:match'^Function' then
25 --[[and not (e.xarg.name:match'^[%a]*'=='operator'
26 or e.xarg.fullname:match'%b<>'
27 or e.xarg.name:match'_'
28 or e.xarg.name:match'[xX]11'
29 or e.xarg.fullname:match'QInternal'
30 or e.xarg.access=='private'
31 or e.xarg.access=='protected' -- FIXME
32 or e.xarg.fullname=='QVariant::canConvert') then --]]
33 e.label = 'Function'
34 ret[e] = true
35 copied = copied + 1
36 else
37 --removed = removed + (e.label:match'^Function' and 1 or 0)
38 --removed = removed + 1
39 end
40 end
41 return ret, copied
42 end
44 local fix_functions = function(index)
45 for f in pairs(index) do
46 local args = {}
47 for i, a in ipairs(f) do
48 -- avoid bogus 'void' arguments
49 if a.xarg.type_name=='void' and i==1 and f[2]==nil then break end
50 if a.label=='Argument' then
51 table.insert(args, a)
52 end
53 end
54 f.arguments = args
55 if elements.is_constructor(f) then
56 f.xarg.fullname = '*new '..f.xarg.fullname
57 f.return_type = f.xarg.type_base..'&'
58 f.xarg.static = '1'
59 elseif elements.is_destructor(f) or f.xarg.type_name=='void' then
60 f.return_type = nil
61 else
62 if false and f.xarg.access=='protected' then
63 local shellname = 'lqt_shell_'..string.gsub(f.parent.xarg.fullname, '::', '_LQT_')
64 f.xarg.fullname = shellname..'::'..f.xarg.name
65 if f.xarg.static~='1' then
66 f.xarg.static='1'
67 local newarg = { label='Argument', xarg = {
68 type_name = f.xarg.member_of_class..'*',
69 }, }
70 table.insert(args, newarg, 1)
71 end
72 end
73 f.return_type = f.xarg.type_name
74 end
75 end
76 return index
77 end
79 local copy_enums = function(index)
80 local ret = {}
81 for e in pairs(index) do
82 if e.label=='Enum'
83 and e.xarg.access~='public' then
84 ret[e] = true
85 end
86 end
87 return ret
88 end
90 local fix_enums = function(index)
91 for e in pairs(index) do
92 local values = {}
93 for _, v in ipairs(e) do
94 if v.label=='Enumerators' then
95 values[#values] = v.xarg.name
96 end
97 end
98 e.values = values
99 end
100 return index
103 local copy_classes = function(index)
104 local ret = {}
105 for e in pairs(index) do
106 if e.label=='Class'
107 and e.xarg.access~='private'
108 and not (e.xarg.fullname:match'%b<>'
109 or e.xarg.fullname=='QDebug::Stream'
110 or e.xarg.fullname=='QForeachContainerBase'
111 or e.xarg.fullname=='QByteArray::Data'
112 or e.xarg.fullname=='QVariant::Private::Data'
113 or e.xarg.fullname=='QRegion::QRegionData'
114 or e.xarg.fullname=='QTextStreamManipulator'
115 or e.xarg.fullname=='QString::Data'
116 or e.xarg.fullname=='QThreadStorageData'
117 ) then
118 ret[e] = true
121 return ret
124 local fill_virtuals = function(index)
125 local classes = {}
126 for c in pairs(index) do
127 classes[c.xarg.fullname] = c
129 local get_virtuals
130 get_virtuals = function(c)
131 local ret = {}
132 for _, f in ipairs(c) do
133 if f.label=='Function' and f.xarg.virtual=='1' then
134 local n = string.match(f.xarg.name, '~') or f.xarg.name
135 if n~='~' then ret[n] = f end
138 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
139 local base = classes[b]
140 if type(base)=='table' then
141 local bv = get_virtuals(base)
142 for n, f in pairs(bv) do
143 if not ret[n] then ret[n] = f end
147 for _, f in ipairs(c) do
148 if f.label=='Function'
149 and f.xarg.access~='private'
150 and (ret[string.match(f.xarg.name, '~') or f.xarg.name]) then
151 f.xarg.virtual = '1'
152 local n = string.match(f.xarg.name, '~')or f.xarg.name
153 ret[n] = f
156 return ret
158 for c in pairs(index) do
159 c.virtuals = get_virtuals(c)
160 for _, f in pairs(c.virtuals) do
161 if f.xarg.abstract=='1' then c.abstract=true break end
164 return index
167 local fill_special_methods = function(index)
168 for c in pairs(index) do
169 local construct, destruct, normal = {}, nil, {}
170 local n = c.xarg.name
171 local auto, copy = true, nil
172 for _, f in ipairs(c) do
173 if n==f.xarg.name then
174 auto = false
175 if #(f.arguments or {})==1 and
176 f.arguments[1].xarg.type_name==(c.xarg.fullname..' const&') then
177 copy = f.xarg.access or 'PUBLIC?'
180 if n==f.xarg.name then
181 table.insert(construct, f)
182 elseif f.xarg.name:match'~' then
183 destruct = f
184 else
185 if (not string.match(f.xarg.name, '^operator%W'))
186 and (not f.xarg.member_template_parameters) then
187 table.insert(normal, f)
191 construct.auto = auto
192 construct.copy = (copy==nil and 'auto' or copy) -- FIXME: must try
193 c.constructors = construct
194 c.destructor = destruct and (destruct.xarg.access or 'PUBLIC?') or 'auto'
195 c.methods = normal
197 return index
200 local fill_copy_constructor = function(index)
201 local classes = {}
202 for c in pairs(index) do
203 classes[c.xarg.name] = c
205 local destr
206 destr = function(c)
207 if c.destructor=='auto' then
208 local ret = nil
209 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
210 local base = classes[b]
211 if base and destr(base)=='private' then
212 c.destructor = 'private'
213 return 'private'
217 return c.destructor
219 local copy_constr
220 copy_constr = function(c)
221 if c.constructors.copy=='auto' then
222 local ret = nil
223 for b in string.gmatch(c.xarg.bases or '', '([^;]+);') do
224 local base = classes[b]
225 if base and copy_constr(base)=='private' then
226 c.constructors.copy = 'private'
227 return 'private'
231 return c.constructors.copy
233 for c in pairs(index) do
234 c.constructors.copy = copy_constr(c)
235 c.destructor = destr(c)
236 --io.stderr:write(c.xarg.fullname, '\t', c.constructors.copy, '\n')
237 --io.stderr:write(c.xarg.fullname, '\t', c.destructor, '\n')
239 return index
242 local fill_typesystem_with_enums = function(enums, types)
243 local ret = {}
244 for e in pairs(enums) do
245 if not types[e.xarg.fullname] then
246 ret[e] = true
247 types[e.xarg.fullname] = {
248 push = function(n)
249 return 'lqtL_pushenum(L, '..n..', "'..e.xarg.fullname..'")', 1
250 end,
251 get = function(n)
252 return 'static_cast<'..e.xarg.fullname..'>'
253 ..'(lqtL_toenum(L, '..n..', "'..e.xarg.fullname..'"))', 1
254 end,
256 else
257 --io.stderr:write(e.xarg.fullname, ': already present\n')
260 return ret
263 local fill_typesystem_with_classes = function(classes, types)
264 local ret = {}
265 for c in pairs(classes) do
266 if not types[c.xarg.fullname] then
267 ret[c] = true
268 types[c.xarg.fullname..'*'] = {
269 -- the argument is a pointer to class
270 push = function(n)
271 return 'lqtL_passudata(L, '..n..', "'..c.xarg.fullname..'*")', 1
272 end,
273 get = function(n)
274 return 'static_cast<'..c.xarg.fullname..'*>'
275 ..'(lqtL_toudata(L, '..n..', "'..c.xarg.fullname..'*"))', 1
276 end,
278 types[c.xarg.fullname..'&'] = {
279 -- the argument is a reference to class
280 push = function(n)
281 return 'lqtL_passudata(L, &'..n..', "'..c.xarg.fullname..'*")', 1
282 end,
283 get = function(n)
284 return '*static_cast<'..c.xarg.fullname..'*>'
285 ..'(lqtL_toudata(L, '..n..', "'..c.xarg.fullname..'*"))', 1
286 end,
288 if c.constructors.copy~='private' then -- and c.destructor~='private' then
289 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
290 types[c.xarg.fullname] = {
291 -- the argument is the class itself
292 push = function(n)
293 return 'lqtL_passudata(L, new '..shellname
294 ..'(L, '..n..'), "'..c.xarg.fullname..'*")', 1
295 end,
296 get = function(n)
297 return '*static_cast<'..c.xarg.fullname..'*>'
298 ..'(lqtL_toudata(L, '..n..', "'..c.xarg.fullname..'*"))', 1
299 end,
301 types[c.xarg.fullname..' const&'] = {
302 -- the argument is a pointer to class
303 push = function(n)
304 return 'lqtL_passudata(L, new '..shellname
305 ..'(L, '..n..'), "'..c.xarg.fullname..'*")', 1
306 end,
307 get = function(n)
308 return '*static_cast<'..c.xarg.fullname..'*>'
309 ..'(lqtL_toudata(L, '..n..', "'..c.xarg.fullname..'*"))', 1
310 end,
312 else
313 io.stderr:write(c.xarg.fullname, ': no copy constructor\n')
315 else
316 io.stderr:write(c.xarg.fullname, ': already present\n')
319 return ret
322 local fill_wrapper_code = function(f, types)
323 local stackn, argn = 1, 1
324 local wrap, line = '', ''
325 if f.xarg.member_of_class and f.xarg.static~='1' then
326 if not types[f.xarg.member_of_class..'*'] then return nil end -- print(f.xarg.member_of_class) return nil end
327 local sget, sn = types[f.xarg.member_of_class..'*'].get(stackn)
328 wrap = wrap .. ' ' .. f.xarg.member_of_class .. '* self = ' .. sget .. ';\n'
329 stackn = stackn + sn
330 wrap = wrap .. [[
331 if (NULL==self) {
332 lua_pushstring(L, "this pointer is NULL");
333 lua_error(L);
336 --print(sget, sn)
337 line = 'self->'..f.xarg.fullname..'('
338 else
339 line = f.xarg.fullname..'('
341 for i, a in ipairs(f.arguments) do
342 if not types[a.xarg.type_name] then return nil end -- print(a.xarg.type_name) return nil end
343 local aget, an = types[a.xarg.type_name].get(stackn)
344 wrap = wrap .. ' ' .. a.xarg.type_name .. ' arg' .. tostring(argn) .. ' = '
345 wrap = wrap .. aget .. ';\n'
346 line = line .. (argn==1 and 'arg' or ', arg') .. argn
347 stackn = stackn + an
348 argn = argn + 1
350 line = line .. ')'
351 -- FIXME: hack follows for constructors
352 if f.calling_line then line = f.calling_line end
353 if f.return_type then line = f.return_type .. ' ret = ' .. line end
354 wrap = wrap .. ' ' .. line .. ';\n lua_settop(L, 0);\n' -- lua_pop(L, '..stackn..');\n'
355 if f.return_type then
356 if not types[f.return_type] then return nil end
357 local rput, rn = types[f.return_type].push'ret'
358 wrap = wrap .. ' luaL_checkstack(L, '..rn..', "cannot grow stack for return value");\n'
359 wrap = wrap .. ' '..rput..';\n return '..rn..';\n'
360 else
361 wrap = wrap .. ' return 0;\n'
363 f.wrapper_code = wrap
364 return f
367 local fill_wrappers = function(functions, types)
368 local ret = {}
369 for f in pairs(functions) do
370 f = fill_wrapper_code(f, types)
371 if f then
372 ret[f] = true
373 local out = 'extern "C" int lqt_bind'..f.xarg.id..' (lua_State *L) {\n'
374 .. f.wrapper_code .. '}\n'
375 --print(out)
378 return ret
381 local argument_name = function(tn, an)
382 local ret
383 if string.match(tn, '%(%*%)') then
384 ret = string.gsub(tn, '%(%*%)', '(*'..an..')', 1)
385 elseif string.match(tn, '%[.*%]') then
386 ret = string.gsub(tn, '(%[.*%])', an..'%1')
387 else
388 ret = tn .. ' ' .. an
390 return ret
393 local virtual_overload = function(v, types)
394 local ret = ''
395 if v.virtual_overload then return v end
396 -- make return type
397 if v.return_type and not types[v.return_type] then return nil end
398 local rget, rn = '', 0
399 if v.return_type then rget, rn = types[v.return_type].get'oldtop+1' end
400 local retget = (v.return_type and argument_name(v.return_type, 'ret')
401 .. ' = ' .. rget .. ';' or '') .. 'lua_settop(L, oldtop);return'
402 .. (v.return_type and ' ret' or '')
403 -- make argument push
404 local pushlines, stack = '', 0
405 for i, a in ipairs(v.arguments) do
406 if not types[a.xarg.type_name] then return nil end
407 local apush, an = types[a.xarg.type_name].push('arg'..i)
408 pushlines = pushlines .. ' ' .. apush .. ';\n'
409 stack = stack + an
411 -- make lua call
412 local luacall = 'lua_pcall(L, '..stack..', '..rn..', 0)'
413 -- make prototype and fallback
414 local proto = (v.return_type or 'void')..' ;;'..v.xarg.name..' ('
415 local fallback = ''
416 for i, a in ipairs(v.arguments) do
417 proto = proto .. (i>1 and ', ' or '')
418 .. argument_name(a.xarg.type_name, 'arg'..i)
419 fallback = fallback .. (i>1 and ', arg' or 'arg') .. i
421 proto = proto .. ')' .. (v.xarg.constant=='1' and ' const' or '')
422 fallback = (v.return_type and 'return this->' or 'this->')
423 .. v.xarg.fullname .. '(' .. fallback .. ');\n}\n'
424 ret = proto .. [[ {
425 int oldtop = lua_gettop(L);
426 lqtL_pushudata(L, this, "]]..v.xarg.member_of_class..[[*");
427 lua_getfield(L, -1, "]]..v.xarg.name..[[");
428 if (lua_isfunction(L, -1)) {
429 lua_insert(L, -2);
430 ]] .. pushlines .. [[
431 if (]]..luacall..[[) {
432 ]]..retget..[[;
435 lua_settop(L, oldtop);
436 ]] .. fallback
437 v.virtual_overload = ret
438 v.virtual_proto = string.gsub(proto, ';;', '', 1)
439 return v
442 local fill_shell_class = function(c, types)
443 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
444 local shell = 'class ' .. shellname .. ' : public ' .. c.xarg.fullname .. ' {\npublic:\n'
445 shell = shell .. ' lua_State *L;\n'
446 for _, constr in ipairs(c.constructors) do
447 if constr.xarg.access~='private' then
448 local cline = ' '..shellname..' (lua_State *l'
449 local argline = ''
450 for i, a in ipairs(constr.arguments) do
451 cline = cline .. ', ' .. argument_name(a.xarg.type_name, 'arg'..i)
452 argline = argline .. (i>1 and ', arg' or 'arg') .. i
454 cline = cline .. ') : ' .. c.xarg.fullname .. '(' .. argline .. '), L(l) {}\n'
455 shell = shell .. cline
458 if c.constructors.copy=='auto' then
459 local cline = ' '..shellname..' (lua_State *l, '..c.xarg.fullname..' const& arg1)'
460 cline = cline .. ' : ' .. c.xarg.fullname .. '(arg1), L(l) {}\n'
461 shell = shell .. cline
463 for i, v in pairs(c.virtuals) do
464 if v.xarg.access~='private' then
465 local vret = virtual_overload(v, types)
466 if v.virtual_proto then shell = shell .. ' virtual ' .. v.virtual_proto .. ';\n' end
469 shell = shell .. '};\n'
470 c.shell_class = shell
471 return c
474 local fill_shell_classes = function(classes, types)
475 local ret = {}
476 for c in pairs(classes) do
477 if c.shell then
478 c = fill_shell_class(c, types)
479 if c then ret [c] = true print(c.shell_class)
480 else
481 io.stderr:write(c.fullname, '\n')
485 return ret
488 local print_virtual_overloads = function(classes, types)
489 for c in pairs(classes) do
490 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
491 for _,v in pairs(c.virtuals) do
492 if v.virtual_overload then
493 print((string.gsub(v.virtual_overload, ';;', shellname..'::', 1)))
497 return classes
500 local print_wrappers = function(index)
501 for c in pairs(index) do
502 local meta = {}
503 for _, f in ipairs(c.methods) do
504 if f.wrapper_code then
505 local out = 'extern "C" int lqt_bind'..f.xarg.id
506 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
507 if f.xarg.access=='public' then print(out) end
508 meta[f.xarg.id] = f.xarg.name
511 if c.shell then
512 for _, f in ipairs(c.constructors) do
513 if f.wrapper_code then
514 local out = 'extern "C" int lqt_bind'..f.xarg.id
515 ..' (lua_State *L) {\n'.. f.wrapper_code .. '}\n'
516 if f.xarg.access=='public' then print(out) end
517 meta[f.xarg.id] = 'new'
522 return index
525 local fix_methods_wrappers = function(classes)
526 for c in pairs(classes) do
527 -- if class seems abstract but has a shell class
528 if c.abstract and c.destructor~='private' then
529 -- is it really abstract?
530 local a = false
531 for _, f in pairs(c.virtuals) do
532 -- if it is abstract but we cannot overload
533 if f.xarg.abstract=='1' and not f.virtual_overload then a = true break end
535 c.abstract = a
537 c.shell = (not c.abstract) and (c.destructor~='private')
538 for _, constr in ipairs(c.constructors) do
539 local shellname = 'lqt_shell_'..string.gsub(c.xarg.fullname, '::', '_LQT_')
540 constr.calling_line = '*new '..shellname..'(L'
541 for i=1,#(constr.arguments) do
542 constr.calling_line = constr.calling_line .. ', arg' .. i
544 constr.calling_line = constr.calling_line .. ')'
547 return classes
550 local functions = copy_functions(idindex)
551 local functions = fix_functions(functions)
553 local enums = copy_enums(idindex)
554 local enums = fix_enums(enums)
556 local classes = copy_classes(idindex)
557 local classes = fill_virtuals(classes)
558 local classes = fill_special_methods(classes)
559 local classes = fill_copy_constructor(classes)
560 local classes = fix_methods_wrappers(classes)
562 local ntable = function(t) local ret=0 for _ in pairs(t) do ret=ret+1 end return ret end
564 local typesystem = dofile'types.lua'
566 local debug = function(...)
567 for i = 1, select('#',...) do
568 io.stderr:write((i==1) and '' or '\t', (select(i,...)))
570 io.stderr:write'\n'
572 debug('funcs', ntable(functions))
573 debug('enums', ntable(enums))
574 debug('class', ntable(classes))
575 local enums = fill_typesystem_with_enums(enums, typesystem)
576 local classes = fill_typesystem_with_classes(classes, typesystem)
577 local functions = fill_wrappers(functions, typesystem)
578 local classes = fill_shell_classes(classes, typesystem)
579 local classes = print_virtual_overloads(classes, typesystem)
580 local classes = print_wrappers(classes)
581 debug('funcs', ntable(functions))
582 debug('enums', ntable(enums))
583 debug('class', ntable(classes))
585 local print_virtuals = function(index)
586 for c in pairs(index) do
587 debug(c.xarg.name)
588 for n, f in pairs(c.virtuals) do debug(' '..n, f.xarg.fullname) end
593 for k,v in pairs(typesystem) do
594 --print(k, v.get'INDEX')
597 --print_virtuals(classes)
599 --print(copy_functions(idindex))