codegen.lua 24 KB

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