codegen.lua 24 KB

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