ToDoxHook.lua 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  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. 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 writeClasses(file)
  436. sortByName(classes)
  437. file:write("\n\\section LuaScriptAPI_Classes Classes\n\n")
  438. for i, class in ipairs(classes) do
  439. writeClass(file, class)
  440. end
  441. end
  442. function writeEnumerates(file)
  443. sortByName(enumerates)
  444. file:write("\\section LuaScriptAPI_Enums Enumerations\n\n")
  445. for i, enumerate in ipairs(enumerates) do
  446. file:write("### " .. enumerate.name .. "\n\n")
  447. for i, value in ipairs(enumerate.values) do
  448. file:write("- int " .. value .. "\n")
  449. end
  450. file:write("\n")
  451. end
  452. end
  453. function writeFunction(file, func)
  454. local line = "- "
  455. -- construct function
  456. if func.type == "" and func.ptr == "" then
  457. line = line .. func.name .. "("
  458. else
  459. line = line .. func.type .. func.ptr .. " " .. func.name .. "("
  460. end
  461. -- write parameters
  462. if func.declarations ~= nil then
  463. local count = table.maxn(func.declarations)
  464. for i = 1, count do
  465. local declaration = func.declarations[i]
  466. if declaration.type ~= "void" then
  467. line = line .. declaration.type .. declaration.ptr .. " " .. declaration.name
  468. -- add paramter default value
  469. if declaration.def ~= "" then
  470. line = line .. " = " .. declaration.def
  471. end
  472. end
  473. if i ~= count then
  474. line = line .. ", "
  475. end
  476. end
  477. end
  478. line = line .. ")"
  479. -- add const
  480. if func.const ~= "" then
  481. line = line .. " " .. func.const
  482. end
  483. file:write(line .. "\n")
  484. end
  485. function writeGlobalConstants(file)
  486. sortByName(globalConstants)
  487. file:write("\n\\section LuaScriptAPI_GlobalConstants Global constants\n")
  488. for i, constant in ipairs(globalConstants) do
  489. local line = "- " .. constant.type:gsub("const ", "") .. constant.ptr .. " " .. constant.name
  490. file:write(line .. "\n")
  491. end
  492. file:write("\n")
  493. end
  494. function writeGlobalFunctions(file)
  495. sortByName(globalFunctions)
  496. file:write("\n\\section LuaScriptAPI_GlobalFunctions Global functions\n")
  497. for i, func in ipairs(globalFunctions) do
  498. writeFunction(file, func)
  499. end
  500. file:write("\n")
  501. end
  502. function writeGlobalProperties(file)
  503. sortByName(globalProperties)
  504. file:write("\n\\section LuaScriptAPI_GlobalProperties Global properties\n")
  505. for i, property in ipairs(globalProperties) do
  506. writeProperty(file, property)
  507. end
  508. end
  509. function writeProperty(file, property)
  510. file:write("- " .. property.type .. property.ptr .. " " .. property.name)
  511. if property.mod:find("tolua_readonly") == nil then
  512. file:write("\n")
  513. else
  514. file:write(" (readonly)\n")
  515. end
  516. end
  517. function classPackage:print()
  518. curDir = getCurrentDirectory()
  519. if flags.o == nil then
  520. print("Invalid output filename");
  521. return
  522. end
  523. local filename = flags.o
  524. local file = io.open(filename, "wt")
  525. file:write("namespace Urho3D\n")
  526. file:write("{\n")
  527. file:write("\n")
  528. file:write("/**\n")
  529. file:write("\\page LuaScriptAPI Lua Scripting API\n")
  530. local i = 1
  531. while self[i] do
  532. self[i]:print("","")
  533. i = i + 1
  534. end
  535. printDescriptionsFromPackageFile(flags.f)
  536. writeClasses(file)
  537. writeEnumerates(file)
  538. writeGlobalFunctions(file)
  539. writeGlobalProperties(file)
  540. writeGlobalConstants(file)
  541. file:write("*/\n")
  542. file:write("\n")
  543. file:write("}\n")
  544. file:close()
  545. end