bgfx/scripts/bgfx-codegen.lua

331 lines
6.9 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 = require "codegen"
local idl = codegen.idl "bgfx.idl"
local func_actions = {
c99 = "\n",
c99decl = "\n",
cppdecl = "\n",
interface_struct = "\n\t",
interface_import = ",\n\t\t\t",
c99_interface = "\n",
cpp_interface = "\n",
c99_functionid = "\n\t",
cpp_functionid = "\n\t\t",
}
local type_actions = {
cflags = "\n",
enums = "\n",
cenums = "\n",
structs = "\n",
cstructs = "\n",
handles = "\n",
chandles = "\n",
funcptrs = "\n",
cfuncptrs = "\n",
}
local function cfunc(f)
return function(func)
if not func.cpponly then
return f(func)
end
end
end
local funcgen = {}
local functemp = {}
functemp.interface_struct = "$CRET (*$CFUNCNAME)($CARGS);"
functemp.interface_import = "bgfx_$CFUNCNAME"
functemp.c99_interface = [[
BGFX_C_API $CRET bgfx_$CFUNCNAME($CARGS)
{
$CONVERSIONCTOC
$PRERETCTOCg_interface->$CFUNCNAME($CALLARGS);
$POSTRETCTOC
}
]]
functemp.c99_functionid = "BGFX_FUNCTION_ID_$CFUNCNAMEUPPER,"
functemp.cpp_functionid = "$CFUNCNAMECAML,"
for action,temp in pairs(functemp) do
funcgen[action] = cfunc(function(func)
return codegen.apply_functemp(func, temp)
end)
end
funcgen.cpp_interface= cfunc(function(func)
if not func.cfunc and not func.conly then
return codegen.apply_functemp(func, [[
$RET $CLASSNAME$FUNCNAME($CPPARGS)$CONST
{
$CONVERSIONCTOCPP
$PRERETCPPTOCg_interface->$CFUNCNAME($CALLARGSCPPTOC);
$POSTRETCPPTOC
}
]])
end
end)
funcgen.c99 = cfunc(function(func)
local temp
if func.cfunc then
temp = "/* BGFX_C_API $CRET bgfx_$CFUNCNAME($CARGS) */\n"
else
temp = [[
BGFX_C_API $CRET bgfx_$CFUNCNAME($CARGS)
{
$CONVERSION
$PRERET$CPPFUNC($CALLARGSCTOCPP);
$POSTRET
}
]]
end
return codegen.apply_functemp(func, temp)
end)
local function cppdecl(func)
local doc = func.comments
if not doc and func.comment then
doc = { func.comment }
end
if doc then
local cname
if not func.cpponly then
if func.multicfunc then
cname = {}
for _, name in ipairs(func.multicfunc) do
cname[#cname+1] = "bgfx_" .. name
end
else
cname = "bgfx_" .. func.cname
end
end
doc = codegen.doxygen_type(doc, func, cname)
end
local funcdecl = codegen.apply_functemp(func, "$RET $FUNCNAME($ARGS)$CONST;\n")
if doc then
return doc .. "\n" .. funcdecl
else
return funcdecl
end
end
function funcgen.cppdecl(func)
-- Don't generate member functions here
if not func.class and not func.conly then
return cppdecl(func)
end
end
funcgen.c99decl = cfunc(function(func)
local doc = func.comments
if not doc and func.comment then
doc = { func.comment }
end
if doc then
doc = codegen.doxygen_ctype(doc, func)
end
local funcdecl = codegen.apply_functemp(func, "BGFX_C_API $CRET bgfx_$CFUNCNAME($CARGS);")
if doc then
return "\n" .. doc .. "\n" .. funcdecl
else
return funcdecl
end
end)
local typegen = {}
local function add_doxygen(typedef, define, cstyle, cname)
local func = cstyle and codegen.doxygen_ctype or codegen.doxygen_type
local doc = func(typedef.comments, typedef, cname or typedef.cname)
if doc then
return doc .. "\n" .. define
else
return define
end
end
function typegen.enums(typedef)
if typedef.enum then
return add_doxygen(typedef, codegen.gen_enum_define(typedef), false, "bgfx_" .. typedef.cname)
end
end
function typegen.cenums(typedef)
if typedef.enum then
return add_doxygen(typedef, codegen.gen_enum_cdefine(typedef), true)
end
end
function typegen.cflags(typedef)
if typedef.flag then
return add_doxygen(typedef, codegen.gen_flag_cdefine(typedef), true)
end
end
function typegen.structs(typedef)
if typedef.struct and not typedef.namespace then
local methods = typedef.methods
if methods then
local m = {}
for _, func in ipairs(methods) do
m[#m+1] = cppdecl(func)
end
methods = m
end
return add_doxygen(typedef, codegen.gen_struct_define(typedef, methods))
end
end
function typegen.cstructs(typedef)
if typedef.struct then
return add_doxygen(typedef, codegen.gen_struct_cdefine(typedef), true)
end
end
function typegen.handles(typedef)
if typedef.handle then
return codegen.gen_handle(typedef)
end
end
function typegen.chandles(typedef)
if typedef.handle then
return codegen.gen_chandle(typedef)
end
end
function typegen.funcptrs(typedef)
if typedef.args then
return add_doxygen(typedef, codegen.gen_funcptr(typedef))
end
end
function typegen.cfuncptrs(typedef)
if typedef.args then
return add_doxygen(typedef, codegen.gen_cfuncptr(typedef), true)
end
end
local function codes()
local temp = {}
for k in pairs(func_actions) do
temp[k] = {}
end
for k in pairs(type_actions) do
temp[k] = {}
end
-- call actions with func
for _, f in ipairs(idl.funcs) do
for k in pairs(func_actions) do
local funcgen = funcgen[k]
if funcgen then
table.insert(temp[k], (funcgen(f)))
end
end
end
-- call actions with type
for _, typedef in ipairs(idl.types) do
for k in pairs(type_actions) do
local typegen = typegen[k]
if typegen then
table.insert(temp[k], (typegen(typedef)))
end
end
end
for k, indent in pairs(func_actions) do
temp[k] = table.concat(temp[k], indent)
end
for k, indent in pairs(type_actions) do
temp[k] = table.concat(temp[k], indent)
end
temp.version = string.format("#define BGFX_API_VERSION UINT32_C(%d)", idl._version or 0)
return temp
end
local codes_tbl = codes()
local function add_path(filename)
local path
if type(paths) == "string" then
path = paths
else
path = assert(paths[filename])
end
return path .. "/" .. filename
end
local function change_indent(str, indent)
if indent == "\t" then
-- strip trailing space only
return (str:gsub("(.-)\n", function (line)
return line:gsub("([ \t]*)$","\n") end))
else
return (str:gsub("(.-)\n", function (line)
return line:gsub("^(\t*)(.-)[ \t]*$",
function (tabs, content)
return indent:rep(#tabs) .. content .. "\n"
end)
end))
end
end
local gen = {}
function gen.apply(tempfile)
local f = assert(io.open(tempfile, "rb"))
local temp = f:read "a"
f:close()
codes_tbl.source = tempfile
return (temp:gsub("$([%l%d_]+)", codes_tbl))
end
function gen.format(codes, f)
return change_indent(codes, f.indent)
end
function gen.changed(codes, outputfile)
local out = io.open(outputfile, "rb")
if out then
local origin = out:read "a"
out:close()
return origin ~= codes
end
return true
end
function gen.write(codes, outputfile)
local out = assert(io.open(outputfile, "wb"))
out:write(codes)
out:close()
end
function gen.gen(tempfile, outputfile, indent)
print ("Generate", outputfile, "from", tempfile)
local codes = gen.apply(tempfile)
codes = change_indent(codes, indent)
if not gen.changed(codes, outputfile) then
print("No change")
end
gen.write(codes, outputfile)
end
return gen