codegen.lua 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. -- Copyright 2019 云风 https://github.com/cloudwu . All rights reserved.
  2. -- License (the same with bgfx) : https://github.com/bkaradzic/bgfx/blob/master/LICENSE
  3. local codegen = {}
  4. local function camelcase_to_underscorecase(name)
  5. local tmp = {}
  6. for v in name:gmatch "[%u%d]+%l*" do
  7. tmp[#tmp+1] = v:lower()
  8. end
  9. return table.concat(tmp, "_")
  10. end
  11. local function convert_typename(name)
  12. if name:match "^%u" then
  13. return "bgfx_" .. camelcase_to_underscorecase(name) .. "_t"
  14. else
  15. return name
  16. end
  17. end
  18. local function convert_funcname(name)
  19. name = name:gsub("^%l", string.upper) -- Change to upper CamlCase
  20. return camelcase_to_underscorecase(name)
  21. end
  22. local function convert_arg(all_types, arg, what)
  23. local t, postfix = arg.fulltype:match "(%a[%a%d_:]*)%s*([*&]+)%s*$"
  24. if t then
  25. arg.type = t
  26. if postfix == "&" then
  27. arg.ref = true
  28. end
  29. else
  30. arg.type = arg.fulltype
  31. end
  32. local ctype = all_types[arg.type]
  33. if not ctype then
  34. error ("Undefined type " .. arg.fulltype .. " for " .. what)
  35. end
  36. arg.ctype = arg.fulltype:gsub(arg.type, ctype.cname):gsub("&", "*")
  37. if ctype.cname ~= arg.type then
  38. arg.cpptype = arg.fulltype:gsub(arg.type, "bgfx::"..arg.type)
  39. else
  40. arg.cpptype = arg.fulltype
  41. end
  42. if arg.ref then
  43. arg.ptype = arg.cpptype:gsub("&", "*")
  44. end
  45. end
  46. local function alternative_name(name)
  47. if name:sub(1,1) == "_" then
  48. return name:sub(2)
  49. else
  50. return name .. "_"
  51. end
  52. end
  53. local function gen_arg_conversion(all_types, arg)
  54. if arg.ctype == arg.fulltype then
  55. -- do not need conversion
  56. arg.aname = arg.name
  57. return
  58. end
  59. local ctype = all_types[arg.type]
  60. if ctype.handle and arg.type == arg.fulltype then
  61. local aname = alternative_name(arg.name)
  62. arg.aname = aname .. ".cpp"
  63. arg.conversion = string.format(
  64. "union { %s c; bgfx::%s cpp; } %s = { %s };" ,
  65. ctype.cname, arg.type, aname, arg.name)
  66. elseif arg.ref then
  67. if ctype.cname == arg.type then
  68. arg.aname = "*" .. arg.name
  69. elseif arg.out and ctype.enum then
  70. local aname = alternative_name(arg.name)
  71. local cpptype = arg.cpptype:match "(.-)%s*&" -- remove &
  72. arg.aname = aname
  73. arg.conversion = string.format("%s %s;", cpptype, aname)
  74. arg.out_conversion = string.format("*%s = (%s)%s;", arg.name, ctype.cname, aname)
  75. else
  76. arg.aname = alternative_name(arg.name)
  77. arg.conversion = string.format(
  78. "%s %s = *(%s)%s;",
  79. arg.cpptype, arg.aname, arg.ptype, arg.name)
  80. end
  81. else
  82. arg.aname = string.format(
  83. "(%s)%s",
  84. arg.cpptype, arg.name)
  85. end
  86. end
  87. local function gen_ret_conversion(all_types, func)
  88. local postfix = { func.vararg and "va_end(argList);" }
  89. func.ret_postfix = postfix
  90. for _, arg in ipairs(func.args) do
  91. if arg.out_conversion then
  92. postfix[#postfix+1] = arg.out_conversion
  93. end
  94. end
  95. local ctype = all_types[func.ret.type]
  96. if ctype.handle then
  97. func.ret_conversion = string.format(
  98. "union { %s c; bgfx::%s cpp; } handle_ret;" ,
  99. ctype.cname, func.ret.type)
  100. func.ret_prefix = "handle_ret.cpp = "
  101. postfix[#postfix+1] = "return handle_ret.c;"
  102. elseif func.ret.fulltype ~= "void" then
  103. local ctype_conversion = func.ret.type == func.ret.ctype and "" or ("(" .. func.ret.ctype .. ")")
  104. if #postfix > 0 then
  105. func.ret_prefix = string.format("%s retValue = %s", func.ret.ctype , ctype_conversion)
  106. postfix[#postfix+1] = "return retValue;"
  107. else
  108. func.ret_prefix = string.format("return %s", ctype_conversion)
  109. end
  110. end
  111. end
  112. function codegen.nameconversion(all_types, all_funcs)
  113. local enums = {}
  114. for k,v in pairs(all_types) do
  115. if not v.cname then
  116. v.cname = convert_typename(k)
  117. end
  118. if v.enum then
  119. enums[#enums+1] = k
  120. end
  121. end
  122. for _, e in ipairs(enums) do
  123. local t = all_types[e]
  124. all_types[e] = nil
  125. all_types[e .. "::Enum"] = t
  126. end
  127. for _,v in ipairs(all_funcs) do
  128. if v.cname == nil then
  129. v.cname = convert_funcname(v.name)
  130. end
  131. if v.class then
  132. v.cname = convert_funcname(v.class) .. "_" .. v.cname
  133. end
  134. for _, arg in ipairs(v.args) do
  135. convert_arg(all_types, arg, v.name)
  136. gen_arg_conversion(all_types, arg)
  137. end
  138. if v.vararg then
  139. local args = v.args
  140. local vararg = {
  141. name = "",
  142. ctype = "...",
  143. aname = "argList",
  144. conversion = string.format(
  145. "va_list argList;\n\tva_start(argList, %s);",
  146. args[#args].name),
  147. }
  148. args[#args + 1] = vararg
  149. v.implname = v.vararg
  150. else
  151. v.implname = v.name
  152. end
  153. convert_arg(all_types, v.ret, v.name .. "@rettype")
  154. gen_ret_conversion(all_types, v)
  155. if v.class then
  156. local classname = v.class
  157. if v.const then
  158. classname = "const " .. classname
  159. end
  160. local classtype = { fulltype = classname .. "*" }
  161. convert_arg(all_types, classtype, "class member " .. v.name)
  162. v.this = classtype.ctype .. " _this"
  163. v.this_conversion = string.format( "%s This = (%s)_this;", classtype.cpptype, classtype.cpptype)
  164. end
  165. end
  166. end
  167. local function lines(tbl)
  168. if #tbl == 0 then
  169. return "//EMPTYLINE"
  170. else
  171. return table.concat(tbl, "\n\t")
  172. end
  173. end
  174. local function remove_emptylines(txt)
  175. return (txt:gsub("\t//EMPTYLINE\n", ""))
  176. end
  177. local function codetemp(func)
  178. local conversion = {}
  179. local args = {}
  180. local callargs = {}
  181. local cppfunc
  182. if func.class then
  183. -- It's a member function
  184. args[1] = func.this
  185. conversion[1] = func.this_conversion
  186. cppfunc = "This->" .. func.name
  187. else
  188. cppfunc = "bgfx::" .. func.implname
  189. end
  190. for _, arg in ipairs(func.args) do
  191. conversion[#conversion+1] = arg.conversion
  192. args[#args+1] = arg.ctype .. " " .. arg.name
  193. callargs[#callargs+1] = arg.aname
  194. end
  195. conversion[#conversion+1] = func.ret_conversion
  196. return {
  197. RET = func.ret.ctype,
  198. FUNCNAME = func.cname,
  199. ARGS = table.concat(args, ", "),
  200. CONVERSION = lines(conversion),
  201. PRERET = func.ret_prefix or "",
  202. CPPFUNC = cppfunc,
  203. CALLARGS = table.concat(callargs, ", "),
  204. POSTRET = lines(func.ret_postfix),
  205. CODE = func.cfunc,
  206. }
  207. end
  208. local function apply_template(func, temp)
  209. func.codetemp = func.codetemp or codetemp(func)
  210. return (temp:gsub("$(%u+)", func.codetemp))
  211. end
  212. local c99temp = [[
  213. BGFX_C_API $RET bgfx_$FUNCNAME($ARGS)
  214. {
  215. $CONVERSION
  216. $PRERET$CPPFUNC($CALLARGS);
  217. $POSTRET
  218. }
  219. ]]
  220. local c99usertemp = [[
  221. BGFX_C_API $RET bgfx_$FUNCNAME($ARGS)
  222. {
  223. $CODE
  224. }
  225. ]]
  226. function codegen.gen_c99(func)
  227. if func.cfunc then
  228. return apply_template(func, c99usertemp)
  229. else
  230. return remove_emptylines(apply_template(func, c99temp))
  231. end
  232. end
  233. local template_function_declaration = [[
  234. /**/
  235. BGFX_C_API $RET bgfx_$FUNCNAME($ARGS);
  236. ]]
  237. function codegen.gen_c99decl(func)
  238. return apply_template(func, template_function_declaration)
  239. end
  240. function codegen.gen_interface_struct(func)
  241. return apply_template(func, "$RET (*$FUNCNAME)($ARGS);")
  242. end
  243. function codegen.gen_interface_import(func)
  244. return "bgfx_" .. func.cname
  245. end
  246. return codegen