codegen.lua 19 KB

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