codegen.lua 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  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 NAMEALIGN = 20
  5. local function namealign(name, align)
  6. align = align or NAMEALIGN
  7. return string.rep(" ", align - #name)
  8. end
  9. local function camelcase_to_underscorecase(name)
  10. local tmp = {}
  11. for v in name:gmatch "[%u%d]+%l*" do
  12. tmp[#tmp+1] = v:lower()
  13. end
  14. return table.concat(tmp, "_")
  15. end
  16. local function underscorecase_to_camelcase(name)
  17. local tmp = {}
  18. for v in name:gmatch "[^_]+" do
  19. tmp[#tmp+1] = v:sub(1,1):upper() .. v:sub(2)
  20. end
  21. return table.concat(tmp)
  22. end
  23. local function convert_funcname(name)
  24. name = name:gsub("^%l", string.upper) -- Change to upper CamlCase
  25. return camelcase_to_underscorecase(name)
  26. end
  27. local function convert_arg(all_types, arg, namespace)
  28. local fulltype, array = arg.fulltype:match "(.-)%s*(%[%s*[%d%a_:]*%s*%])"
  29. if array then
  30. arg.fulltype = fulltype
  31. arg.array = array
  32. local enum, value = array:match "%[%s*([%a%d]+)::([%a%d]+)%]"
  33. if enum then
  34. local typedef = all_types[ enum .. "::Enum" ]
  35. if typedef == nil then
  36. error ("Unknown Enum " .. enum)
  37. end
  38. arg.carray = "[BGFX_" .. camelcase_to_underscorecase(enum):upper() .. "_" .. value:upper() .. "]"
  39. end
  40. end
  41. local t, postfix = arg.fulltype:match "(%a[%a%d_:]*)%s*([*&]+)%s*$"
  42. if t then
  43. arg.type = t
  44. if postfix == "&" then
  45. arg.ref = true
  46. end
  47. else
  48. local prefix, t = arg.fulltype:match "^%s*(%a+)%s+(%S+)"
  49. if prefix then
  50. arg.type = t
  51. else
  52. arg.type = arg.fulltype
  53. end
  54. end
  55. local ctype
  56. local substruct = namespace.substruct
  57. if substruct then
  58. ctype = substruct[arg.type]
  59. end
  60. if not ctype then
  61. ctype = all_types[arg.type]
  62. end
  63. if not ctype then
  64. error ("Undefined type " .. arg.fulltype .. " in " .. namespace.name)
  65. end
  66. arg.ctype = arg.fulltype:gsub(arg.type, ctype.cname):gsub("&", "*")
  67. if ctype.cname ~= arg.type then
  68. arg.cpptype = arg.fulltype:gsub(arg.type, "bgfx::"..arg.type)
  69. else
  70. arg.cpptype = arg.fulltype
  71. end
  72. if arg.ref then
  73. arg.ptype = arg.cpptype:gsub("&", "*")
  74. end
  75. end
  76. local function alternative_name(name)
  77. if name:sub(1,1) == "_" then
  78. return name:sub(2)
  79. else
  80. return name .. "_"
  81. end
  82. end
  83. local function gen_arg_conversion(all_types, arg)
  84. if arg.ctype == arg.fulltype then
  85. -- do not need conversion
  86. return
  87. end
  88. local ctype = all_types[arg.type]
  89. if ctype.handle and arg.type == arg.fulltype then
  90. local aname = alternative_name(arg.name)
  91. arg.aname = aname .. ".cpp"
  92. arg.aname_cpp2c = aname .. ".c"
  93. arg.conversion = string.format(
  94. "union { %s c; bgfx::%s cpp; } %s = { %s };" ,
  95. ctype.cname, arg.type, aname, arg.name)
  96. arg.conversion_back = string.format(
  97. "union { bgfx::%s cpp; %s c; } %s = { %s };" ,
  98. arg.type, ctype.cname, aname, arg.name)
  99. elseif arg.ref then
  100. if ctype.cname == arg.type then
  101. arg.aname = "*" .. arg.name
  102. arg.aname_cpp2c = "&" .. arg.name
  103. elseif arg.out and ctype.enum then
  104. local aname = alternative_name(arg.name)
  105. local cpptype = arg.cpptype:match "(.-)%s*&" -- remove &
  106. local c99type = arg.ctype:match "(.-)%s*%*" -- remove *
  107. arg.aname = aname
  108. arg.aname_cpp2c = "&" .. aname
  109. arg.conversion = string.format("%s %s;", cpptype, aname)
  110. arg.conversion_back = string.format("%s %s;", c99type, aname);
  111. arg.out_conversion = string.format("*%s = (%s)%s;", arg.name, ctype.cname, aname)
  112. arg.out_conversion_back = string.format("%s = (%s)%s;", arg.name, c99type, aname)
  113. else
  114. arg.aname = alternative_name(arg.name)
  115. arg.aname_cpp2c = string.format("(%s)&%s" , arg.ctype , arg.name)
  116. arg.conversion = string.format(
  117. "%s %s = *(%s)%s;",
  118. arg.cpptype, arg.aname, arg.ptype, arg.name)
  119. end
  120. else
  121. local cpptype = arg.cpptype
  122. local ctype = arg.ctype
  123. if arg.array then
  124. cpptype = cpptype .. "*"
  125. ctype = ctype .. "*"
  126. end
  127. arg.aname = string.format(
  128. "(%s)%s",
  129. cpptype, arg.name)
  130. arg.aname_cpp2c = string.format(
  131. "(%s)%s",
  132. ctype, arg.name)
  133. end
  134. end
  135. local function gen_ret_conversion(all_types, func)
  136. local postfix = { func.vararg and "va_end(argList);" }
  137. local postfix_cpp2c = { postfix[1] }
  138. func.ret_postfix = postfix
  139. func.ret_postfix_cpp2c = postfix_cpp2c
  140. for _, arg in ipairs(func.args) do
  141. if arg.out_conversion then
  142. postfix[#postfix+1] = arg.out_conversion
  143. postfix_cpp2c[#postfix_cpp2c+1] = arg.out_conversion_back
  144. end
  145. end
  146. local ctype = all_types[func.ret.type]
  147. if ctype.handle then
  148. func.ret_conversion = string.format(
  149. "union { %s c; bgfx::%s cpp; } handle_ret;" ,
  150. ctype.cname, ctype.name)
  151. func.ret_conversion_cpp2c = string.format(
  152. "union { bgfx::%s cpp; %s c; } handle_ret;" ,
  153. ctype.name, ctype.cname)
  154. func.ret_prefix = "handle_ret.cpp = "
  155. func.ret_prefix_cpp2c = "handle_ret.c = "
  156. postfix[#postfix+1] = "return handle_ret.c;"
  157. postfix_cpp2c[#postfix_cpp2c+1] = "return handle_ret.cpp;"
  158. elseif func.ret.fulltype ~= "void" then
  159. local ctype_conversion = ""
  160. local conversion_back = ""
  161. if ctype.name ~= ctype.cname then
  162. if func.ret.ref then
  163. ctype_conversion = "(" .. func.ret.ctype .. ")&"
  164. conversion_back = "*(" .. func.ret.ptype .. ")"
  165. else
  166. ctype_conversion = "(" .. func.ret.ctype .. ")"
  167. conversion_back = "(" .. func.ret.cpptype .. ")"
  168. end
  169. end
  170. if #postfix > 0 then
  171. func.ret_prefix = string.format("%s retValue = %s", func.ret.ctype , ctype_conversion)
  172. func.ret_prefix_cpp2c = string.format("%s retValue = %s", func.ret.cpptype , conversion_back)
  173. local ret = "return retValue;"
  174. postfix[#postfix+1] = ret
  175. postfix_cpp2c[#postfix_cpp2c+1] = ret
  176. else
  177. func.ret_prefix = string.format("return %s", ctype_conversion)
  178. func.ret_prefix_cpp2c = string.format("return %s", conversion_back)
  179. end
  180. end
  181. end
  182. local function convert_vararg(v)
  183. if v.vararg then
  184. local args = v.args
  185. local vararg = {
  186. name = "",
  187. fulltype = "...",
  188. type = "...",
  189. ctype = "...",
  190. aname = "argList",
  191. conversion = string.format(
  192. "va_list argList;\n\tva_start(argList, %s);",
  193. args[#args].name),
  194. }
  195. args[#args + 1] = vararg
  196. v.alter_name = v.vararg
  197. end
  198. end
  199. function codegen.nameconversion(all_types, all_funcs)
  200. for _,v in ipairs(all_types) do
  201. local name = v.name
  202. local cname = v.cname
  203. if cname == nil then
  204. if name:match "^%u" then
  205. cname = camelcase_to_underscorecase(name)
  206. else
  207. v.cname = name
  208. end
  209. end
  210. if v.flag then
  211. v.cname = "BGFX_" .. cname:upper()
  212. elseif cname then
  213. if v.namespace then
  214. cname = camelcase_to_underscorecase(v.namespace) .. "_" .. cname
  215. end
  216. v.cname = "bgfx_".. cname .. "_t"
  217. end
  218. if v.enum then
  219. v.typename = v.name
  220. v.name = v.name .. "::Enum"
  221. end
  222. end
  223. -- make index
  224. for _,v in ipairs(all_types) do
  225. if not v.namespace then
  226. if all_types[v.name] then
  227. error ("Duplicate type " .. v.name)
  228. elseif not v.flag then
  229. all_types[v.name] = v
  230. end
  231. end
  232. end
  233. -- make sub struct index
  234. for _,v in ipairs(all_types) do
  235. if v.namespace then
  236. local super = all_types[v.namespace]
  237. if not super then
  238. error ("Define " .. v.namespace .. " first")
  239. end
  240. local substruct = super.substruct
  241. if not substruct then
  242. substruct = {}
  243. super.substruct = substruct
  244. end
  245. if substruct[v.name] then
  246. error ( "Duplicate sub struct " .. v.name .. " in " .. v.namespace)
  247. end
  248. substruct[#substruct+1] = v
  249. substruct[v.name] = v
  250. end
  251. end
  252. for _,v in ipairs(all_types) do
  253. if v.struct then
  254. for _, item in ipairs(v.struct) do
  255. convert_arg(all_types, item, v)
  256. end
  257. elseif v.args then
  258. -- funcptr
  259. for _, arg in ipairs(v.args) do
  260. convert_arg(all_types, arg, v)
  261. end
  262. convert_vararg(v)
  263. convert_arg(all_types, v.ret, v)
  264. end
  265. end
  266. local funcs = {}
  267. local funcs_conly = {}
  268. local funcs_alter = {}
  269. for _,v in ipairs(all_funcs) do
  270. if v.cname == nil then
  271. v.cname = convert_funcname(v.name)
  272. end
  273. if v.class then
  274. v.cname = convert_funcname(v.class) .. "_" .. v.cname
  275. local classtype = all_types[v.class]
  276. if classtype then
  277. local methods = classtype.methods
  278. if not methods then
  279. methods = {}
  280. classtype.methods = methods
  281. end
  282. methods[#methods+1] = v
  283. end
  284. elseif not v.conly then
  285. funcs[v.name] = v
  286. end
  287. if v.conly then
  288. table.insert(funcs_conly, v)
  289. end
  290. for _, arg in ipairs(v.args) do
  291. convert_arg(all_types, arg, v)
  292. gen_arg_conversion(all_types, arg)
  293. end
  294. convert_vararg(v)
  295. if v.alter_name then
  296. funcs_alter[#funcs_alter+1] = v
  297. end
  298. convert_arg(all_types, v.ret, v)
  299. gen_ret_conversion(all_types, v)
  300. local namespace = v.class
  301. if namespace then
  302. local classname = namespace
  303. if v.const then
  304. classname = "const " .. classname
  305. end
  306. local classtype = { fulltype = classname .. "*" }
  307. convert_arg(all_types, classtype, v)
  308. v.this = classtype.ctype .. " _this"
  309. v.this_conversion = string.format( "%s This = (%s)_this;", classtype.cpptype, classtype.cpptype)
  310. v.this_to_c = string.format("(%s)this", classtype.ctype)
  311. end
  312. end
  313. for _, v in ipairs(funcs_conly) do
  314. local func = funcs[v.name]
  315. if func then
  316. func.multicfunc = func.multicfunc or { func.cname }
  317. table.insert(func.multicfunc, v.cname)
  318. end
  319. end
  320. for _, v in ipairs(funcs_alter) do
  321. local func = funcs[v.alter_name]
  322. v.alter_cname = func.cname
  323. end
  324. end
  325. local function lines(tbl)
  326. if not tbl or #tbl == 0 then
  327. return "//EMPTYLINE"
  328. else
  329. return table.concat(tbl, "\n\t")
  330. end
  331. end
  332. local function remove_emptylines(txt)
  333. return (txt:gsub("\t//EMPTYLINE\n", ""))
  334. end
  335. local function codetemp(func)
  336. local conversion = {}
  337. local conversion_c2cpp = {}
  338. local args = {}
  339. local cargs = {}
  340. local callargs_conversion = {}
  341. local callargs_conversion_back = {}
  342. local callargs = {}
  343. local cppfunc
  344. local classname
  345. if func.class then
  346. -- It's a member function
  347. cargs[1] = func.this
  348. conversion[1] = func.this_conversion
  349. cppfunc = "This->" .. func.name
  350. callargs[1] = "_this"
  351. callargs_conversion_back[1] = func.this_to_c
  352. classname = func.class .. "::"
  353. else
  354. cppfunc = "bgfx::" .. tostring(func.alter_name or func.name)
  355. classname = ""
  356. end
  357. for _, arg in ipairs(func.args) do
  358. conversion[#conversion+1] = arg.conversion
  359. conversion_c2cpp[#conversion_c2cpp+1] = arg.conversion_back
  360. local cname = arg.ctype .. " " .. arg.name
  361. if arg.array then
  362. cname = cname .. (arg.carray or arg.array)
  363. end
  364. local name = arg.fulltype .. " " .. arg.name
  365. if arg.array then
  366. name = name .. arg.array
  367. end
  368. if arg.default ~= nil then
  369. name = name .. " = " .. tostring(arg.default)
  370. end
  371. cargs[#cargs+1] = cname
  372. args[#args+1] = name
  373. callargs_conversion[#callargs_conversion+1] = arg.aname or arg.name
  374. callargs_conversion_back[#callargs_conversion_back+1] = arg.aname_cpp2c or arg.name
  375. callargs[#callargs+1] = arg.name
  376. end
  377. conversion[#conversion+1] = func.ret_conversion
  378. conversion_c2cpp[#conversion_c2cpp+1] = func.ret_conversion_cpp2c
  379. local ARGS
  380. local args_n = #args
  381. if args_n == 0 then
  382. ARGS = ""
  383. elseif args_n == 1 then
  384. ARGS = args[1]
  385. else
  386. ARGS = "\n\t " .. table.concat(args, "\n\t, ") .. "\n\t"
  387. end
  388. local preret_c2c
  389. local postret_c2c = {}
  390. local conversion_c2c = {}
  391. local callfunc_c2c
  392. if func.vararg then
  393. postret_c2c[1] = "va_end(argList);"
  394. local vararg = func.args[#func.args]
  395. callargs[#callargs] = vararg.aname
  396. callargs_conversion_back[#callargs_conversion_back] = vararg.aname
  397. conversion_c2c[1] = vararg.conversion
  398. conversion_c2cpp[1] = vararg.conversion
  399. if func.ret.fulltype == "void" then
  400. preret_c2c = ""
  401. else
  402. preret_c2c = func.ret.ctype .. " retValue = "
  403. postret_c2c[#postret_c2c+1] = "return retValue;"
  404. end
  405. callfunc_c2c = func.alter_cname or func.cname
  406. else
  407. if func.ret.fulltype == "void" then
  408. preret_c2c = ""
  409. else
  410. preret_c2c = "return "
  411. end
  412. callfunc_c2c = func.cname
  413. end
  414. outCargs = table.concat(cargs, ", ")
  415. if outCargs == "" then
  416. outCargs = "void"
  417. end
  418. return {
  419. RET = func.ret.fulltype,
  420. CRET = func.ret.ctype,
  421. CFUNCNAME = func.cname,
  422. CFUNCNAMEUPPER = func.cname:upper(),
  423. CFUNCNAMECAML = underscorecase_to_camelcase(func.cname),
  424. FUNCNAME = func.name,
  425. CARGS = outCargs,
  426. CPPARGS = table.concat(args, ", "),
  427. ARGS = ARGS,
  428. CONVERSION = lines(conversion),
  429. CONVERSIONCTOC = lines(conversion_c2c),
  430. CONVERSIONCTOCPP = lines(conversion_c2cpp),
  431. PRERET = func.ret_prefix or "",
  432. PRERETCPPTOC = func.ret_prefix_cpp2c or "",
  433. CPPFUNC = cppfunc,
  434. CALLFUNCCTOC = callfunc_c2c,
  435. CALLARGSCTOCPP = table.concat(callargs_conversion, ", "),
  436. CALLARGSCPPTOC = table.concat(callargs_conversion_back, ", "),
  437. CALLARGS = table.concat(callargs, ", "),
  438. POSTRET = lines(func.ret_postfix),
  439. POSTRETCPPTOC = lines(func.ret_postfix_cpp2c),
  440. PRERETCTOC = preret_c2c,
  441. POSTRETCTOC = lines(postret_c2c),
  442. CLASSNAME = classname,
  443. CONST = func.const and " const" or "",
  444. }
  445. end
  446. local function apply_template(func, temp)
  447. func.codetemp = func.codetemp or codetemp(func)
  448. return (temp:gsub("$(%u+)", func.codetemp))
  449. end
  450. function codegen.apply_functemp(func, temp)
  451. return remove_emptylines(apply_template(func, temp))
  452. end
  453. function codegen.gen_funcptr(funcptr)
  454. return apply_template(funcptr, "typedef $RET (*$FUNCNAME)($ARGS);")
  455. end
  456. function codegen.gen_cfuncptr(funcptr)
  457. return apply_template(funcptr, "typedef $CRET (*$CFUNCNAME)($CARGS);")
  458. end
  459. local function doxygen_funcret(r, func, prefix)
  460. if not func or func.ret.fulltype == "void" or func.ret.comment == nil then
  461. return
  462. end
  463. r[#r+1] = prefix
  464. if type(func.ret.comment) == "string" then
  465. r[#r+1] = string.format("%s @returns %s", prefix, func.ret.comment)
  466. else
  467. r[#r+1] = string.format("%s @returns %s", prefix, func.ret.comment[1])
  468. for i = 2,#func.ret.comment do
  469. r[#r+1] = string.format("%s %s", prefix, func.ret.comment[i])
  470. end
  471. end
  472. return r
  473. end
  474. local function doxygen_func(r, func, prefix)
  475. if not func or not func.args or #func.args == 0 then
  476. return
  477. end
  478. r[#r+1] = prefix
  479. for _, arg in ipairs(func.args) do
  480. local inout
  481. if arg.out then
  482. inout = "out"
  483. elseif arg.inout then
  484. inout = "inout"
  485. else
  486. inout = "in"
  487. end
  488. local comment = string.format("%s @param[%s] %s", prefix, inout, arg.name)
  489. if arg.comment then
  490. if type(arg.comment) == "string" then
  491. r[#r+1] = comment .. " " .. arg.comment
  492. else
  493. r[#r+1] = comment .. " " .. arg.comment[1]
  494. for i = 2,#arg.comment do
  495. r[#r+1] = string.format("%s %s", prefix, arg.comment[i])
  496. end
  497. end
  498. else
  499. r[#r+1] = comment
  500. end
  501. end
  502. doxygen_funcret(r, func, prefix)
  503. return r
  504. end
  505. function codegen.doxygen_type(doxygen, func, cname)
  506. if doxygen == nil then
  507. return
  508. end
  509. local result = {}
  510. for _, line in ipairs(doxygen) do
  511. result[#result+1] = "/// " .. line
  512. end
  513. doxygen_func(result, func, "///")
  514. if cname then
  515. result[#result+1] = "///"
  516. if type(cname) == "string" then
  517. result[#result+1] = string.format("/// @attention C99 equivalent is `%s`.", cname)
  518. else
  519. local names = {}
  520. for _, v in ipairs(cname) do
  521. names[#names+1] = "`" .. v .. "`"
  522. end
  523. result[#result+1] = string.format("/// @attention C99 equivalent are %s.", table.concat(names, ","))
  524. end
  525. end
  526. result[#result+1] = "///"
  527. return table.concat(result, "\n")
  528. end
  529. function codegen.doxygen_ctype(doxygen, func)
  530. if doxygen == nil then
  531. return
  532. end
  533. local result = {
  534. "/**",
  535. }
  536. for _, line in ipairs(doxygen) do
  537. result[#result+1] = " * " .. line
  538. end
  539. doxygen_func(result, func, " *")
  540. result[#result+1] = " *"
  541. result[#result+1] = " */"
  542. return table.concat(result, "\n")
  543. end
  544. local enum_temp = [[
  545. struct $NAME
  546. {
  547. $COMMENT
  548. enum Enum
  549. {
  550. $ITEMS
  551. Count
  552. };
  553. };
  554. ]]
  555. function codegen.gen_enum_define(enum)
  556. assert(type(enum.enum) == "table", "Not an enum")
  557. local items = {}
  558. for _, item in ipairs(enum.enum) do
  559. local text
  560. if not item.comment then
  561. text = item.name .. ","
  562. else
  563. text = string.format("%s,%s //!< %s",
  564. item.name, namealign(item.name), item.comment)
  565. end
  566. items[#items+1] = text
  567. end
  568. local comment = ""
  569. if enum.comment then
  570. comment = "/// " .. enum.comment
  571. end
  572. local temp = {
  573. NAME = enum.typename,
  574. COMMENT = comment,
  575. ITEMS = table.concat(items, "\n\t\t"),
  576. }
  577. return (enum_temp:gsub("$(%u+)", temp))
  578. end
  579. local cenum_temp = [[
  580. typedef enum $NAME
  581. {
  582. $ITEMS
  583. $COUNT
  584. } $NAME_t;
  585. ]]
  586. function codegen.gen_enum_cdefine(enum)
  587. assert(type(enum.enum) == "table", "Not an enum")
  588. local cname = enum.cname:match "(.-)_t$"
  589. local uname = cname:upper()
  590. local items = {}
  591. for index , item in ipairs(enum.enum) do
  592. local comment = item.comment or ""
  593. local ename = item.cname
  594. if not ename then
  595. if enum.underscore then
  596. ename = camelcase_to_underscorecase(item.name)
  597. else
  598. ename = item.name
  599. end
  600. ename = ename:upper()
  601. end
  602. local name = uname .. "_" .. ename
  603. items[#items+1] = string.format("%s,%s /** (%2d) %s%s */",
  604. name,
  605. namealign(name, 40),
  606. index - 1,
  607. comment,
  608. namealign(comment, 30))
  609. end
  610. local temp = {
  611. NAME = cname,
  612. COUNT = uname .. "_COUNT",
  613. ITEMS = table.concat(items, "\n\t"),
  614. }
  615. return (cenum_temp:gsub("$(%u+)", temp))
  616. end
  617. local function flag_format(flag)
  618. if not flag.format then
  619. flag.format = "%0" .. (flag.bits // 4) .. "x"
  620. end
  621. end
  622. function codegen.gen_flag_cdefine(flag)
  623. assert(type(flag.flag) == "table", "Not a flag")
  624. flag_format(flag)
  625. local s = {}
  626. local shift = flag.shift
  627. local base = flag.base or 0
  628. local cap = 1 << (flag.range or 0)
  629. for index, item in ipairs(flag.flag) do
  630. local name
  631. if item.cname then
  632. name = flag.cname .. "_" .. item.cname
  633. else
  634. name = flag.cname .. "_" .. camelcase_to_underscorecase(item.name):upper()
  635. end
  636. local value = item.value
  637. if flag.const then
  638. -- use value directly
  639. elseif shift then
  640. if value then
  641. if value > 0 then
  642. value = value - 1
  643. end
  644. else
  645. value = index + base - 1
  646. end
  647. if value >= cap then
  648. error (string.format("Out of range for %s (%d/%d)", name, value, cap))
  649. end
  650. value = value << shift
  651. elseif #item == 0 then
  652. if value then
  653. if value > 0 then
  654. value = 1 << (value - 1)
  655. end
  656. else
  657. local s = index + base - 2
  658. if s >= 0 then
  659. value = 1 << s
  660. else
  661. value = 0
  662. end
  663. end
  664. end
  665. if value == nil then
  666. if item.comment then
  667. if type(item.comment) == "table" then
  668. for _, c in ipairs(item.comment) do
  669. s[#s+1] = "/// " .. c
  670. end
  671. else
  672. s[#s+1] = "/// " .. item.comment
  673. end
  674. end
  675. local sets = { "" }
  676. for _, v in ipairs(item) do
  677. sets[#sets+1] = flag.cname .. "_" .. camelcase_to_underscorecase(v):upper()
  678. end
  679. s[#s+1] = string.format("#define %s (0%s \\\n\t)\n", name, table.concat(sets, " \\\n\t| "))
  680. else
  681. local comment = ""
  682. if item.comment then
  683. comment = " //!< " .. item.comment
  684. end
  685. value = string.format(flag.format, value)
  686. local code = string.format("#define %s %sUINT%d_C(0x%s)%s",
  687. name, namealign(name, 35), flag.bits, value, comment)
  688. s[#s+1] = code
  689. end
  690. end
  691. local mask
  692. if flag.range then
  693. if flag.range == 64 then
  694. mask = "ffffffffffffffff"
  695. else
  696. mask = string.format(flag.format, ((1 << flag.range) - 1) << shift)
  697. end
  698. mask = string.format("UINT%d_C(0x%s)", flag.bits, mask)
  699. end
  700. if shift then
  701. local name = flag.cname .. "_SHIFT"
  702. local comment = flag.desc or ""
  703. local shift_align = tostring(shift)
  704. shift_align = shift_align .. namealign(shift_align, #mask)
  705. local comment = ""
  706. if flag.desc then
  707. comment = string.format(" //!< %s bit shift", flag.desc)
  708. end
  709. local code = string.format("#define %s %s%s%s", name, namealign(name, 35), shift_align, comment)
  710. s[#s+1] = code
  711. end
  712. if flag.range then
  713. local name = flag.cname .. "_MASK"
  714. local comment = ""
  715. if flag.desc then
  716. comment = string.format(" //!< %s bit mask", flag.desc)
  717. end
  718. local code = string.format("#define %s %s%s%s", name, namealign(name, 35), mask, comment)
  719. s[#s+1] = code
  720. end
  721. if flag.helper then
  722. s[#s+1] = string.format(
  723. "#define %s(v) ( ( (uint%d_t)(v)<<%s )&%s)",
  724. flag.cname,
  725. flag.bits,
  726. (flag.cname .. "_SHIFT"),
  727. (flag.cname .. "_MASK"))
  728. end
  729. s[#s+1] = ""
  730. return table.concat(s, "\n")
  731. end
  732. local function text_with_comments(items, item, cstyle, is_classmember)
  733. local name = item.name
  734. if item.array then
  735. if cstyle then
  736. name = name .. (item.carray or item.array)
  737. else
  738. name = name .. item.array
  739. end
  740. end
  741. local typename
  742. if cstyle then
  743. typename = item.ctype
  744. else
  745. typename = item.fulltype
  746. end
  747. if is_classmember then
  748. name = "m_" .. name
  749. end
  750. local text = string.format("%s%s %s;", typename, namealign(typename), name)
  751. if item.comment then
  752. if type(item.comment) == "table" then
  753. table.insert(items, "")
  754. if cstyle then
  755. table.insert(items, "/**")
  756. for _, c in ipairs(item.comment) do
  757. table.insert(items, " * " .. c)
  758. end
  759. table.insert(items, " */")
  760. else
  761. for _, c in ipairs(item.comment) do
  762. table.insert(items, "/// " .. c)
  763. end
  764. end
  765. else
  766. text = string.format(
  767. cstyle and "%s %s/** %s%s */" or "%s %s//!< %s",
  768. text, namealign(text, 40), item.comment, namealign(item.comment, 40))
  769. end
  770. end
  771. items[#items+1] = text
  772. end
  773. local struct_temp = [[
  774. struct $NAME
  775. {
  776. $METHODS
  777. $SUBSTRUCTS
  778. $ITEMS
  779. };
  780. ]]
  781. function codegen.gen_struct_define(struct, methods)
  782. assert(type(struct.struct) == "table", "Not a struct")
  783. local items = {}
  784. for _, item in ipairs(struct.struct) do
  785. text_with_comments(items, item, false, methods ~= nil and not struct.shortname)
  786. end
  787. local ctor = {}
  788. if struct.ctor then
  789. ctor[1] = struct.name .. "();"
  790. ctor[2] = ""
  791. end
  792. if methods then
  793. for _, m in ipairs(methods) do
  794. if m:sub(-1) ~= "\n" then
  795. m = m .. "\n"
  796. end
  797. for line in m:gmatch "(.-)\n" do
  798. ctor[#ctor+1] = line
  799. end
  800. ctor[#ctor+1] = ""
  801. end
  802. end
  803. local subs = {}
  804. if struct.substruct then
  805. for _, v in ipairs(struct.substruct) do
  806. local s = codegen.gen_struct_define(v)
  807. s = s:gsub("\n", "\n\t")
  808. subs[#subs+1] = s
  809. end
  810. end
  811. local temp = {
  812. NAME = struct.name,
  813. SUBSTRUCTS = lines(subs),
  814. ITEMS = table.concat(items, "\n\t"),
  815. METHODS = lines(ctor),
  816. }
  817. return remove_emptylines(struct_temp:gsub("$(%u+)", temp))
  818. end
  819. local cstruct_temp = [[
  820. typedef struct $NAME_s
  821. {
  822. $ITEMS
  823. } $NAME_t;
  824. ]]
  825. local cstruct_empty_temp = [[
  826. struct $NAME_s;
  827. typedef struct $NAME_s $NAME_t;
  828. ]]
  829. function codegen.gen_struct_cdefine(struct)
  830. assert(type(struct.struct) == "table", "Not a struct")
  831. local cname = struct.cname:match "(.-)_t$"
  832. local items = {}
  833. for _, item in ipairs(struct.struct) do
  834. text_with_comments(items, item, true)
  835. end
  836. local temp = {
  837. NAME = cname,
  838. ITEMS = table.concat(items, "\n\t"),
  839. }
  840. local codetemp = #struct.struct == 0 and cstruct_empty_temp or cstruct_temp
  841. return (codetemp:gsub("$(%u+)", temp))
  842. end
  843. local chandle_temp = [[
  844. typedef struct $NAME_s { uint16_t idx; } $NAME_t;
  845. ]]
  846. function codegen.gen_chandle(handle)
  847. assert(handle.handle, "Not a handle")
  848. return (chandle_temp:gsub("$(%u+)", { NAME = handle.cname:match "(.-)_t$" }))
  849. end
  850. local handle_temp = [[
  851. struct $NAME { uint16_t idx; };
  852. inline bool isValid($NAME _handle) { return bgfx::kInvalidHandle != _handle.idx; }
  853. ]]
  854. function codegen.gen_handle(handle)
  855. assert(handle.handle, "Not a handle")
  856. return (handle_temp:gsub("$(%u+)", { NAME = handle.name }))
  857. end
  858. return codegen