ToDoxHook.lua 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. -- Copyright (c) 2008-2022 the Urho3D project
  2. -- License: MIT
  3. classes = {}
  4. enumerates = {}
  5. globalConstants = {}
  6. globalFunctions = {}
  7. globalProperties = {}
  8. currentClass = nil
  9. currentFunction = nil
  10. curDir = nil
  11. function classClass:print(ident,close)
  12. local class = {}
  13. class.name = self.name
  14. class.base = self.base
  15. class.lname = self.lname
  16. class.type = self.type
  17. class.btype = self.btype
  18. class.ctype = self.ctype
  19. currentClass = class
  20. local i = 1
  21. while self[i] do
  22. self[i]:print(ident.." ",",")
  23. i = i + 1
  24. end
  25. currentClass = nil
  26. table.insert(classes, class)
  27. end
  28. function classCode:print(ident,close)
  29. end
  30. function classDeclaration:print(ident,close)
  31. local declaration = {}
  32. declaration.mod = self.mod
  33. declaration.type = self.type
  34. declaration.ptr = self.ptr
  35. declaration.name = self.name
  36. declaration.dim = self.dim
  37. declaration.def = self.def
  38. declaration.ret = self.ret
  39. if currentFunction ~= nil then
  40. if currentFunction.declarations == nil then
  41. currentFunction.declarations = { declaration }
  42. else
  43. table.insert(currentFunction.declarations, declaration)
  44. end
  45. end
  46. end
  47. function classEnumerate:print(ident,close)
  48. local enumerate = {}
  49. enumerate.name = self.name
  50. local i = 1
  51. while self[i] do
  52. if self[i] ~= "" then
  53. if enumerate.values == nil then
  54. enumerate.values = { self[i] }
  55. else
  56. table.insert(enumerate.values, self[i])
  57. end
  58. end
  59. i = i + 1
  60. end
  61. if enumerate.values ~= nil then
  62. table.insert(enumerates, enumerate)
  63. end
  64. end
  65. function deepCopy(t)
  66. if type(t) ~= "table" then
  67. return t
  68. end
  69. local mt = getmetatable(t)
  70. local ret = {}
  71. for k, v in pairs(t) do
  72. if type(v) == "table" then
  73. v = deepCopy(v)
  74. end
  75. ret[k] = v
  76. end
  77. setmetatable(ret, mt)
  78. return ret
  79. end
  80. function printFunction(self,ident,close,isfunc)
  81. local func = {}
  82. func.mod = self.mod
  83. func.type = self.type
  84. func.ptr = self.ptr
  85. func.name = self.name
  86. func.lname = self.lname
  87. func.const = self.const
  88. func.cname = self.cname
  89. func.lname = self.lname
  90. if isfunc then
  91. func.name = func.lname
  92. end
  93. currentFunction = func
  94. local i = 1
  95. while self.args[i] do
  96. self.args[i]:print(ident.." ",",")
  97. i = i + 1
  98. end
  99. currentFunction = nil
  100. if currentClass == nil then
  101. table.insert(globalFunctions, func)
  102. else
  103. if func.name == "new" then
  104. -- add construct function
  105. local ctor = deepCopy(func)
  106. ctor.type = ""
  107. ctor.ptr = ""
  108. ctor.name = currentClass.name
  109. ctor.lname = currentClass.name
  110. ctor.const = "(GC)"
  111. ctor.cname = currentClass.name
  112. ctor.lname = currentClass.name
  113. if currentClass.functions == nil then
  114. currentClass.functions = { ctor }
  115. else
  116. table.insert(currentClass.functions, ctor)
  117. end
  118. end
  119. if func.name == "delete" then
  120. func.type = "void"
  121. end
  122. if currentClass.functions == nil then
  123. currentClass.functions = { func }
  124. else
  125. table.insert(currentClass.functions, func)
  126. end
  127. end
  128. end
  129. function classFunction:print(ident,close)
  130. printFunction(self,ident,close, true)
  131. end
  132. function classOperator:print(ident,close)
  133. printFunction(self,ident,close, false)
  134. end
  135. function classVariable:print(ident,close)
  136. local property = {}
  137. property.mod = self.mod
  138. property.type = self.type
  139. property.ptr = self.ptr
  140. property.name = self.lname
  141. property.def = self.def
  142. property.ret = self.ret
  143. if currentClass == nil then
  144. if property.mod:find("tolua_property__") == nil then
  145. table.insert(globalConstants, property)
  146. else
  147. table.insert(globalProperties, property)
  148. end
  149. else
  150. if currentClass.properties == nil then
  151. currentClass.properties = { property }
  152. else
  153. table.insert(currentClass.properties, property)
  154. end
  155. end
  156. end
  157. function classVerbatim:print(ident,close)
  158. end
  159. function sortByName(t)
  160. table.sort(t, function(a, b) return a.name < b.name end)
  161. end
  162. function getCurrentDirectory()
  163. local separator = (package.config):gsub("\n.*","")
  164. local path = ""
  165. local tmpFile = os.tmpname()
  166. if separator == "\\" then
  167. if not tmpFile:find(':') then
  168. -- Workaround broken os.tmpname()
  169. tmpFile = os.getenv('TMP') .. tmpFile
  170. end
  171. os.execute("cd > " .. tmpFile)
  172. else
  173. os.execute("pwd > " .. tmpFile)
  174. end
  175. local tmpHandler = io.open(tmpFile, "r")
  176. path = tmpHandler:read("*a"):gsub("\n.*","")
  177. tmpHandler:close()
  178. os.remove(tmpFile)
  179. return {path = path, separator = separator}
  180. end
  181. function isTypeEquivalent(headerType, packageType)
  182. -- {["headerType"] = {"packageType1", "packageType2", ...}}
  183. --local equivalenceTable = {["StringHash"] = {"const String"}}
  184. if headerType == packageType then
  185. return true
  186. else
  187. if equivalenceTable ~= nil then
  188. for headerEqType, packageEqTypes in pairs(equivalenceTable) do
  189. if headerEqType == headerType then
  190. if packageEqTypes ~= nil then
  191. for i, packageEqType in ipairs(packageEqTypes) do
  192. if packageEqType == packageType then
  193. return true
  194. end
  195. end
  196. end
  197. return false
  198. end
  199. end
  200. end
  201. end
  202. return false
  203. end
  204. function isSameFilename(str1, str2, excludeExtensions)
  205. str1 = str1:gsub("^.*" .. curDir.separator.. "(.*)$", "%1", 1)
  206. str2 = str2:gsub("^.*" .. curDir.separator.. "(.*)$", "%1", 1)
  207. if excludeExtensions == true then
  208. str1 = str1:gsub("^(.*)%..*$", "%1", 1)
  209. str2 = str2:gsub("^(.*)%..*$", "%1", 1)
  210. end
  211. if str1 == str2 then
  212. return true
  213. else
  214. return false
  215. end
  216. end
  217. function isSameFunction(headerFunc, packageFunc, strict)
  218. if headerFunc.name == packageFunc.name then
  219. if strict == true then
  220. if headerFunc.declarations ~= nil and packageFunc.declarations ~= nil then
  221. --for _, decl in ipairs(headerFunc.declarations) do print("FuncHeader Param: \""..decl.type.."\", \""..decl.ptr.."\", \""..decl.name.."\", \""..decl.def.."\"") end
  222. --for _, decl in ipairs(packageFunc.declarations) do print("FuncPackage Param: \""..decl.type.."\", \""..decl.ptr.."\", \""..decl.name.."\", \""..decl.def.."\"") end
  223. for i, headerDecl in ipairs(headerFunc.declarations) do
  224. if packageFunc.declarations[i] ~= nil then
  225. if not isTypeEquivalent(headerDecl.type, packageFunc.declarations[i].type) then
  226. return false
  227. end
  228. else
  229. if headerDecl.def == "" then
  230. return false
  231. end
  232. end
  233. end
  234. return true
  235. else
  236. return true
  237. end
  238. else
  239. return true
  240. end
  241. end
  242. return false
  243. end
  244. function printDescriptionsFromPackageFile(filename, directory)
  245. for line in io.lines(filename) do
  246. line = line:gsub("%c", "")
  247. if line:find("^%s*%$pfile%s+\"(.+)\"") ~= nil then
  248. -- If it's another package file, load it (recursive)
  249. local nextPath = curDir.path .. curDir.separator
  250. nextPath = nextPath .. line:gsub("^%s*%$pfile%s+\"(.+)\"", "%1", 1):gsub("/", curDir.separator)
  251. local nextDirectory = line:gsub("^%s*%$pfile%s+\"(.+)\"", "%1", 1):gsub("/.*$", ""):gsub("/", curDir.separator)
  252. printDescriptionsFromPackageFile(nextPath, nextDirectory)
  253. elseif line:find("^%s*%$#include%s+\"(.+)\"") ~= nil then
  254. -- If it's an include, load it to fetch the descriptions
  255. local nextFilename = line:gsub("^%s*%$#include%s+\"(.+)\"", "%1", 1):gsub("/", curDir.separator)
  256. if isSameFilename(filename, nextFilename, true) then
  257. -- Must be same as Package Name
  258. printDescriptions(nextFilename, directory)
  259. end
  260. end
  261. end
  262. end
  263. function printDescriptions(filename, directory)
  264. -- Search Descriptions
  265. local className = nil
  266. local classScope = nil
  267. local description = nil
  268. local sourceEnginePath = curDir.path .. curDir.separator .. ".." .. curDir.separator .. ".." .. curDir.separator
  269. for line in io.lines(sourceEnginePath .. filename) do
  270. line = line:gsub("%c", "")
  271. -- Entering Class
  272. if line:find("^%s*[Cc]lass%s+(.+)") ~= nil then
  273. local classDefine = line:gsub("^%s*[Cc]lass%s+([%w_][^:;]*)%s*:*.*", "%1")
  274. className = classDefine:gsub("[%w_]+%s+([%w_]+).*", "%1")
  275. -- Struct Defined (same as Class)
  276. elseif line:find("^%s*[Ss]truct%s+(.+)") ~= nil then
  277. local classDefine = line:gsub("^%s*[Ss]truct%s+([%w_][^:;]*)%s*:*.*", "%1")
  278. className = classDefine:gsub("[%w_]+%s+([%w_]+).*", "%1")
  279. elseif className ~= nil then
  280. -- Detecting Scope
  281. if line:find("^%s*(%w+)%s*:%s*$") ~= nil then
  282. classScope = line:gsub("^%s*(%w)%s*:%s*$", "%1")
  283. -- Leaving Class
  284. elseif line:find("^%s*}%s*$") ~= nil then
  285. className = nil
  286. classScope = nil
  287. description = nil
  288. -- Gather Informations
  289. elseif className ~= nil and classScope ~= nil then
  290. -- Line stating with "///" (Description)
  291. if line:find("^%s*///") ~= nil then
  292. description = line:gsub("^%s*///%s*", "")
  293. -- Not Empty Line (Function)
  294. elseif line:find("^%s*$") == nil and description ~= nil then
  295. printElementDescription(className, classScope, line, description)
  296. description = nil
  297. end
  298. end
  299. end
  300. end
  301. end
  302. function getElementFromLine(line)
  303. local element = {}
  304. element.name = nil
  305. element.params = nil
  306. element.type = nil
  307. -- Type Detect (Function)
  308. if line:find("^.*%s*([%w_~]+)%s*%(.*%).*$") ~= nil then
  309. element.name = line:gsub("^.*%s+([%w_~]+)%s*%(.*%).*$", "%1")
  310. element.type = "functions"
  311. if line:find("^.+%(.*%)") ~= nil then
  312. element.params = {}
  313. local params_str = line:gsub("^.+%((.*)%).*$", "%1", 1)
  314. --print("Current Params: "..params_str)
  315. if params_str ~= "" then
  316. for param_str in params_str:gmatch("[^,;]+") do
  317. local param = {}
  318. param.type = ""
  319. if param_str:find("^%s*(const%s+)") ~= nil then
  320. param.type = "const "
  321. param_str = param_str:gsub("^%s*((const%s+)", "")
  322. end
  323. param.type = param.type..param_str:gsub("^%s*([%w_]+).*$", "%1")
  324. param.ptr = param_str:gsub("^%s*[%w_]+([%*&]?).*$", "%1")
  325. param.name = param_str:gsub("^%s*[%w_]+[%*&]?%s+([%w_]+).*$", "%1")
  326. param.def = ""
  327. if param_str:find(".+=.+") then
  328. param.def = param_str:gsub("^.*=%s*(.*)$", "%1")
  329. end
  330. table.insert(element.params, param)
  331. end
  332. else
  333. local param = {}
  334. param.type = "void"
  335. param.ptr = ""
  336. param.name = ""
  337. param.def = ""
  338. table.insert(element.params, param)
  339. end
  340. end
  341. -- Type Detect (Property)
  342. elseif line:find("^.*%s+([%w_]+)%s*.*;%s*$") ~= nil then
  343. element.name = line:gsub("^.*%s+([%w_]+)%s*.*;%s*$", "%1")
  344. element.type = "properties"
  345. end
  346. return element
  347. end
  348. function printElementDescription(className, classScope, line, description)
  349. if description ~= "" then
  350. local currentElement = getElementFromLine(line)
  351. -- Search Class & Function/Property, Then Map
  352. if currentElement.name ~= nil and currentElement.type ~= nil then
  353. --print("Name: " .. currentElement.name)
  354. --print("ok (name = \"" .. currentElement.name .. "\", type = \"" .. currentElement.type .. "\", className = \"" .. className .. "\")")
  355. for i, class in ipairs(classes) do
  356. if class.name == className then
  357. --print("Class: "..class.name.." = "..className)
  358. if class[currentElement.type] ~= nil then
  359. for j, storedElement in ipairs(class[currentElement.type]) do
  360. local isSameName = false
  361. if storedElement.name == currentElement.name then
  362. isSameName = true
  363. -- Consider that the name is the same if it has an additionnal "_" at the end and if it's a property
  364. elseif storedElement.name .. "_" == currentElement.name and currentElement.type == "properties" then
  365. isSameName = true
  366. end
  367. if isSameName == true then
  368. --print("Element: " .. storedElement.name .. " = " .. currentElement.name)
  369. -- Confirm that the function is the same and not an overloading one
  370. local isSameElement = true
  371. if currentElement.type == "functions" then
  372. local candidateElement = {declarations = currentElement.params}
  373. candidateElement.name = currentElement.name
  374. isSameElement = isSameFunction(candidateElement, storedElement, true)
  375. --if isSameElement == true then print("Is same element? True") else print("Is same element? False") end
  376. end
  377. if isSameElement == true then
  378. --print("Element: " .. storedElement.name .. " = " .. currentElement.name)
  379. if storedElement.descriptions == nil then
  380. --print("[New description table]")
  381. classes[i][currentElement.type][j].descriptions = {}
  382. end
  383. --print("Description: "..description)
  384. --print("")
  385. table.insert(classes[i][currentElement.type][j].descriptions, description)
  386. return
  387. end
  388. end
  389. end
  390. end
  391. end
  392. end
  393. --print("")
  394. end
  395. end
  396. end
  397. function writeClass(file, class)
  398. file:write("<a name=\"Class_" .. class.name .. "\"></a>\n")
  399. if class.base == "" then
  400. file:write("### " .. class.name .. "\n\n")
  401. else
  402. file:write("### " .. class.name .. " : " .. class.base .. "\n")
  403. end
  404. if class.functions ~= nil then
  405. file:write("\nMethods:\n\n")
  406. for i, func in ipairs(class.functions) do
  407. writeFunction(file, func)
  408. end
  409. end
  410. if class.properties ~= nil then
  411. file:write("\nProperties:\n\n")
  412. for i, property in ipairs(class.properties) do
  413. writeProperty(file, property)
  414. end
  415. end
  416. file:write("\n")
  417. end
  418. function writeTableOfContents(file)
  419. file:write("\n\\section LuaScriptAPI_TableOfContents Table of contents\n\n")
  420. file:write("\\ref LuaScriptAPI_ClassList \"Class list\"<br>\n")
  421. file:write("\\ref LuaScriptAPI_Classes \"Classes\"<br>\n")
  422. file:write("\\ref LuaScriptAPI_Enums \"Enumerations\"<br>\n")
  423. file:write("\\ref LuaScriptAPI_toluaFunctions \"tolua functions\"<br>\n")
  424. file:write("\\ref LuaScriptAPI_GlobalFunctions \"Global functions\"<br>\n")
  425. file:write("\\ref LuaScriptAPI_GlobalProperties \"Global properties\"<br>\n")
  426. file:write("\\ref LuaScriptAPI_GlobalConstants \"Global constants\"<br>\n")
  427. end
  428. function writeClassList(file)
  429. sortByName(classes)
  430. file:write("\n\\section LuaScriptAPI_ClassList Class list\n\n")
  431. for i, class in ipairs(classes) do
  432. file:write("<a href=\"#Class_" .. class.name .. "\"><b>" .. class.name .. "</b></a>\n")
  433. end
  434. end
  435. function writeClasses(file)
  436. file:write("\n\\section LuaScriptAPI_Classes Classes\n\n")
  437. for i, class in ipairs(classes) do
  438. writeClass(file, class)
  439. end
  440. end
  441. function writeEnumerates(file)
  442. sortByName(enumerates)
  443. file:write("\\section LuaScriptAPI_Enums Enumerations\n\n")
  444. for i, enumerate in ipairs(enumerates) do
  445. file:write("### " .. enumerate.name .. "\n\n")
  446. for i, value in ipairs(enumerate.values) do
  447. file:write("- int " .. value .. "\n")
  448. end
  449. file:write("\n")
  450. end
  451. end
  452. function writeFunction(file, func)
  453. if func.name:match("^_") then
  454. -- Skip internal functions
  455. return
  456. end
  457. local line = "- "
  458. -- construct function
  459. if func.type == "" and func.ptr == "" then
  460. line = line .. func.name .. "("
  461. else
  462. line = line .. func.type .. func.ptr .. " " .. func.name .. "("
  463. end
  464. -- write parameters
  465. if func.declarations ~= nil then
  466. local count = table.maxn(func.declarations)
  467. for i = 1, count do
  468. local declaration = func.declarations[i]
  469. if declaration.type ~= "void" then
  470. line = line .. declaration.type .. declaration.ptr .. " " .. declaration.name
  471. -- add parameter default value
  472. if declaration.def ~= "" then
  473. line = line .. " = " .. declaration.def
  474. end
  475. end
  476. if i ~= count then
  477. line = line .. ", "
  478. end
  479. end
  480. end
  481. line = line .. ")"
  482. -- add const
  483. if func.const ~= "" then
  484. line = line .. " " .. func.const
  485. end
  486. file:write(line .. "\n")
  487. end
  488. function writeGlobalConstants(file)
  489. sortByName(globalConstants)
  490. file:write("\n\\section LuaScriptAPI_GlobalConstants Global constants\n")
  491. for i, constant in ipairs(globalConstants) do
  492. local line = "- " .. constant.type:gsub("const ", "") .. constant.ptr .. " " .. constant.name
  493. file:write(line .. "\n")
  494. end
  495. file:write("\n")
  496. end
  497. function writeToluaFunctions(file)
  498. file:write("\n\\section LuaScriptAPI_toluaFunctions tolua functions\n")
  499. file:write("- lua_usertype cast(lua_usertype object, const char* typename)\n")
  500. file:write("- lua_table getpeer(lua_userdata object)\n")
  501. file:write("- void inherit(lua_table table, lua_usertype c_instance)\n")
  502. file:write("- bool releaseownership(lua_usertype object)\n")
  503. file:write("- void setpeer(lua_userdata object, lua_table table)\n")
  504. file:write("- bool takeownership(lua_usertype object)\n")
  505. file:write("- const char* type(lua_object object)\n")
  506. file:write("\n")
  507. end
  508. function writeGlobalFunctions(file)
  509. sortByName(globalFunctions)
  510. file:write("\n\\section LuaScriptAPI_GlobalFunctions Global functions\n")
  511. for i, func in ipairs(globalFunctions) do
  512. writeFunction(file, func)
  513. end
  514. file:write("\n")
  515. end
  516. function writeGlobalProperties(file)
  517. sortByName(globalProperties)
  518. file:write("\n\\section LuaScriptAPI_GlobalProperties Global properties\n")
  519. for i, property in ipairs(globalProperties) do
  520. writeProperty(file, property)
  521. end
  522. end
  523. function writeProperty(file, property)
  524. file:write("- " .. property.type .. property.ptr .. " " .. property.name)
  525. if property.mod:find("tolua_readonly") == nil then
  526. file:write("\n")
  527. else
  528. file:write(" (readonly)\n")
  529. end
  530. end
  531. function classPackage:print()
  532. curDir = getCurrentDirectory()
  533. if flags.o == nil then
  534. print("Invalid output filename");
  535. return
  536. end
  537. local filename = flags.o
  538. local file = io.open(filename, "wt")
  539. file:write("namespace Urho3D\n")
  540. file:write("{\n")
  541. file:write("\n")
  542. file:write("/**\n")
  543. file:write("\\page LuaScriptAPI Lua scripting API\n")
  544. local i = 1
  545. while self[i] do
  546. self[i]:print("","")
  547. i = i + 1
  548. end
  549. printDescriptionsFromPackageFile(flags.f)
  550. writeTableOfContents(file)
  551. writeClassList(file)
  552. writeClasses(file)
  553. writeEnumerates(file)
  554. writeToluaFunctions(file)
  555. writeGlobalFunctions(file)
  556. writeGlobalProperties(file)
  557. writeGlobalConstants(file)
  558. file:write("*/\n")
  559. file:write("\n")
  560. file:write("}\n")
  561. file:close()
  562. end