ToDoxHook.lua 19 KB

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