81c6610293
* Minor rewording & increase of clarity * Corrected a noun that was being used as a verb * Slight rewording of the C99 bindings notices * Actually, this sounds better & looks nicer
997 lines
24 KiB
Lua
997 lines
24 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 DEFAULT_NAME_ALIGN = 20
|
|
local DEFINE_NAME_ALIGN = 41
|
|
|
|
local function namealign(name, align)
|
|
align = align or DEFAULT_NAME_ALIGN
|
|
return string.rep(" ", align - #name)
|
|
end
|
|
|
|
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 to_underscorecase(name)
|
|
local tmp = {}
|
|
for v in name:gmatch "[_%u][%l%d]*" do
|
|
if v:byte() == 95 then -- '_'
|
|
v = v:sub(2) -- remove _
|
|
end
|
|
tmp[#tmp+1] = v
|
|
end
|
|
return table.concat(tmp, "_")
|
|
end
|
|
|
|
local function underscorecase_to_camelcase(name)
|
|
local tmp = {}
|
|
for v in name:gmatch "[^_]+" do
|
|
tmp[#tmp+1] = v:sub(1,1):upper() .. v:sub(2)
|
|
end
|
|
return table.concat(tmp)
|
|
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, namespace)
|
|
local fulltype, array = arg.fulltype:match "(.-)%s*(%[%s*[%d%a_:]*%s*%])"
|
|
if array then
|
|
arg.fulltype = fulltype
|
|
arg.array = array
|
|
local enum, value = array:match "%[%s*([%a%d]+)::([%a%d]+)%]"
|
|
if enum then
|
|
local typedef = all_types[ enum .. "::Enum" ]
|
|
if typedef == nil then
|
|
error ("Unknown Enum " .. enum)
|
|
end
|
|
arg.carray = "[BGFX_" .. camelcase_to_underscorecase(enum):upper() .. "_" .. value:upper() .. "]"
|
|
end
|
|
end
|
|
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
|
|
local prefix, t = arg.fulltype:match "^%s*(%a+)%s+(%S+)"
|
|
if prefix then
|
|
arg.type = t
|
|
else
|
|
arg.type = arg.fulltype
|
|
end
|
|
end
|
|
local ctype
|
|
local substruct = namespace.substruct
|
|
if substruct then
|
|
ctype = substruct[arg.type]
|
|
end
|
|
if not ctype then
|
|
ctype = all_types[arg.type]
|
|
end
|
|
if not ctype then
|
|
error ("Undefined type " .. arg.fulltype .. " in " .. namespace.name)
|
|
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
|
|
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.aname_cpp2c = aname .. ".c"
|
|
arg.conversion = string.format(
|
|
"union { %s c; bgfx::%s cpp; } %s = { %s };" ,
|
|
ctype.cname, arg.type, aname, arg.name)
|
|
arg.conversion_back = string.format(
|
|
"union { bgfx::%s cpp; %s c; } %s = { %s };" ,
|
|
arg.type, ctype.cname, aname, arg.name)
|
|
elseif arg.ref then
|
|
if ctype.cname == arg.type then
|
|
arg.aname = "*" .. arg.name
|
|
arg.aname_cpp2c = "&" .. arg.name
|
|
elseif arg.out and ctype.enum then
|
|
local aname = alternative_name(arg.name)
|
|
local cpptype = arg.cpptype:match "(.-)%s*&" -- remove &
|
|
local c99type = arg.ctype:match "(.-)%s*%*" -- remove *
|
|
arg.aname = aname
|
|
arg.aname_cpp2c = "&" .. aname
|
|
arg.conversion = string.format("%s %s;", cpptype, aname)
|
|
arg.conversion_back = string.format("%s %s;", c99type, aname);
|
|
arg.out_conversion = string.format("*%s = (%s)%s;", arg.name, ctype.cname, aname)
|
|
arg.out_conversion_back = string.format("%s = (%s)%s;", arg.name, c99type, aname)
|
|
else
|
|
arg.aname = alternative_name(arg.name)
|
|
arg.aname_cpp2c = string.format("(%s)&%s" , arg.ctype , arg.name)
|
|
arg.conversion = string.format(
|
|
"%s %s = *(%s)%s;",
|
|
arg.cpptype, arg.aname, arg.ptype, arg.name)
|
|
end
|
|
else
|
|
local cpptype = arg.cpptype
|
|
local ctype = arg.ctype
|
|
if arg.array then
|
|
cpptype = cpptype .. "*"
|
|
ctype = ctype .. "*"
|
|
end
|
|
arg.aname = string.format(
|
|
"(%s)%s",
|
|
cpptype, arg.name)
|
|
arg.aname_cpp2c = string.format(
|
|
"(%s)%s",
|
|
ctype, arg.name)
|
|
end
|
|
end
|
|
|
|
local function gen_ret_conversion(all_types, func)
|
|
local postfix = { func.vararg and "va_end(argList);" }
|
|
local postfix_cpp2c = { postfix[1] }
|
|
func.ret_postfix = postfix
|
|
func.ret_postfix_cpp2c = postfix_cpp2c
|
|
|
|
for _, arg in ipairs(func.args) do
|
|
if arg.out_conversion then
|
|
postfix[#postfix+1] = arg.out_conversion
|
|
postfix_cpp2c[#postfix_cpp2c+1] = arg.out_conversion_back
|
|
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, ctype.name)
|
|
func.ret_conversion_cpp2c = string.format(
|
|
"union { bgfx::%s cpp; %s c; } handle_ret;" ,
|
|
ctype.name, ctype.cname)
|
|
func.ret_prefix = "handle_ret.cpp = "
|
|
func.ret_prefix_cpp2c = "handle_ret.c = "
|
|
postfix[#postfix+1] = "return handle_ret.c;"
|
|
postfix_cpp2c[#postfix_cpp2c+1] = "return handle_ret.cpp;"
|
|
elseif func.ret.fulltype ~= "void" then
|
|
local ctype_conversion = ""
|
|
local conversion_back = ""
|
|
if ctype.name ~= ctype.cname then
|
|
if func.ret.ref then
|
|
ctype_conversion = "(" .. func.ret.ctype .. ")&"
|
|
conversion_back = "*(" .. func.ret.ptype .. ")"
|
|
else
|
|
ctype_conversion = "(" .. func.ret.ctype .. ")"
|
|
conversion_back = "(" .. func.ret.cpptype .. ")"
|
|
end
|
|
end
|
|
if #postfix > 0 then
|
|
func.ret_prefix = string.format("%s retValue = %s", func.ret.ctype , ctype_conversion)
|
|
func.ret_prefix_cpp2c = string.format("%s retValue = %s", func.ret.cpptype , conversion_back)
|
|
local ret = "return retValue;"
|
|
postfix[#postfix+1] = ret
|
|
postfix_cpp2c[#postfix_cpp2c+1] = ret
|
|
else
|
|
func.ret_prefix = string.format("return %s", ctype_conversion)
|
|
func.ret_prefix_cpp2c = string.format("return %s", conversion_back)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function convert_vararg(v)
|
|
if v.vararg then
|
|
local args = v.args
|
|
local vararg = {
|
|
name = "",
|
|
fulltype = "...",
|
|
type = "...",
|
|
ctype = "...",
|
|
aname = "argList",
|
|
conversion = string.format(
|
|
"va_list argList;\n\tva_start(argList, %s);",
|
|
args[#args].name),
|
|
}
|
|
args[#args + 1] = vararg
|
|
v.alter_name = v.vararg
|
|
end
|
|
end
|
|
|
|
local function calc_flag_values(flag)
|
|
local shift = flag.shift
|
|
local base = flag.base or 0
|
|
local cap = 1 << (flag.range or 0)
|
|
|
|
if flag.range then
|
|
if flag.range == 64 then
|
|
flag.mask = 0xffffffffffffffff
|
|
else
|
|
flag.mask = ((1 << flag.range) - 1) << shift
|
|
end
|
|
end
|
|
|
|
local values = {}
|
|
for index, item in ipairs(flag.flag) do
|
|
local value = item.value
|
|
if flag.const then
|
|
-- use value directly
|
|
elseif shift then
|
|
if value then
|
|
if value > 0 then
|
|
value = value - 1
|
|
end
|
|
else
|
|
value = index + base - 1
|
|
end
|
|
if value >= cap then
|
|
error (string.format("Out of range for %s.%s (%d/%d)", flag.name, item.name, value, cap))
|
|
end
|
|
value = value << shift
|
|
elseif #item == 0 then
|
|
if value then
|
|
if value > 0 then
|
|
value = 1 << (value - 1)
|
|
end
|
|
else
|
|
local s = index + base - 2
|
|
if s >= 0 then
|
|
value = 1 << s
|
|
else
|
|
value = 0
|
|
end
|
|
end
|
|
end
|
|
if not value then
|
|
-- It's a combine flags
|
|
value = 0
|
|
for _, name in ipairs(item) do
|
|
local v = values[name]
|
|
if v then
|
|
value = value | v
|
|
else
|
|
-- todo : it's a undefined flag
|
|
value = nil
|
|
break
|
|
end
|
|
end
|
|
end
|
|
item.value = value
|
|
values[item.name] = value
|
|
end
|
|
end
|
|
|
|
function codegen.nameconversion(all_types, all_funcs)
|
|
for _,v in ipairs(all_types) do
|
|
local name = v.name
|
|
local cname = v.cname
|
|
if cname == nil then
|
|
if name:match "^%u" then
|
|
cname = camelcase_to_underscorecase(name)
|
|
elseif not v.flag then
|
|
v.cname = name
|
|
end
|
|
end
|
|
if cname and not v.flag then
|
|
if v.namespace then
|
|
cname = camelcase_to_underscorecase(v.namespace) .. "_" .. cname
|
|
end
|
|
v.cname = "bgfx_".. cname .. "_t"
|
|
end
|
|
if v.enum then
|
|
v.typename = v.name
|
|
v.name = v.name .. "::Enum"
|
|
end
|
|
if v.flag then
|
|
calc_flag_values(v)
|
|
end
|
|
end
|
|
|
|
-- make index
|
|
for _,v in ipairs(all_types) do
|
|
if not v.namespace then
|
|
if all_types[v.name] then
|
|
error ("Duplicate type " .. v.name)
|
|
elseif not v.flag then
|
|
all_types[v.name] = v
|
|
end
|
|
end
|
|
end
|
|
|
|
-- make sub struct index
|
|
for _,v in ipairs(all_types) do
|
|
if v.namespace then
|
|
local super = all_types[v.namespace]
|
|
if not super then
|
|
error ("Define " .. v.namespace .. " first")
|
|
end
|
|
local substruct = super.substruct
|
|
if not substruct then
|
|
substruct = {}
|
|
super.substruct = substruct
|
|
end
|
|
if substruct[v.name] then
|
|
error ( "Duplicate sub struct " .. v.name .. " in " .. v.namespace)
|
|
end
|
|
v.parent_class = super
|
|
substruct[#substruct+1] = v
|
|
substruct[v.name] = v
|
|
end
|
|
end
|
|
|
|
for _,v in ipairs(all_types) do
|
|
if v.struct then
|
|
for _, item in ipairs(v.struct) do
|
|
convert_arg(all_types, item, v)
|
|
end
|
|
elseif v.args then
|
|
-- funcptr
|
|
for _, arg in ipairs(v.args) do
|
|
convert_arg(all_types, arg, v)
|
|
end
|
|
convert_vararg(v)
|
|
convert_arg(all_types, v.ret, v)
|
|
end
|
|
end
|
|
|
|
local funcs = {}
|
|
local funcs_conly = {}
|
|
local funcs_alter = {}
|
|
|
|
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
|
|
local classtype = all_types[v.class]
|
|
if classtype then
|
|
local methods = classtype.methods
|
|
if not methods then
|
|
methods = {}
|
|
classtype.methods = methods
|
|
end
|
|
methods[#methods+1] = v
|
|
end
|
|
elseif not v.conly then
|
|
funcs[v.name] = v
|
|
end
|
|
|
|
if v.conly then
|
|
table.insert(funcs_conly, v)
|
|
end
|
|
|
|
for _, arg in ipairs(v.args) do
|
|
convert_arg(all_types, arg, v)
|
|
gen_arg_conversion(all_types, arg)
|
|
end
|
|
convert_vararg(v)
|
|
if v.alter_name then
|
|
funcs_alter[#funcs_alter+1] = v
|
|
end
|
|
convert_arg(all_types, v.ret, v)
|
|
gen_ret_conversion(all_types, v)
|
|
local namespace = v.class
|
|
if namespace then
|
|
local classname = namespace
|
|
if v.const then
|
|
classname = "const " .. classname
|
|
end
|
|
local classtype = { fulltype = classname .. "*" }
|
|
convert_arg(all_types, classtype, v)
|
|
v.this = classtype.ctype
|
|
v.this_type = classtype
|
|
v.this_conversion = string.format( "%s This = (%s)_this;", classtype.cpptype, classtype.cpptype)
|
|
v.this_to_c = string.format("(%s)this", classtype.ctype)
|
|
end
|
|
end
|
|
|
|
for _, v in ipairs(funcs_conly) do
|
|
local func = funcs[v.name]
|
|
if func then
|
|
func.multicfunc = func.multicfunc or { func.cname }
|
|
table.insert(func.multicfunc, v.cname)
|
|
end
|
|
end
|
|
|
|
for _, v in ipairs(funcs_alter) do
|
|
local func = funcs[v.alter_name]
|
|
v.alter_cname = func.cname
|
|
end
|
|
end
|
|
|
|
local function lines(tbl)
|
|
if not tbl or #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 conversion_c2cpp = {}
|
|
local args = {}
|
|
local cargs = {}
|
|
local callargs_conversion = {}
|
|
local callargs_conversion_back = {}
|
|
local callargs = {}
|
|
local cppfunc
|
|
local classname
|
|
|
|
if func.class then
|
|
-- It's a member function
|
|
cargs[1] = func.this .. " _this"
|
|
conversion[1] = func.this_conversion
|
|
cppfunc = "This->" .. func.name
|
|
callargs[1] = "_this"
|
|
callargs_conversion_back[1] = func.this_to_c
|
|
classname = func.class .. "::"
|
|
else
|
|
cppfunc = "bgfx::" .. tostring(func.alter_name or func.name)
|
|
classname = ""
|
|
end
|
|
for _, arg in ipairs(func.args) do
|
|
conversion[#conversion+1] = arg.conversion
|
|
conversion_c2cpp[#conversion_c2cpp+1] = arg.conversion_back
|
|
local cname = arg.ctype .. " " .. arg.name
|
|
if arg.array then
|
|
cname = cname .. (arg.carray or arg.array)
|
|
end
|
|
local name = arg.fulltype .. " " .. arg.name
|
|
if arg.array then
|
|
name = name .. arg.array
|
|
end
|
|
if arg.default ~= nil then
|
|
name = name .. " = " .. tostring(arg.default)
|
|
end
|
|
cargs[#cargs+1] = cname
|
|
args[#args+1] = name
|
|
callargs_conversion[#callargs_conversion+1] = arg.aname or arg.name
|
|
callargs_conversion_back[#callargs_conversion_back+1] = arg.aname_cpp2c or arg.name
|
|
callargs[#callargs+1] = arg.name
|
|
end
|
|
conversion[#conversion+1] = func.ret_conversion
|
|
conversion_c2cpp[#conversion_c2cpp+1] = func.ret_conversion_cpp2c
|
|
|
|
local ARGS
|
|
local args_n = #args
|
|
if args_n == 0 then
|
|
ARGS = ""
|
|
elseif args_n == 1 then
|
|
ARGS = args[1]
|
|
else
|
|
ARGS = "\n\t " .. table.concat(args, "\n\t, ") .. "\n\t"
|
|
end
|
|
|
|
local preret_c2c
|
|
local postret_c2c = {}
|
|
local conversion_c2c = {}
|
|
local callfunc_c2c
|
|
|
|
if func.vararg then
|
|
postret_c2c[1] = "va_end(argList);"
|
|
local vararg = func.args[#func.args]
|
|
callargs[#callargs] = vararg.aname
|
|
callargs_conversion_back[#callargs_conversion_back] = vararg.aname
|
|
conversion_c2c[1] = vararg.conversion
|
|
conversion_c2cpp[1] = vararg.conversion
|
|
|
|
if func.ret.fulltype == "void" then
|
|
preret_c2c = ""
|
|
else
|
|
preret_c2c = func.ret.ctype .. " retValue = "
|
|
postret_c2c[#postret_c2c+1] = "return retValue;"
|
|
end
|
|
callfunc_c2c = func.alter_cname or func.cname
|
|
else
|
|
if func.ret.fulltype == "void" then
|
|
preret_c2c = ""
|
|
else
|
|
preret_c2c = "return "
|
|
end
|
|
callfunc_c2c = func.cname
|
|
end
|
|
|
|
outCargs = table.concat(cargs, ", ")
|
|
if outCargs == "" then
|
|
outCargs = "void"
|
|
end
|
|
|
|
return {
|
|
RET = func.ret.fulltype,
|
|
CRET = func.ret.ctype,
|
|
CFUNCNAME = func.cname,
|
|
CFUNCNAMEUPPER = func.cname:upper(),
|
|
CFUNCNAMECAML = underscorecase_to_camelcase(func.cname),
|
|
FUNCNAME = func.name,
|
|
CARGS = outCargs,
|
|
CPPARGS = table.concat(args, ", "),
|
|
ARGS = ARGS,
|
|
CONVERSION = lines(conversion),
|
|
CONVERSIONCTOC = lines(conversion_c2c),
|
|
CONVERSIONCTOCPP = lines(conversion_c2cpp),
|
|
PRERET = func.ret_prefix or "",
|
|
PRERETCPPTOC = func.ret_prefix_cpp2c or "",
|
|
CPPFUNC = cppfunc,
|
|
CALLFUNCCTOC = callfunc_c2c,
|
|
CALLARGSCTOCPP = table.concat(callargs_conversion, ", "),
|
|
CALLARGSCPPTOC = table.concat(callargs_conversion_back, ", "),
|
|
CALLARGS = table.concat(callargs, ", "),
|
|
POSTRET = lines(func.ret_postfix),
|
|
POSTRETCPPTOC = lines(func.ret_postfix_cpp2c),
|
|
PRERETCTOC = preret_c2c,
|
|
POSTRETCTOC = lines(postret_c2c),
|
|
CLASSNAME = classname,
|
|
CONST = func.const and " const" or "",
|
|
}
|
|
end
|
|
|
|
local function apply_template(func, temp)
|
|
func.codetemp = func.codetemp or codetemp(func)
|
|
return (temp:gsub("$(%u+)", func.codetemp))
|
|
end
|
|
|
|
function codegen.apply_functemp(func, temp)
|
|
return remove_emptylines(apply_template(func, temp))
|
|
end
|
|
|
|
function codegen.gen_funcptr(funcptr)
|
|
return apply_template(funcptr, "typedef $RET (*$FUNCNAME)($ARGS);")
|
|
end
|
|
|
|
function codegen.gen_cfuncptr(funcptr)
|
|
return apply_template(funcptr, "typedef $CRET (*$CFUNCNAME)($CARGS);")
|
|
end
|
|
|
|
local function doxygen_funcret(r, func, prefix)
|
|
if not func or func.ret.fulltype == "void" or func.ret.comment == nil then
|
|
return
|
|
end
|
|
r[#r+1] = prefix
|
|
r[#r+1] = string.format("%s @returns %s", prefix, func.ret.comment[1])
|
|
for i = 2,#func.ret.comment do
|
|
r[#r+1] = string.format("%s %s", prefix, func.ret.comment[i])
|
|
end
|
|
return r
|
|
end
|
|
|
|
local function doxygen_func(r, func, prefix)
|
|
if not func or not func.args or #func.args == 0 then
|
|
return
|
|
end
|
|
r[#r+1] = prefix
|
|
for _, arg in ipairs(func.args) do
|
|
local inout
|
|
if arg.out then
|
|
inout = "out"
|
|
elseif arg.inout then
|
|
inout = "inout"
|
|
else
|
|
inout = "in"
|
|
end
|
|
local comment = string.format("%s @param[%s] %s", prefix, inout, arg.name)
|
|
if arg.comment then
|
|
r[#r+1] = comment .. " " .. arg.comment[1]
|
|
for i = 2,#arg.comment do
|
|
r[#r+1] = string.format("%s %s", prefix, arg.comment[i])
|
|
end
|
|
else
|
|
r[#r+1] = comment
|
|
end
|
|
end
|
|
doxygen_funcret(r, func, prefix)
|
|
return r
|
|
end
|
|
|
|
function codegen.doxygen_type(doxygen, func, cname)
|
|
if doxygen == nil then
|
|
return
|
|
end
|
|
local result = {}
|
|
for _, line in ipairs(doxygen) do
|
|
result[#result+1] = "/// " .. line
|
|
end
|
|
doxygen_func(result, func, "///")
|
|
if cname then
|
|
result[#result+1] = "///"
|
|
if type(cname) == "string" then
|
|
result[#result+1] = string.format("/// @attention C99's equivalent binding is `%s`.", cname)
|
|
else
|
|
local names = {}
|
|
for _, v in ipairs(cname) do
|
|
names[#names+1] = "`" .. v .. "`"
|
|
end
|
|
result[#result+1] = string.format("/// @attention C99's equivalent bindings are %s.", table.concat(names, ","))
|
|
end
|
|
end
|
|
result[#result+1] = "///"
|
|
return table.concat(result, "\n")
|
|
end
|
|
|
|
function codegen.doxygen_ctype(doxygen, func)
|
|
if doxygen == nil then
|
|
return
|
|
end
|
|
local result = {
|
|
"/**",
|
|
}
|
|
for _, line in ipairs(doxygen) do
|
|
result[#result+1] = " * " .. line
|
|
end
|
|
doxygen_func(result, func, " *")
|
|
result[#result+1] = " *"
|
|
result[#result+1] = " */"
|
|
return table.concat(result, "\n")
|
|
end
|
|
|
|
local enum_temp = [[
|
|
struct $NAME
|
|
{
|
|
$COMMENT
|
|
enum Enum
|
|
{
|
|
$ITEMS
|
|
|
|
Count
|
|
};
|
|
};
|
|
]]
|
|
|
|
function codegen.gen_enum_define(enum)
|
|
assert(type(enum.enum) == "table", "Not an enum")
|
|
local items = {}
|
|
for _, item in ipairs(enum.enum) do
|
|
local text
|
|
if not item.comment then
|
|
text = item.name .. ","
|
|
else
|
|
local comment = table.concat(item.comment, " ")
|
|
text = string.format("%s,%s //!< %s",
|
|
item.name, namealign(item.name), comment)
|
|
end
|
|
items[#items+1] = text
|
|
end
|
|
local comment = ""
|
|
if enum.comment then
|
|
comment = "/// " .. enum.comment
|
|
end
|
|
local temp = {
|
|
NAME = enum.typename,
|
|
COMMENT = comment,
|
|
ITEMS = table.concat(items, "\n\t\t"),
|
|
}
|
|
return (enum_temp:gsub("$(%u+)", temp))
|
|
end
|
|
|
|
local cenum_temp = [[
|
|
typedef enum $NAME
|
|
{
|
|
$ITEMS
|
|
|
|
$COUNT
|
|
|
|
} $NAME_t;
|
|
]]
|
|
function codegen.gen_enum_cdefine(enum)
|
|
assert(type(enum.enum) == "table", "Not an enum")
|
|
local cname = enum.cname:match "(.-)_t$"
|
|
local uname = cname:upper()
|
|
local items = {}
|
|
for index , item in ipairs(enum.enum) do
|
|
local comment = ""
|
|
if item.comment then
|
|
comment = table.concat(item.comment, " ")
|
|
end
|
|
local ename = item.cname
|
|
if not ename then
|
|
if enum.underscore then
|
|
ename = camelcase_to_underscorecase(item.name)
|
|
else
|
|
ename = item.name
|
|
end
|
|
ename = ename:upper()
|
|
end
|
|
local name = uname .. "_" .. ename
|
|
items[#items+1] = string.format("%s,%s /** (%2d) %s%s */",
|
|
name,
|
|
namealign(name, 40),
|
|
index - 1,
|
|
comment,
|
|
namealign(comment, 30))
|
|
end
|
|
|
|
local temp = {
|
|
NAME = cname,
|
|
COUNT = uname .. "_COUNT",
|
|
ITEMS = table.concat(items, "\n\t"),
|
|
}
|
|
|
|
return (cenum_temp:gsub("$(%u+)", temp))
|
|
end
|
|
|
|
local function flag_format(flag)
|
|
if not flag.format then
|
|
flag.format = "%0" .. (flag.bits // 4) .. "x"
|
|
end
|
|
end
|
|
|
|
function codegen.gen_flag_cdefine(flag)
|
|
assert(type(flag.flag) == "table", "Not a flag")
|
|
flag_format(flag)
|
|
local cname = "BGFX_" .. (flag.cname or to_underscorecase(flag.name):upper())
|
|
local s = {}
|
|
local shift = flag.shift
|
|
for index, item in ipairs(flag.flag) do
|
|
local name
|
|
if item.cname then
|
|
name = cname .. "_" .. item.cname
|
|
else
|
|
name = cname .. "_" .. to_underscorecase(item.name):upper()
|
|
end
|
|
local value = item.value
|
|
|
|
-- combine flags
|
|
if #item > 0 then
|
|
if item.comment then
|
|
for _, c in ipairs(item.comment) do
|
|
s[#s+1] = "/// " .. c
|
|
end
|
|
end
|
|
local sets = { "" }
|
|
for _, v in ipairs(item) do
|
|
sets[#sets+1] = cname .. "_" .. to_underscorecase(v):upper()
|
|
end
|
|
s[#s+1] = string.format("#define %s (0%s \\\n\t)\n", name, table.concat(sets, " \\\n\t| "))
|
|
else
|
|
local comment = ""
|
|
if item.comment then
|
|
if #item.comment > 1 then
|
|
s[#s+1] = ""
|
|
for _, c in ipairs(item.comment) do
|
|
s[#s+1] = "/// " .. c
|
|
end
|
|
else
|
|
comment = " //!< " .. item.comment[1]
|
|
end
|
|
end
|
|
value = string.format(flag.format, value)
|
|
local code = string.format("#define %s %sUINT%d_C(0x%s)%s",
|
|
name, namealign(name, DEFINE_NAME_ALIGN), flag.bits, value, comment)
|
|
s[#s+1] = code
|
|
end
|
|
end
|
|
|
|
local mask
|
|
if flag.mask then
|
|
mask = string.format(flag.format, flag.mask)
|
|
mask = string.format("UINT%d_C(0x%s)", flag.bits, mask)
|
|
end
|
|
|
|
if shift then
|
|
local name = cname .. "_SHIFT"
|
|
local comment = flag.desc or ""
|
|
local shift_align = tostring(shift)
|
|
shift_align = shift_align .. namealign(shift_align, #mask)
|
|
local comment = ""
|
|
if flag.desc then
|
|
comment = string.format(" //!< %s bit shift", flag.desc)
|
|
end
|
|
local code = string.format("#define %s %s%s%s", name, namealign(name, DEFINE_NAME_ALIGN), shift_align, comment)
|
|
s[#s+1] = code
|
|
end
|
|
if flag.range then
|
|
local name = cname .. "_MASK"
|
|
local comment = ""
|
|
if flag.desc then
|
|
comment = string.format(" //!< %s bit mask", flag.desc)
|
|
end
|
|
local code = string.format("#define %s %s%s%s", name, namealign(name, DEFINE_NAME_ALIGN), mask, comment)
|
|
s[#s+1] = code
|
|
end
|
|
|
|
if flag.helper then
|
|
s[#s+1] = string.format(
|
|
"#define %s(v) ( ( (uint%d_t)(v)<<%s )&%s)",
|
|
cname,
|
|
flag.bits,
|
|
(cname .. "_SHIFT"),
|
|
(cname .. "_MASK"))
|
|
end
|
|
|
|
s[#s+1] = ""
|
|
|
|
return table.concat(s, "\n")
|
|
end
|
|
|
|
local function text_with_comments(items, item, cstyle, is_classmember)
|
|
local name = item.name
|
|
if item.array then
|
|
if cstyle then
|
|
name = name .. (item.carray or item.array)
|
|
else
|
|
name = name .. item.array
|
|
end
|
|
end
|
|
local typename
|
|
if cstyle then
|
|
typename = item.ctype
|
|
else
|
|
typename = item.fulltype
|
|
end
|
|
if is_classmember then
|
|
name = "m_" .. name
|
|
end
|
|
local text = string.format("%s%s %s;", typename, namealign(typename), name)
|
|
if item.comment then
|
|
if #item.comment > 1 then
|
|
table.insert(items, "")
|
|
if cstyle then
|
|
table.insert(items, "/**")
|
|
for _, c in ipairs(item.comment) do
|
|
table.insert(items, " * " .. c)
|
|
end
|
|
table.insert(items, " */")
|
|
else
|
|
for _, c in ipairs(item.comment) do
|
|
table.insert(items, "/// " .. c)
|
|
end
|
|
end
|
|
else
|
|
text = string.format(
|
|
cstyle and "%s %s/** %s%s */" or "%s %s//!< %s",
|
|
text, namealign(text, 40), item.comment[1], namealign(item.comment[1], 40))
|
|
end
|
|
end
|
|
items[#items+1] = text
|
|
end
|
|
|
|
local struct_temp = [[
|
|
struct $NAME
|
|
{
|
|
$METHODS
|
|
$SUBSTRUCTS
|
|
$ITEMS
|
|
};
|
|
]]
|
|
|
|
function codegen.gen_struct_define(struct, methods)
|
|
assert(type(struct.struct) == "table", "Not a struct")
|
|
local items = {}
|
|
for _, item in ipairs(struct.struct) do
|
|
text_with_comments(items, item, false, methods ~= nil and not struct.shortname)
|
|
end
|
|
local ctor = {}
|
|
if struct.ctor then
|
|
ctor[1] = struct.name .. "();"
|
|
ctor[2] = ""
|
|
end
|
|
if methods then
|
|
for _, m in ipairs(methods) do
|
|
if m:sub(-1) ~= "\n" then
|
|
m = m .. "\n"
|
|
end
|
|
for line in m:gmatch "(.-)\n" do
|
|
ctor[#ctor+1] = line
|
|
end
|
|
ctor[#ctor+1] = ""
|
|
end
|
|
end
|
|
local subs = {}
|
|
if struct.substruct then
|
|
for _, v in ipairs(struct.substruct) do
|
|
local s = codegen.gen_struct_define(v)
|
|
s = s:gsub("\n", "\n\t")
|
|
subs[#subs+1] = s
|
|
end
|
|
end
|
|
|
|
local temp = {
|
|
NAME = struct.name,
|
|
SUBSTRUCTS = lines(subs),
|
|
ITEMS = table.concat(items, "\n\t"),
|
|
METHODS = lines(ctor),
|
|
}
|
|
return remove_emptylines(struct_temp:gsub("$(%u+)", temp))
|
|
end
|
|
|
|
local cstruct_temp = [[
|
|
typedef struct $NAME_s
|
|
{
|
|
$ITEMS
|
|
|
|
} $NAME_t;
|
|
]]
|
|
local cstruct_empty_temp = [[
|
|
struct $NAME_s;
|
|
typedef struct $NAME_s $NAME_t;
|
|
]]
|
|
function codegen.gen_struct_cdefine(struct)
|
|
assert(type(struct.struct) == "table", "Not a struct")
|
|
local cname = struct.cname:match "(.-)_t$"
|
|
local items = {}
|
|
for _, item in ipairs(struct.struct) do
|
|
text_with_comments(items, item, true)
|
|
end
|
|
local temp = {
|
|
NAME = cname,
|
|
ITEMS = table.concat(items, "\n\t"),
|
|
}
|
|
local codetemp = #struct.struct == 0 and cstruct_empty_temp or cstruct_temp
|
|
return (codetemp:gsub("$(%u+)", temp))
|
|
end
|
|
|
|
local chandle_temp = [[
|
|
typedef struct $NAME_s { uint16_t idx; } $NAME_t;
|
|
]]
|
|
function codegen.gen_chandle(handle)
|
|
assert(handle.handle, "Not a handle")
|
|
return (chandle_temp:gsub("$(%u+)", { NAME = handle.cname:match "(.-)_t$" }))
|
|
end
|
|
|
|
local handle_temp = [[
|
|
struct $NAME { uint16_t idx; };
|
|
inline bool isValid($NAME _handle) { return bgfx::kInvalidHandle != _handle.idx; }
|
|
]]
|
|
function codegen.gen_handle(handle)
|
|
assert(handle.handle, "Not a handle")
|
|
return (handle_temp:gsub("$(%u+)", { NAME = handle.name }))
|
|
end
|
|
|
|
local idl = require "idl"
|
|
local doxygen = require "doxygen"
|
|
local conversion
|
|
local idlfile = {}
|
|
|
|
function codegen.load(filename)
|
|
assert(conversion == nil, "Don't call codegen.load() after codegen.idl()")
|
|
assert(idlfile[filename] == nil, "Duplicate load " .. filename)
|
|
local source = doxygen.load(filename)
|
|
|
|
local f = assert(load(source, filename , "t", idl))
|
|
f()
|
|
idlfile[filename] = true
|
|
end
|
|
|
|
function codegen.idl(filename)
|
|
if conversion == nil then
|
|
if filename and not idlfile[filename] then
|
|
codegen.load(filename)
|
|
end
|
|
assert(next(idlfile), "call codegen.load() first")
|
|
conversion = true
|
|
codegen.nameconversion(idl.types, idl.funcs)
|
|
end
|
|
return idl
|
|
end
|
|
|
|
return codegen
|