ToDoxHook.lua 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634
  1. --
  2. -- Copyright (c) 2008-2014 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. if directory ~= nil then
  287. sourceEnginePath = sourceEnginePath .. directory .. curDir.separator
  288. end
  289. for line in io.lines(sourceEnginePath .. filename) do
  290. line = line:gsub("%c", "")
  291. -- Entering Class
  292. if line:find("^%s*[Cc]lass%s+(.+)") ~= nil then
  293. local classDefine = line:gsub("^%s*[Cc]lass%s+([%w_][^:;]*)%s*:*.*", "%1")
  294. className = classDefine:gsub("[%w_]+%s+([%w_]+).*", "%1")
  295. -- Struct Defined (same as Class)
  296. elseif line:find("^%s*[Ss]truct%s+(.+)") ~= nil then
  297. local classDefine = line:gsub("^%s*[Ss]truct%s+([%w_][^:;]*)%s*:*.*", "%1")
  298. className = classDefine:gsub("[%w_]+%s+([%w_]+).*", "%1")
  299. elseif className ~= nil then
  300. -- Detecting Scope
  301. if line:find("^%s*(%w+)%s*:%s*$") ~= nil then
  302. classScope = line:gsub("^%s*(%w)%s*:%s*$", "%1")
  303. -- Leaving Class
  304. elseif line:find("^%s*}%s*$") ~= nil then
  305. className = nil
  306. classScope = nil
  307. description = nil
  308. -- Gather Informations
  309. elseif className ~= nil and classScope ~= nil then
  310. -- Line stating with "///" (Description)
  311. if line:find("^%s*///") ~= nil then
  312. description = line:gsub("^%s*///%s*", "")
  313. -- Not Empty Line (Function)
  314. elseif line:find("^%s*$") == nil and description ~= nil then
  315. printElementDescription(className, classScope, line, description)
  316. description = nil
  317. end
  318. end
  319. end
  320. end
  321. end
  322. function getElementFromLine(line)
  323. local element = {}
  324. element.name = nil
  325. element.params = nil
  326. element.type = nil
  327. -- Type Detect (Function)
  328. if line:find("^.*%s*([%w_~]+)%s*%(.*%).*$") ~= nil then
  329. element.name = line:gsub("^.*%s+([%w_~]+)%s*%(.*%).*$", "%1")
  330. element.type = "functions"
  331. if line:find("^.+%(.*%)") ~= nil then
  332. element.params = {}
  333. local params_str = line:gsub("^.+%((.*)%).*$", "%1", 1)
  334. --print("Current Params: "..params_str)
  335. if params_str ~= "" then
  336. for param_str in params_str:gmatch("[^,;]+") do
  337. local param = {}
  338. param.type = ""
  339. if param_str:find("^%s*(const%s+)") ~= nil then
  340. param.type = "const "
  341. param_str = param_str:gsub("^%s*((const%s+)", "")
  342. end
  343. param.type = param.type..param_str:gsub("^%s*([%w_]+).*$", "%1")
  344. param.ptr = param_str:gsub("^%s*[%w_]+([%*&]?).*$", "%1")
  345. param.name = param_str:gsub("^%s*[%w_]+[%*&]?%s+([%w_]+).*$", "%1")
  346. param.def = ""
  347. if param_str:find(".+=.+") then
  348. param.def = param_str:gsub("^.*=%s*(.*)$", "%1")
  349. end
  350. table.insert(element.params, param)
  351. end
  352. else
  353. local param = {}
  354. param.type = "void"
  355. param.ptr = ""
  356. param.name = ""
  357. param.def = ""
  358. table.insert(element.params, param)
  359. end
  360. end
  361. -- Type Detect (Property)
  362. elseif line:find("^.*%s+([%w_]+)%s*.*;%s*$") ~= nil then
  363. element.name = line:gsub("^.*%s+([%w_]+)%s*.*;%s*$", "%1")
  364. element.type = "properties"
  365. end
  366. return element
  367. end
  368. function printElementDescription(className, classScope, line, description)
  369. if description ~= "" then
  370. local currentElement = getElementFromLine(line)
  371. -- Search Class & Function/Property, Then Map
  372. if currentElement.name ~= nil and currentElement.type ~= nil then
  373. --print("Name: " .. currentElement.name)
  374. --print("ok (name = \"" .. currentElement.name .. "\", type = \"" .. currentElement.type .. "\", className = \"" .. className .. "\")")
  375. for i, class in ipairs(classes) do
  376. if class.name == className then
  377. --print("Class: "..class.name.." = "..className)
  378. if class[currentElement.type] ~= nil then
  379. for j, storedElement in ipairs(class[currentElement.type]) do
  380. local isSameName = false
  381. if storedElement.name == currentElement.name then
  382. isSameName = true
  383. -- Consider that the name is the same if it has an additionnal "_" at the end and if it's a property
  384. elseif storedElement.name .. "_" == currentElement.name and currentElement.type == "properties" then
  385. isSameName = true
  386. end
  387. if isSameName == true then
  388. --print("Element: " .. storedElement.name .. " = " .. currentElement.name)
  389. -- Confirm that the function is the same and not an overloading one
  390. local isSameElement = true
  391. if currentElement.type == "functions" then
  392. local candidateElement = {declarations = currentElement.params}
  393. candidateElement.name = currentElement.name
  394. isSameElement = isSameFunction(candidateElement, storedElement, true)
  395. --if isSameElement == true then print("Is same element? True") else print("Is same element? False") end
  396. end
  397. if isSameElement == true then
  398. --print("Element: " .. storedElement.name .. " = " .. currentElement.name)
  399. if storedElement.descriptions == nil then
  400. --print("[New description table]")
  401. classes[i][currentElement.type][j].descriptions = {}
  402. end
  403. --print("Description: "..description)
  404. --print("")
  405. table.insert(classes[i][currentElement.type][j].descriptions, description)
  406. return
  407. end
  408. end
  409. end
  410. end
  411. end
  412. end
  413. --print("")
  414. end
  415. end
  416. end
  417. function writeClass(file, class)
  418. file:write("<a name=\"Class_" .. class.name .. "\"></a>\n")
  419. if class.base == "" then
  420. file:write("### " .. class.name .. "\n\n")
  421. else
  422. file:write("### " .. class.name .. " : " .. class.base .. "\n")
  423. end
  424. if class.functions ~= nil then
  425. file:write("\nMethods:\n\n")
  426. for i, func in ipairs(class.functions) do
  427. writeFunction(file, func)
  428. end
  429. end
  430. if class.properties ~= nil then
  431. file:write("\nProperties:\n\n")
  432. for i, property in ipairs(class.properties) do
  433. writeProperty(file, property)
  434. end
  435. end
  436. file:write("\n")
  437. end
  438. function writeTableOfContents(file)
  439. file:write("\n\\section LuaScriptAPI_TableOfContents Table of contents\n\n")
  440. file:write("\\ref LuaScriptAPI_ClassList \"Class list\"<br>\n")
  441. file:write("\\ref LuaScriptAPI_Classes \"Classes\"<br>\n")
  442. file:write("\\ref LuaScriptAPI_Enums \"Enumerations\"<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. local line = "- "
  473. -- construct function
  474. if func.type == "" and func.ptr == "" then
  475. line = line .. func.name .. "("
  476. else
  477. line = line .. func.type .. func.ptr .. " " .. func.name .. "("
  478. end
  479. -- write parameters
  480. if func.declarations ~= nil then
  481. local count = table.maxn(func.declarations)
  482. for i = 1, count do
  483. local declaration = func.declarations[i]
  484. if declaration.type ~= "void" then
  485. line = line .. declaration.type .. declaration.ptr .. " " .. declaration.name
  486. -- add paramter default value
  487. if declaration.def ~= "" then
  488. line = line .. " = " .. declaration.def
  489. end
  490. end
  491. if i ~= count then
  492. line = line .. ", "
  493. end
  494. end
  495. end
  496. line = line .. ")"
  497. -- add const
  498. if func.const ~= "" then
  499. line = line .. " " .. func.const
  500. end
  501. file:write(line .. "\n")
  502. end
  503. function writeGlobalConstants(file)
  504. sortByName(globalConstants)
  505. file:write("\n\\section LuaScriptAPI_GlobalConstants Global constants\n")
  506. for i, constant in ipairs(globalConstants) do
  507. local line = "- " .. constant.type:gsub("const ", "") .. constant.ptr .. " " .. constant.name
  508. file:write(line .. "\n")
  509. end
  510. file:write("\n")
  511. end
  512. function writeGlobalFunctions(file)
  513. sortByName(globalFunctions)
  514. file:write("\n\\section LuaScriptAPI_GlobalFunctions Global functions\n")
  515. for i, func in ipairs(globalFunctions) do
  516. writeFunction(file, func)
  517. end
  518. file:write("\n")
  519. end
  520. function writeGlobalProperties(file)
  521. sortByName(globalProperties)
  522. file:write("\n\\section LuaScriptAPI_GlobalProperties Global properties\n")
  523. for i, property in ipairs(globalProperties) do
  524. writeProperty(file, property)
  525. end
  526. end
  527. function writeProperty(file, property)
  528. file:write("- " .. property.type .. property.ptr .. " " .. property.name)
  529. if property.mod:find("tolua_readonly") == nil then
  530. file:write("\n")
  531. else
  532. file:write(" (readonly)\n")
  533. end
  534. end
  535. function classPackage:print()
  536. curDir = getCurrentDirectory()
  537. if flags.o == nil then
  538. print("Invalid output filename");
  539. return
  540. end
  541. local filename = flags.o
  542. local file = io.open(filename, "wt")
  543. file:write("namespace Urho3D\n")
  544. file:write("{\n")
  545. file:write("\n")
  546. file:write("/**\n")
  547. file:write("\\page LuaScriptAPI Lua scripting API\n")
  548. local i = 1
  549. while self[i] do
  550. self[i]:print("","")
  551. i = i + 1
  552. end
  553. printDescriptionsFromPackageFile(flags.f)
  554. writeTableOfContents(file)
  555. writeClassList(file)
  556. writeClasses(file)
  557. writeEnumerates(file)
  558. writeGlobalFunctions(file)
  559. writeGlobalProperties(file)
  560. writeGlobalConstants(file)
  561. file:write("*/\n")
  562. file:write("\n")
  563. file:write("}\n")
  564. file:close()
  565. end