mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-17 20:52:36 +01:00
Add idl codegen
This commit is contained in:
271
scripts/codegen.lua
Normal file
271
scripts/codegen.lua
Normal file
@@ -0,0 +1,271 @@
|
||||
-- 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
|
||||
Reference in New Issue
Block a user