bgfx/scripts/codegen.lua
2019-02-27 18:04:27 +08:00

272 lines
6.6 KiB
Lua

-- Copyright 2019 云风 https://github.com/cloudwu . All rights reserved.
-- License (the same with bgfx) : https://github.com/bkaradzic/bgfx/blob/master/LICENSE
local codegen = {}
local function camelcase_to_underscorecase(name)
local tmp = {}
for v in name:gmatch "[%u%d]+%l*" do
tmp[#tmp+1] = v:lower()
end
return table.concat(tmp, "_")
end
local function convert_typename(name)
if name:match "^%u" then
return "bgfx_" .. camelcase_to_underscorecase(name) .. "_t"
else
return name
end
end
local function convert_funcname(name)
name = name:gsub("^%l", string.upper) -- Change to upper CamlCase
return camelcase_to_underscorecase(name)
end
local function convert_arg(all_types, arg, what)
local t, postfix = arg.fulltype:match "(%a[%a%d_:]*)%s*([*&]+)%s*$"
if t then
arg.type = t
if postfix == "&" then
arg.ref = true
end
else
arg.type = arg.fulltype
end
local ctype = all_types[arg.type]
if not ctype then
error ("Undefined type " .. arg.fulltype .. " for " .. what)
end
arg.ctype = arg.fulltype:gsub(arg.type, ctype.cname):gsub("&", "*")
if ctype.cname ~= arg.type then
arg.cpptype = arg.fulltype:gsub(arg.type, "bgfx::"..arg.type)
else
arg.cpptype = arg.fulltype
end
if arg.ref then
arg.ptype = arg.cpptype:gsub("&", "*")
end
end
local function alternative_name(name)
if name:sub(1,1) == "_" then
return name:sub(2)
else
return name .. "_"
end
end
local function gen_arg_conversion(all_types, arg)
if arg.ctype == arg.fulltype then
-- do not need conversion
arg.aname = arg.name
return
end
local ctype = all_types[arg.type]
if ctype.handle and arg.type == arg.fulltype then
local aname = alternative_name(arg.name)
arg.aname = aname .. ".cpp"
arg.conversion = string.format(
"union { %s c; bgfx::%s cpp; } %s = { %s };" ,
ctype.cname, arg.type, aname, arg.name)
elseif arg.ref then
if ctype.cname == arg.type then
arg.aname = "*" .. arg.name
elseif arg.out and ctype.enum then
local aname = alternative_name(arg.name)
local cpptype = arg.cpptype:match "(.-)%s*&" -- remove &
arg.aname = aname
arg.conversion = string.format("%s %s;", cpptype, aname)
arg.out_conversion = string.format("*%s = (%s)%s;", arg.name, ctype.cname, aname)
else
arg.aname = alternative_name(arg.name)
arg.conversion = string.format(
"%s %s = *(%s)%s;",
arg.cpptype, arg.aname, arg.ptype, arg.name)
end
else
arg.aname = string.format(
"(%s)%s",
arg.cpptype, arg.name)
end
end
local function gen_ret_conversion(all_types, func)
local postfix = { func.vararg and "va_end(argList);" }
func.ret_postfix = postfix
for _, arg in ipairs(func.args) do
if arg.out_conversion then
postfix[#postfix+1] = arg.out_conversion
end
end
local ctype = all_types[func.ret.type]
if ctype.handle then
func.ret_conversion = string.format(
"union { %s c; bgfx::%s cpp; } handle_ret;" ,
ctype.cname, func.ret.type)
func.ret_prefix = "handle_ret.cpp = "
postfix[#postfix+1] = "return handle_ret.c;"
elseif func.ret.fulltype ~= "void" then
local ctype_conversion = func.ret.type == func.ret.ctype and "" or ("(" .. func.ret.ctype .. ")")
if #postfix > 0 then
func.ret_prefix = string.format("%s retValue = %s", func.ret.ctype , ctype_conversion)
postfix[#postfix+1] = "return retValue;"
else
func.ret_prefix = string.format("return %s", ctype_conversion)
end
end
end
function codegen.nameconversion(all_types, all_funcs)
local enums = {}
for k,v in pairs(all_types) do
if not v.cname then
v.cname = convert_typename(k)
end
if v.enum then
enums[#enums+1] = k
end
end
for _, e in ipairs(enums) do
local t = all_types[e]
all_types[e] = nil
all_types[e .. "::Enum"] = t
end
for _,v in ipairs(all_funcs) do
if v.cname == nil then
v.cname = convert_funcname(v.name)
end
if v.class then
v.cname = convert_funcname(v.class) .. "_" .. v.cname
end
for _, arg in ipairs(v.args) do
convert_arg(all_types, arg, v.name)
gen_arg_conversion(all_types, arg)
end
if v.vararg then
local args = v.args
local vararg = {
name = "",
ctype = "...",
aname = "argList",
conversion = string.format(
"va_list argList;\n\tva_start(argList, %s);",
args[#args].name),
}
args[#args + 1] = vararg
v.implname = v.vararg
else
v.implname = v.name
end
convert_arg(all_types, v.ret, v.name .. "@rettype")
gen_ret_conversion(all_types, v)
if v.class then
local classname = v.class
if v.const then
classname = "const " .. classname
end
local classtype = { fulltype = classname .. "*" }
convert_arg(all_types, classtype, "class member " .. v.name)
v.this = classtype.ctype .. " _this"
v.this_conversion = string.format( "%s This = (%s)_this;", classtype.cpptype, classtype.cpptype)
end
end
end
local function lines(tbl)
if #tbl == 0 then
return "//EMPTYLINE"
else
return table.concat(tbl, "\n\t")
end
end
local function remove_emptylines(txt)
return (txt:gsub("\t//EMPTYLINE\n", ""))
end
local function codetemp(func)
local conversion = {}
local args = {}
local callargs = {}
local cppfunc
if func.class then
-- It's a member function
args[1] = func.this
conversion[1] = func.this_conversion
cppfunc = "This->" .. func.name
else
cppfunc = "bgfx::" .. func.implname
end
for _, arg in ipairs(func.args) do
conversion[#conversion+1] = arg.conversion
args[#args+1] = arg.ctype .. " " .. arg.name
callargs[#callargs+1] = arg.aname
end
conversion[#conversion+1] = func.ret_conversion
return {
RET = func.ret.ctype,
FUNCNAME = func.cname,
ARGS = table.concat(args, ", "),
CONVERSION = lines(conversion),
PRERET = func.ret_prefix or "",
CPPFUNC = cppfunc,
CALLARGS = table.concat(callargs, ", "),
POSTRET = lines(func.ret_postfix),
CODE = func.cfunc,
}
end
local function apply_template(func, temp)
func.codetemp = func.codetemp or codetemp(func)
return (temp:gsub("$(%u+)", func.codetemp))
end
local c99temp = [[
BGFX_C_API $RET bgfx_$FUNCNAME($ARGS)
{
$CONVERSION
$PRERET$CPPFUNC($CALLARGS);
$POSTRET
}
]]
local c99usertemp = [[
BGFX_C_API $RET bgfx_$FUNCNAME($ARGS)
{
$CODE
}
]]
function codegen.gen_c99(func)
if func.cfunc then
return apply_template(func, c99usertemp)
else
return remove_emptylines(apply_template(func, c99temp))
end
end
local template_function_declaration = [[
/**/
BGFX_C_API $RET bgfx_$FUNCNAME($ARGS);
]]
function codegen.gen_c99decl(func)
return apply_template(func, template_function_declaration)
end
function codegen.gen_interface_struct(func)
return apply_template(func, "$RET (*$FUNCNAME)($ARGS);")
end
function codegen.gen_interface_import(func)
return "bgfx_" .. func.cname
end
return codegen