ToDoxHook.lua 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. --
  2. -- Copyright (c) 2008-2017 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. -- Workaround broken os.tmpname() on Windows platform
  187. tmpFile = os.getenv('TMP') .. tmpFile
  188. os.execute("cd > " .. tmpFile)
  189. else
  190. os.execute("pwd > " .. tmpFile)
  191. end
  192. local tmpHandler = io.open(tmpFile, "r")
  193. path = tmpHandler:read("*a"):gsub("\n.*","")
  194. tmpHandler:close()
  195. os.remove(tmpFile)
  196. return {path = path, separator = separator}
  197. end
  198. function isTypeEquivalent(headerType, packageType)
  199. -- {["headerType"] = {"packageType1", "packageType2", ...}}
  200. --local equivalenceTable = {["StringHash"] = {"const String"}}
  201. if headerType == packageType then
  202. return true
  203. else
  204. if equivalenceTable ~= nil then
  205. for headerEqType, packageEqTypes in pairs(equivalenceTable) do
  206. if headerEqType == headerType then
  207. if packageEqTypes ~= nil then
  208. for i, packageEqType in ipairs(packageEqTypes) do
  209. if packageEqType == packageType then
  210. return true
  211. end
  212. end
  213. end
  214. return false
  215. end
  216. end
  217. end
  218. end
  219. return false
  220. end
  221. function isSameFilename(str1, str2, excludeExtensions)
  222. str1 = str1:gsub("^.*" .. curDir.separator.. "(.*)$", "%1", 1)
  223. str2 = str2:gsub("^.*" .. curDir.separator.. "(.*)$", "%1", 1)
  224. if excludeExtensions == true then
  225. str1 = str1:gsub("^(.*)%..*$", "%1", 1)
  226. str2 = str2:gsub("^(.*)%..*$", "%1", 1)
  227. end
  228. if str1 == str2 then
  229. return true
  230. else
  231. return false
  232. end
  233. end
  234. function isSameFunction(headerFunc, packageFunc, strict)
  235. if headerFunc.name == packageFunc.name then
  236. if strict == true then
  237. if headerFunc.declarations ~= nil and packageFunc.declarations ~= nil then
  238. --for _, decl in ipairs(headerFunc.declarations) do print("FuncHeader Param: \""..decl.type.."\", \""..decl.ptr.."\", \""..decl.name.."\", \""..decl.def.."\"") end
  239. --for _, decl in ipairs(packageFunc.declarations) do print("FuncPackage Param: \""..decl.type.."\", \""..decl.ptr.."\", \""..decl.name.."\", \""..decl.def.."\"") end
  240. for i, headerDecl in ipairs(headerFunc.declarations) do
  241. if packageFunc.declarations[i] ~= nil then
  242. if not isTypeEquivalent(headerDecl.type, packageFunc.declarations[i].type) then
  243. return false
  244. end
  245. else
  246. if headerDecl.def == "" then
  247. return false
  248. end
  249. end
  250. end
  251. return true
  252. else
  253. return true
  254. end
  255. else
  256. return true
  257. end
  258. end
  259. return false
  260. end
  261. function printDescriptionsFromPackageFile(filename, directory)
  262. for line in io.lines(filename) do
  263. line = line:gsub("%c", "")
  264. if line:find("^%s*%$pfile%s+\"(.+)\"") ~= nil then
  265. -- If it's another package file, load it (recursive)
  266. local nextPath = curDir.path .. curDir.separator
  267. nextPath = nextPath .. line:gsub("^%s*%$pfile%s+\"(.+)\"", "%1", 1):gsub("/", curDir.separator)
  268. local nextDirectory = line:gsub("^%s*%$pfile%s+\"(.+)\"", "%1", 1):gsub("/.*$", ""):gsub("/", curDir.separator)
  269. printDescriptionsFromPackageFile(nextPath, nextDirectory)
  270. elseif line:find("^%s*%$#include%s+\"(.+)\"") ~= nil then
  271. -- If it's an include, load it to fetch the descriptions
  272. local nextFilename = line:gsub("^%s*%$#include%s+\"(.+)\"", "%1", 1):gsub("/", curDir.separator)
  273. if isSameFilename(filename, nextFilename, true) then
  274. -- Must be same as Package Name
  275. printDescriptions(nextFilename, directory)
  276. end
  277. end
  278. end
  279. end
  280. function printDescriptions(filename, directory)
  281. -- Search Descriptions
  282. local className = nil
  283. local classScope = nil
  284. local description = nil
  285. local sourceEnginePath = curDir.path .. curDir.separator .. ".." .. curDir.separator .. ".." .. curDir.separator
  286. for line in io.lines(sourceEnginePath .. filename) do
  287. line = line:gsub("%c", "")
  288. -- Entering Class
  289. if line:find("^%s*[Cc]lass%s+(.+)") ~= nil then
  290. local classDefine = line:gsub("^%s*[Cc]lass%s+([%w_][^:;]*)%s*:*.*", "%1")
  291. className = classDefine:gsub("[%w_]+%s+([%w_]+).*", "%1")
  292. -- Struct Defined (same as Class)
  293. elseif line:find("^%s*[Ss]truct%s+(.+)") ~= nil then
  294. local classDefine = line:gsub("^%s*[Ss]truct%s+([%w_][^:;]*)%s*:*.*", "%1")
  295. className = classDefine:gsub("[%w_]+%s+([%w_]+).*", "%1")
  296. elseif className ~= nil then
  297. -- Detecting Scope
  298. if line:find("^%s*(%w+)%s*:%s*$") ~= nil then
  299. classScope = line:gsub("^%s*(%w)%s*:%s*$", "%1")
  300. -- Leaving Class
  301. elseif line:find("^%s*}%s*$") ~= nil then
  302. className = nil
  303. classScope = nil
  304. description = nil
  305. -- Gather Informations
  306. elseif className ~= nil and classScope ~= nil then
  307. -- Line stating with "///" (Description)
  308. if line:find("^%s*///") ~= nil then
  309. description = line:gsub("^%s*///%s*", "")
  310. -- Not Empty Line (Function)
  311. elseif line:find("^%s*$") == nil and description ~= nil then
  312. printElementDescription(className, classScope, line, description)
  313. description = nil
  314. end
  315. end
  316. end
  317. end
  318. end
  319. function getElementFromLine(line)
  320. local element = {}
  321. element.name = nil
  322. element.params = nil
  323. element.type = nil
  324. -- Type Detect (Function)
  325. if line:find("^.*%s*([%w_~]+)%s*%(.*%).*$") ~= nil then
  326. element.name = line:gsub("^.*%s+([%w_~]+)%s*%(.*%).*$", "%1")
  327. element.type = "functions"
  328. if line:find("^.+%(.*%)") ~= nil then
  329. element.params = {}
  330. local params_str = line:gsub("^.+%((.*)%).*$", "%1", 1)
  331. --print("Current Params: "..params_str)
  332. if params_str ~= "" then
  333. for param_str in params_str:gmatch("[^,;]+") do
  334. local param = {}
  335. param.type = ""
  336. if param_str:find("^%s*(const%s+)") ~= nil then
  337. param.type = "const "
  338. param_str = param_str:gsub("^%s*((const%s+)", "")
  339. end
  340. param.type = param.type..param_str:gsub("^%s*([%w_]+).*$", "%1")
  341. param.ptr = param_str:gsub("^%s*[%w_]+([%*&]?).*$", "%1")
  342. param.name = param_str:gsub("^%s*[%w_]+[%*&]?%s+([%w_]+).*$", "%1")
  343. param.def = ""
  344. if param_str:find(".+=.+") then
  345. param.def = param_str:gsub("^.*=%s*(.*)$", "%1")
  346. end
  347. table.insert(element.params, param)
  348. end
  349. else
  350. local param = {}
  351. param.type = "void"
  352. param.ptr = ""
  353. param.name = ""
  354. param.def = ""
  355. table.insert(element.params, param)
  356. end
  357. end
  358. -- Type Detect (Property)
  359. elseif line:find("^.*%s+([%w_]+)%s*.*;%s*$") ~= nil then
  360. element.name = line:gsub("^.*%s+([%w_]+)%s*.*;%s*$", "%1")
  361. element.type = "properties"
  362. end
  363. return element
  364. end
  365. function printElementDescription(className, classScope, line, description)
  366. if description ~= "" then
  367. local currentElement = getElementFromLine(line)
  368. -- Search Class & Function/Property, Then Map
  369. if currentElement.name ~= nil and currentElement.type ~= nil then
  370. --print("Name: " .. currentElement.name)
  371. --print("ok (name = \"" .. currentElement.name .. "\", type = \"" .. currentElement.type .. "\", className = \"" .. className .. "\")")
  372. for i, class in ipairs(classes) do
  373. if class.name == className then
  374. --print("Class: "..class.name.." = "..className)
  375. if class[currentElement.type] ~= nil then
  376. for j, storedElement in ipairs(class[currentElement.type]) do
  377. local isSameName = false
  378. if storedElement.name == currentElement.name then
  379. isSameName = true
  380. -- Consider that the name is the same if it has an additionnal "_" at the end and if it's a property
  381. elseif storedElement.name .. "_" == currentElement.name and currentElement.type == "properties" then
  382. isSameName = true
  383. end
  384. if isSameName == true then
  385. --print("Element: " .. storedElement.name .. " = " .. currentElement.name)
  386. -- Confirm that the function is the same and not an overloading one
  387. local isSameElement = true
  388. if currentElement.type == "functions" then
  389. local candidateElement = {declarations = currentElement.params}
  390. candidateElement.name = currentElement.name
  391. isSameElement = isSameFunction(candidateElement, storedElement, true)
  392. --if isSameElement == true then print("Is same element? True") else print("Is same element? False") end
  393. end
  394. if isSameElement == true then
  395. --print("Element: " .. storedElement.name .. " = " .. currentElement.name)
  396. if storedElement.descriptions == nil then
  397. --print("[New description table]")
  398. classes[i][currentElement.type][j].descriptions = {}
  399. end
  400. --print("Description: "..description)
  401. --print("")
  402. table.insert(classes[i][currentElement.type][j].descriptions, description)
  403. return
  404. end
  405. end
  406. end
  407. end
  408. end
  409. end
  410. --print("")
  411. end
  412. end
  413. end
  414. function writeClass(file, class)
  415. file:write("<a name=\"Class_" .. class.name .. "\"></a>\n")
  416. if class.base == "" then
  417. file:write("### " .. class.name .. "\n\n")
  418. else
  419. file:write("### " .. class.name .. " : " .. class.base .. "\n")
  420. end
  421. if class.functions ~= nil then
  422. file:write("\nMethods:\n\n")
  423. for i, func in ipairs(class.functions) do
  424. writeFunction(file, func)
  425. end
  426. end
  427. if class.properties ~= nil then
  428. file:write("\nProperties:\n\n")
  429. for i, property in ipairs(class.properties) do
  430. writeProperty(file, property)
  431. end
  432. end
  433. file:write("\n")
  434. end
  435. function writeTableOfContents(file)
  436. file:write("\n\\section LuaScriptAPI_TableOfContents Table of contents\n\n")
  437. file:write("\\ref LuaScriptAPI_ClassList \"Class list\"<br>\n")
  438. file:write("\\ref LuaScriptAPI_Classes \"Classes\"<br>\n")
  439. file:write("\\ref LuaScriptAPI_Enums \"Enumerations\"<br>\n")
  440. file:write("\\ref LuaScriptAPI_toluaFunctions \"tolua functions\"<br>\n")
  441. file:write("\\ref LuaScriptAPI_GlobalFunctions \"Global functions\"<br>\n")
  442. file:write("\\ref LuaScriptAPI_GlobalProperties \"Global properties\"<br>\n")
  443. file:write("\\ref LuaScriptAPI_GlobalConstants \"Global constants\"<br>\n")
  444. end
  445. function writeClassList(file)
  446. sortByName(classes)
  447. file:write("\n\\section LuaScriptAPI_ClassList Class list\n\n")
  448. for i, class in ipairs(classes) do
  449. file:write("<a href=\"#Class_" .. class.name .. "\"><b>" .. class.name .. "</b></a>\n")
  450. end
  451. end
  452. function writeClasses(file)
  453. file:write("\n\\section LuaScriptAPI_Classes Classes\n\n")
  454. for i, class in ipairs(classes) do
  455. writeClass(file, class)
  456. end
  457. end
  458. function writeEnumerates(file)
  459. sortByName(enumerates)
  460. file:write("\\section LuaScriptAPI_Enums Enumerations\n\n")
  461. for i, enumerate in ipairs(enumerates) do
  462. file:write("### " .. enumerate.name .. "\n\n")
  463. for i, value in ipairs(enumerate.values) do
  464. file:write("- int " .. value .. "\n")
  465. end
  466. file:write("\n")
  467. end
  468. end
  469. function writeFunction(file, func)
  470. if func.name:match("^_") then
  471. -- Skip internal functions
  472. return
  473. end
  474. local line = "- "
  475. -- construct function
  476. if func.type == "" and func.ptr == "" then
  477. line = line .. func.name .. "("
  478. else
  479. line = line .. func.type .. func.ptr .. " " .. func.name .. "("
  480. end
  481. -- write parameters
  482. if func.declarations ~= nil then
  483. local count = table.maxn(func.declarations)
  484. for i = 1, count do
  485. local declaration = func.declarations[i]
  486. if declaration.type ~= "void" then
  487. line = line .. declaration.type .. declaration.ptr .. " " .. declaration.name
  488. -- add parameter default value
  489. if declaration.def ~= "" then
  490. line = line .. " = " .. declaration.def
  491. end
  492. end
  493. if i ~= count then
  494. line = line .. ", "
  495. end
  496. end
  497. end
  498. line = line .. ")"
  499. -- add const
  500. if func.const ~= "" then
  501. line = line .. " " .. func.const
  502. end
  503. file:write(line .. "\n")
  504. end
  505. function writeGlobalConstants(file)
  506. sortByName(globalConstants)
  507. file:write("\n\\section LuaScriptAPI_GlobalConstants Global constants\n")
  508. for i, constant in ipairs(globalConstants) do
  509. local line = "- " .. constant.type:gsub("const ", "") .. constant.ptr .. " " .. constant.name
  510. file:write(line .. "\n")
  511. end
  512. file:write("\n")
  513. end
  514. function writeToluaFunctions(file)
  515. file:write("\n\\section LuaScriptAPI_toluaFunctions tolua functions\n")
  516. file:write("- lua_usertype cast(lua_usertype object, const char* typename)\n")
  517. file:write("- lua_table getpeer(lua_userdata object)\n")
  518. file:write("- void inherit(lua_table table, lua_usertype c_instance)\n")
  519. file:write("- bool releaseownership(lua_usertype object)\n")
  520. file:write("- void setpeer(lua_userdata object, lua_table table)\n")
  521. file:write("- bool takeownership(lua_usertype object)\n")
  522. file:write("- const char* type(lua_object object)\n")
  523. file:write("\n")
  524. end
  525. function writeGlobalFunctions(file)
  526. sortByName(globalFunctions)
  527. file:write("\n\\section LuaScriptAPI_GlobalFunctions Global functions\n")
  528. for i, func in ipairs(globalFunctions) do
  529. writeFunction(file, func)
  530. end
  531. file:write("\n")
  532. end
  533. function writeGlobalProperties(file)
  534. sortByName(globalProperties)
  535. file:write("\n\\section LuaScriptAPI_GlobalProperties Global properties\n")
  536. for i, property in ipairs(globalProperties) do
  537. writeProperty(file, property)
  538. end
  539. end
  540. function writeProperty(file, property)
  541. file:write("- " .. property.type .. property.ptr .. " " .. property.name)
  542. if property.mod:find("tolua_readonly") == nil then
  543. file:write("\n")
  544. else
  545. file:write(" (readonly)\n")
  546. end
  547. end
  548. function classPackage:print()
  549. curDir = getCurrentDirectory()
  550. if flags.o == nil then
  551. print("Invalid output filename");
  552. return
  553. end
  554. local filename = flags.o
  555. local file = io.open(filename, "wt")
  556. file:write("namespace Urho3D\n")
  557. file:write("{\n")
  558. file:write("\n")
  559. file:write("/**\n")
  560. file:write("\\page LuaScriptAPI Lua scripting API\n")
  561. local i = 1
  562. while self[i] do
  563. self[i]:print("","")
  564. i = i + 1
  565. end
  566. printDescriptionsFromPackageFile(flags.f)
  567. writeTableOfContents(file)
  568. writeClassList(file)
  569. writeClasses(file)
  570. writeEnumerates(file)
  571. writeToluaFunctions(file)
  572. writeGlobalFunctions(file)
  573. writeGlobalProperties(file)
  574. writeGlobalConstants(file)
  575. file:write("*/\n")
  576. file:write("\n")
  577. file:write("}\n")
  578. file:close()
  579. end