awtk/tools/idl_gen/gen_lua.js
2018-06-20 11:43:34 +08:00

486 lines
12 KiB
JavaScript

const fs = require('fs');
const builtin = `#include "custom.c"\n\n`;
function genAll(json) {
function toLuaClassName(name) {
name = name.replace(/_t$/, '');
name = name.replace(/(^|_)[a-z]/g, r => {
if (r.length > 1) {
r = r.substr(1);
}
return r.toUpperCase();
});
return name;
}
function getClassInfo(name) {
for (let i = 0; i < json.length; i++) {
let iter = json[i];
if (iter.type === 'class' && iter.name === name) {
return iter;
}
}
return null;
}
function genClassChain(name) {
let str = '';
let info = getClassInfo(name);
while (info) {
str += '/' + info.name;
if (!info.parent) {
break;
}
info = getClassInfo(info.parent);
}
return str;
}
function genDecl(index, type, name) {
let str = '';
str += ` ${type} ${name} = `;
if (index < 0) {
if (type.indexOf('*') >= 0) {
str += 'NULL;\n';
} else {
str += '0;\n';
}
} else {
if (type.indexOf('char*') >= 0) {
str += `(${type})luaL_checkstring(L, ${index+1});\n`;
} else if (type.indexOf('wchar_t*') >= 0) {
str += `(${type})lua_touserdata(L, ${index+1});\n`;
} else if (type.indexOf('void*') >= 0) {
if (name !== 'ctx') {
str += `(${type})lua_touserdata(L, ${index+1});\n`;
} else {
str += ' NULL;\n';
}
} else if (type.indexOf('*') >= 0) {
const type_name = type.replace(/\*/g, '');
str += `(${type})tk_checkudata(L, ${index+1}, "${type_name}");\n`;
} else if (type.indexOf('float') >= 0 || type.indexOf('double') >= 0) {
str += `(${type})luaL_checknumber(L, ${index+1});\n`;
} else if (type.indexOf('bool_t') >= 0) {
str += `(${type})lua_toboolean(L, ${index+1});\n`;
} else if (type.indexOf('func_t') >= 0) {
str += `(${type})lua_tocfunction(L, ${index+1});\n`;
} else {
str += `(${type})luaL_checkinteger(L, ${index+1});\n`;
}
}
return str;
}
function genParamsDecl(m) {
let str = '';
if (m.return != 'void') {
str = genDecl(-1, m.return, 'ret');
}
m.params.forEach((iter, index) => {
str += genDecl(index, iter.type, iter.name);
})
return str;
}
function genReturnData(type, name) {
let str = '';
if (type.indexOf('char*') >= 0) {
str = ` lua_pushstring(L,(char*)(${name}));\n\n`;
str += ' return 1;\n';
} else if (type.indexOf('wchar_t*') >= 0) {
str = ` lua_pushlightuserdata(L,(void*)(${name}));\n\n`;
str += ' return 1;\n';
} else if (type.indexOf('*') >= 0) {
const typeName = type.replace(/\*/g, "");
str += ` return tk_newuserdata(L, ${name}, "${genClassChain(typeName)}", "awtk.${typeName}");\n`;
} else if (type.indexOf('int') >= 0) {
str = ` lua_pushinteger(L,(lua_Integer)(${name}));\n\n`;
str += ' return 1;\n';
} else if (type.indexOf('bool_t') >= 0) {
str = ` lua_pushboolean(L,(lua_Integer)(${name}));\n\n`;
str += ' return 1;\n';
} else {
str = ` lua_pushnumber(L,(lua_Number)(${name}));\n\n`;
str += ' return 1;\n';
}
return str;
}
function genCallMethod(cls, m) {
const ret_type = m.return;
let str = ret_type == 'void' ? ' ' : ' ret = '
str += `(${ret_type})${m.name}(`;
m.params.forEach((iter, index) => {
if (index > 0) {
str += ', ' + iter.name;
} else {
str += iter.name;
}
})
str += ');\n';
str += '\n';
if (ret_type == 'void') {
str += ' return 0;\n';
} else {
if (m.isConstructor) {
str += genReturnData(`${cls.name}*`, 'ret');
} else {
str += genReturnData(ret_type, 'ret');
}
}
return str;
}
function genMethod(cls, m) {
const args_nr = m.params.length;
let str = `static int wrap_${m.name}(lua_State* L) {\n`;
str += genParamsDecl(m);
str += genCallMethod(cls, m);
str += '}\n\n';
return str;
}
function genSetProperty(index, cls, p) {
let str = '';
if (index === 0) {
str += ` if(strcmp(name, "${p.name}") == 0) {\n`;
} else {
str += ` else if(strcmp(name, "${p.name}") == 0) {\n`;
}
if (p.readonly) {
str += ` printf("${p.name} is readonly\\n");\n`;
str += ` return 0;\n`;
} else {
str += genDecl(2, p.type, p.name);
str += ` obj->${p.name} = ${p.name};\n`;
}
str += ' }\n';
return str;
}
function genGetProperty(index, cls, p) {
let str = '';
if (index === 0) {
str += ` if(strcmp(name, "${p.name}") == 0) {\n`;
} else {
str += ` else if(strcmp(name, "${p.name}") == 0) {\n`;
}
str += ' ';
str += genReturnData(p.type, `obj->${p.name}`);
str += ' }\n';
return str;
}
function methodToShortName(clsName, methodName) {
return methodName.replace(clsName.replace(/_t$/, '') + "_", '')
}
function genSetProp(cls) {
let str = '';
const clsName = cls.name;
str += `static int wrap_${clsName}_set_prop(lua_State* L) {\n`;
str += genDecl(0, cls.name + '*', "obj");
str += genDecl(1, "const char*", "name");
str += ' (void)obj;\n';
str += ' (void)name;\n';
let hasSetProps = false;
cls.properties.forEach((m, index) => {
str += genSetProperty(index, cls, m);
hasSetProps = true;
});
if (hasSetProps) {
str += ` else {\n`;
}
if (cls.parent) {
str += ` return wrap_${cls.parent}_set_prop(L);\n`;
} else if (hasSetProps) {
str += ` printf("%s: not supported %s\\n", __FUNCTION__, name);\n`;
str += ` return 0;\n`;
}
if (hasSetProps) {
str += ` }\n`;
} else {
str += ` printf("%s: not supported %s\\n", __FUNCTION__, name);\n`;
str += ` return 0;\n`;
}
str += `}\n\n`;
return str;
}
function genGetProp(cls) {
let str = '';
const clsName = cls.name;
str += `static int wrap_${clsName}_get_prop(lua_State* L) {\n`;
str += genDecl(0, cls.name + '*', "obj");
str += genDecl(1, "const char*", "name");
str += ` const luaL_Reg* ret = find_member(${cls.name}_member_funcs, name);\n\n`;
str += ' (void)obj;\n';
str += ' (void)name;\n';
str += ' if(ret) {\n';
str += ' lua_pushcfunction(L, ret->func);\n';
str += ' return 1;\n';
str += ' }\n';
cls.properties.forEach((m, index) => {
str += genGetProperty(index, cls, m);
});
str += ` else {\n`;
if (cls.parent) {
str += ` return wrap_${cls.parent}_get_prop(L);\n`;
} else {
if(cls.name === 'widget_t') {
str += ` widget_t* child = widget_lookup(obj, name, FALSE);\n`;
str += ` if(child != NULL) {\n`;
str += ` return tk_newuserdata(L, child, "/widget_t", "awtk.widget_t");\n`;
str += ` }\n`;
}
str += ` printf("%s: not supported %s\\n", __FUNCTION__, name);\n`;
str += ` return 0;\n`;
}
str += ` }\n`;
str += `}\n\n`;
return str;
}
function genClassInit(cls) {
let str = '';
const clsName = cls.name;
str += `static void ${cls.name}_init(lua_State* L) {\n`;
str += ' static const struct luaL_Reg static_funcs[] = {\n'
cls.methods.forEach(m => {
const name = methodToShortName(cls.name, m.name);
if (m.isConstructor || m.isStatic) {
str += ` {"${name}", wrap_${m.name}},\n`;
}
});
str += ` {NULL, NULL}\n`;
str += ' };\n\n'
if(!cls.isFake) {
str += ' static const struct luaL_Reg index_funcs[] = {\n'
str += ` {"__index", wrap_${clsName}_get_prop},\n`;
str += ` {"__newindex", wrap_${clsName}_set_prop},\n`;
str += ` {NULL, NULL}\n`;
str += ' };\n\n'
str += ` luaL_newmetatable(L, "awtk.${cls.name}");\n`;
str += ` lua_pushstring(L, "__index");\n`;
str += ' lua_pushvalue(L, -2);\n';
str += ' lua_settable(L, -3);\n';
str += ` luaL_openlib(L, NULL, index_funcs, 0);\n`;
}
str += ` luaL_openlib(L, "${toLuaClassName(cls.name)}", static_funcs, 0);\n`;
str += ' lua_settop(L, 0);\n';
str += '}\n';
return str;
}
function genMethods(cls) {
let str = '';
const clsName = cls.name;
cls.methods.forEach(m => {
if (!m.isPrivate && !m.isCustom) {
str += genMethod(cls, m);
}
});
if(!cls.isFake) {
str += `\nstatic const struct luaL_Reg ${cls.name}_member_funcs[] = {\n`
cls.methods.forEach(m => {
const name = methodToShortName(cls.name, m.name);
if (!m.isConstructor && !m.isStatic) {
str += ` {"${name}", wrap_${m.name}},\n`;
}
});
str += ` {NULL, NULL}\n`;
str += '};\n\n'
}
return str;
}
function genClass(cls) {
let str = '';
str += genMethods(cls);
if(!cls.isFake) {
str += genSetProp(cls);
str += genGetProp(cls);
}
str += genClassInit(cls);
return str;
}
function genEnum(cls) {
let str = `static void ${cls.name}_init(lua_State* L) {\n`;
str += ' lua_newtable(L);\n';
str += ` lua_setglobal(L, "${toLuaClassName(cls.name)}");\n`;
str += ` lua_getglobal(L, "${toLuaClassName(cls.name)}");\n\n`;
const clsNamePrefix = cls.prefix;
cls.consts.forEach(iter => {
const name = iter.name.replace(clsNamePrefix, "");
str += ` lua_pushstring(L, "${name}");\n`
str += ` lua_pushinteger(L, ${iter.name});\n`;
str += ` lua_settable(L, -3); \n\n`;
});
str += '}\n\n';
return str;
}
function genOne(cls) {
if(!cls.scriptable) {
return '';
}
if (cls.type == 'class') {
return genClass(cls);
} else if (cls.type == 'enum') {
return genEnum(cls);
} else {
return '';
}
}
function genIncludes(json) {
let result = '/*XXX: generated by lua_gen. dont edit it.*/\n';
result += '#include "lua/lua.h"\n';
result += '#include "lua/lualib.h"\n';
result += '#include "lua/lauxlib.h"\n';
result += '#include "base/utf8.h"\n';
json.forEach(iter => {
if (result.indexOf(iter.header) <= 0) {
result += `#include "${iter.header}"\n`;
}
});
result += "\n";
return result;
}
function genFuncDecls(json) {
let result = '';
json.forEach(iter => {
if (iter.type == 'class') {
const clsName = iter.name;
if(!iter.isFake && iter.scriptable) {
result += `static int wrap_${clsName}_get_prop(lua_State* L);\n`;
result += `static int wrap_${clsName}_set_prop(lua_State* L);\n`;
}
}
});
result += '\n';
return result;
}
function genInit(json) {
let result = '';
result += `\nvoid luaL_openawtk(lua_State* L) {\n`;
result += ` globals_init(L);\n`;
json.forEach(iter => {
if (iter.type === 'class' || iter.type === 'enum') {
if(iter.scriptable) {
result += ` ${iter.name}_init(L);\n`;
}
}
});
result += ' s_current_L = L;\n';
result += '}\n';
return result;
}
function genGlobals(json) {
let str = '';
json.forEach(iter => {
if (iter.type == 'method') {
str += genMethod({}, iter);
}
});
str += 'static void globals_init(lua_State* L) {\n';
json.forEach(iter => {
if (iter.type == 'method') {
str += ` lua_pushcfunction(L, wrap_${iter.name});\n`;
str += ` lua_setglobal(L, "${iter.name}");\n`;
} else if (iter.type == 'const') {
str += ` lua_pushinteger(L, ${iter.name});\n`;
str += ` lua_setglobal(L, "${iter.name}");\n`;
}
});
str += ' lua_pushcfunction(L, to_str);\n';
str += ' lua_setglobal(L, "to_str");\n';
str += ' lua_pushcfunction(L, to_wstr);\n';
str += ' lua_setglobal(L, "to_wstr");\n';
str += '}\n\n';
return str;
}
let result = genIncludes(json);
result += builtin;
result += genFuncDecls(json);
result += genGlobals(json);
json.forEach(iter => {
result += genOne(iter);
});
result += genInit(json);
return result;
}
function run() {
fs.writeFileSync('../../lua/tk_lua.c', genAll(JSON.parse(fs.readFileSync('idl.json').toString())));
}
run();