ToDoxHook.lua 19 KB

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