diff options
author | 2019-10-13 13:50:38 +0200 | |
---|---|---|
committer | 2019-10-13 07:50:38 -0400 | |
commit | 0837e7451a84f95c29dbdb9bd6b8b931fee1635d (patch) | |
tree | 626bcbd250a7fbebdf5958a288d2f438d4f9fb5a /3rdparty/bgfx/scripts/codegen.lua | |
parent | c913ccb59d713ce0b91135520719ac5385e54358 (diff) |
WIP: sync bgfx, bx and bimg with latest upstream (#5723)
* Sync with bgfx upstream revision b91d0b6
* Sync with bx upstream revision d60912b
* Sync with bimg upstream revision bd81f60
* Add astc-codec decoder
* Rename VertexDecl to VertexLayout
* Rename UniformType enum Int1 to Sampler.
* Add NVN stub
* Fix unused-const-variable error on macOS
* Drop redundant explicit language parameters
buildoptions_cpp are only applied to c++ files and buildoptions_objcpp are only
applied to objective c++ files. As such, hardcoding -x offers no benefit while
preventing overrides (such as one needed by 3rdparty/bgfx/src/renderer_vk.cpp on
macOS) from working.
* Re-introduce -x c++ in places where C code is compiled as C++ to prevent clang from throwing a warning
* Build bgfx as Objective-C++ on macOS
It is needed due to included headers
* Enable Direct3D12 and Vulkan bgfx rendering backends
* Enable building of spirv shaders
* Properly escape /c in cmd call
* Comment out dx12 bgfx renderer
* Honor VERBOSE setting during shaders build
* Only invert hlsl shader XYZ_TO_sRGB matrix for opengl
* Add spirv shaders
* OpenGL ES needs transposed matrix too
* Metal needs transposed matrix as well
Diffstat (limited to '3rdparty/bgfx/scripts/codegen.lua')
-rw-r--r-- | 3rdparty/bgfx/scripts/codegen.lua | 994 |
1 files changed, 994 insertions, 0 deletions
diff --git a/3rdparty/bgfx/scripts/codegen.lua b/3rdparty/bgfx/scripts/codegen.lua new file mode 100644 index 00000000000..3b6a3a937ce --- /dev/null +++ b/3rdparty/bgfx/scripts/codegen.lua @@ -0,0 +1,994 @@ +-- 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 NAMEALIGN = 20 + +local function namealign(name, align) + align = align or NAMEALIGN + 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 equivalent is `%s`.", cname) + else + local names = {} + for _, v in ipairs(cname) do + names[#names+1] = "`" .. v .. "`" + end + result[#result+1] = string.format("/// @attention C99 equivalent 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 + 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, 35), 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, 35), 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, 35), 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 |