typescript.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. -- done:
  2. --
  3. -- - callbacks
  4. -- - module functions
  5. -- - object methods
  6. -- - rest parameters
  7. -- - uses of the name 'default'
  8. -- - uses of the name '*'
  9. -- - uses of the type '*'
  10. -- - rest returns
  11. -- - interface inheritance
  12. -- - arguments with defaults are optional
  13. -- - vector operators
  14. -- - vec3.up etc
  15. -- - table arguments with specified fields
  16. -- - fn variant documentation
  17. -- - type and interface documentation
  18. -- - show call example in documentation (e.g. `const [x, y, z] = collider.getPosition()`)
  19. -- - fix callbacks being generated with a `this` parameter
  20. --
  21. -- todo:
  22. --
  23. -- - lovr.graphics.getDevice() more specific return value and documentation
  24. -- (available in .table field of return value)
  25. -- - global vector constructors documentation
  26. -- - LuaTable specializations
  27. -- - enet
  28. -- - http
  29. --
  30. -- some recommended changes to lovr-docs i found while working on this:
  31. --
  32. -- - mark draw return value as optional somehow?
  33. -- - make pass.setColor() explicitly accept vec3/vec4
  34. local include_references = false -- generate @see `other` references in documentation
  35. -- todo: the global constructors should be automated in order to include documentation
  36. local vector_ops = [[
  37. /** @noSelf **/ declare function vec2(x?: number, y?: number): Vec2
  38. /** @noSelf **/ declare function vec2(u: Vec2): Vec2
  39. /** @noSelf **/ declare function Vec2(x?: number, y?: number): Vec2
  40. /** @noSelf **/ declare function Vec2(u: Vec2): Vec2
  41. declare namespace vec2 {
  42. const zero: Vec2
  43. const one: Vec2
  44. }
  45. /** @noSelf **/ declare function vec3(x?: number, y?: number, z?: number): Vec3
  46. /** @noSelf **/ declare function vec3(u: Vec3): Vec3
  47. /** @noSelf **/ declare function vec3(m: Mat4): Vec3
  48. /** @noSelf **/ declare function vec3(q: Quat): Vec3
  49. /** @noSelf **/ declare function Vec3(x?: number, y?: number, z?: number): Vec3
  50. /** @noSelf **/ declare function Vec3(u: Vec3): Vec3
  51. /** @noSelf **/ declare function Vec3(m: Mat4): Vec3
  52. /** @noSelf **/ declare function Vec3(q: Quat): Vec3
  53. declare namespace vec3 {
  54. const zero: Vec3
  55. const one: Vec3
  56. const left: Vec3
  57. const right: Vec3
  58. const up: Vec3
  59. const down: Vec3
  60. const back: Vec3
  61. const forward: Vec3
  62. }
  63. /** @noSelf **/ declare function vec4(x?: number, y?: number, z?: number, w?: number): Vec4
  64. /** @noSelf **/ declare function vec4(u: Vec4): Vec4
  65. /** @noSelf **/ declare function Vec4(x?: number, y?: number, z?: number, w?: number): Vec4
  66. /** @noSelf **/ declare function Vec4(u: Vec4): Vec4
  67. declare namespace vec4 {
  68. const zero: Vec4
  69. const one: Vec4
  70. }
  71. /** @noSelf **/ declare function quat(angle?: number, ax?: number, ay?: number, az?: number, raw?: boolean): Quat
  72. /** @noSelf **/ declare function quat(r: Quat): Quat
  73. /** @noSelf **/ declare function quat(v: Vec3): Quat
  74. /** @noSelf **/ declare function quat(v: Vec3, u: Vec3): Quat
  75. /** @noSelf **/ declare function quat(m: Mat4): Quat
  76. /** @noSelf **/ declare function quat(): Quat
  77. /** @noSelf **/ declare function Quat(angle?: number, ax?: number, ay?: number, az?: number, raw?: boolean): Quat
  78. /** @noSelf **/ declare function Quat(r: Quat): Quat
  79. /** @noSelf **/ declare function Quat(v: Vec3): Quat
  80. /** @noSelf **/ declare function Quat(v: Vec3, u: Vec3): Quat
  81. /** @noSelf **/ declare function Quat(m: Mat4): Quat
  82. /** @noSelf **/ declare function Quat(): Quat
  83. declare namespace quat {
  84. const identity: Quat
  85. }
  86. /** @noSelf **/ declare function mat4(): Mat4
  87. /** @noSelf **/ declare function mat4(n: Mat4): Mat4
  88. /** @noSelf **/ declare function mat4(position?: Vec3, scale?: Vec3, rotation?: Quat): Mat4
  89. /** @noSelf **/ declare function mat4(position?: Vec3, rotation?: Quat): Mat4
  90. /** @noSelf **/ declare function mat4(...rest: number[]): Mat4
  91. /** @noSelf **/ declare function mat4(d: number): Mat4
  92. /** @noSelf **/ declare function Mat4(): Mat4
  93. /** @noSelf **/ declare function Mat4(n: Mat4): Mat4
  94. /** @noSelf **/ declare function Mat4(position?: Vec3, scale?: Vec3, rotation?: Quat): Mat4
  95. /** @noSelf **/ declare function Mat4(position?: Vec3, rotation?: Quat): Mat4
  96. /** @noSelf **/ declare function Mat4(...rest: number[]): Mat4
  97. /** @noSelf **/ declare function Mat4(d: number): Mat4
  98. declare interface Vec2 {
  99. add_temp: LuaAdditionMethod<Vec2, Vec2>
  100. sub_temp: LuaSubtractionMethod<Vec2, Vec2>
  101. mul_temp: LuaMultiplicationMethod<Vec2 | number, Vec2>
  102. div_temp: LuaDivisionMethod<Vec2 | number, Vec2>
  103. 1: number
  104. 2: number
  105. x: number
  106. y: number
  107. r: number
  108. g: number
  109. s: number
  110. t: number
  111. }
  112. declare interface Vec3 {
  113. add_temp: LuaAdditionMethod<Vec3, Vec3>
  114. sub_temp: LuaSubtractionMethod<Vec3, Vec3>
  115. mul_temp: LuaMultiplicationMethod<Vec3 | number, Vec3>
  116. div_temp: LuaDivisionMethod<Vec3 | number, Vec3>
  117. 1: number
  118. 2: number
  119. 3: number
  120. x: number
  121. y: number
  122. z: number
  123. r: number
  124. g: number
  125. b: number
  126. s: number
  127. t: number
  128. p: number
  129. }
  130. declare interface Vec4 {
  131. add_temp: LuaAdditionMethod<Vec4, Vec4>
  132. sub_temp: LuaSubtractionMethod<Vec4, Vec4>
  133. mul_temp: LuaMultiplicationMethod<Vec4 | number, Vec4>
  134. div_temp: LuaDivisionMethod<Vec4 | number, Vec4>
  135. 1: number
  136. 2: number
  137. 3: number
  138. 4: number
  139. x: number
  140. y: number
  141. z: number
  142. w: number
  143. r: number
  144. g: number
  145. b: number
  146. a: number
  147. s: number
  148. t: number
  149. p: number
  150. q: number
  151. }
  152. declare interface Quat {
  153. add_temp: LuaAdditionMethod<Quat, Quat>
  154. sub_temp: LuaSubtractionMethod<Quat, Quat>
  155. mul_temp: LuaMultiplicationMethod<Quat, Quat> & LuaMultiplicationMethod<Vec3, Vec3>
  156. div_temp: LuaDivisionMethod<Quat, Quat>
  157. 1: number
  158. 2: number
  159. 3: number
  160. 4: number
  161. x: number
  162. y: number
  163. z: number
  164. w: number
  165. }
  166. declare interface Mat4 {
  167. add_temp: LuaAdditionMethod<Mat4, Mat4>
  168. sub_temp: LuaSubtractionMethod<Mat4, Mat4>
  169. mul_temp: LuaMultiplicationMethod<Mat4 | number, Mat4> & LuaMultiplicationMethod<Vec3, Vec3> & LuaMultiplicationMethod<Vec4, Vec4>
  170. div_temp: LuaDivisionMethod<Mat4 | number, Mat4>
  171. 1: number
  172. 2: number
  173. 3: number
  174. 4: number
  175. 5: number
  176. 6: number
  177. 7: number
  178. 8: number
  179. 9: number
  180. 10: number
  181. 11: number
  182. 12: number
  183. 13: number
  184. 14: number
  185. 15: number
  186. 16: number
  187. }
  188. ]]
  189. return function (api)
  190. local path = lovr.filesystem.getSource() .. '/typescript'
  191. if lovr.system.getOS() == 'Windows' then
  192. os.execute('mkdir ' .. path:gsub('/', '\\'))
  193. else
  194. os.execute('mkdir -p ' .. path)
  195. end
  196. local out = io.open(path .. '/lovr-api.d.ts', 'w')
  197. assert(out)
  198. local function put (...)
  199. out:write(...)
  200. end
  201. local indentation = ''
  202. local function indent()
  203. indentation = (' '):rep(#indentation + 2)
  204. end
  205. local function unindent()
  206. indentation = (' '):rep(#indentation - 2)
  207. end
  208. local function quote_string (str)
  209. return "'" .. str:gsub('\\', '\\\\'):gsub("'", "\\'") .. "'"
  210. end
  211. local function put_doc(doc)
  212. if doc:match('\n') then
  213. put(indentation, '/**\n',
  214. indentation, ' * ', doc:gsub('\n', '\n' .. indentation .. ' * '), '\n',
  215. indentation, ' */\n')
  216. else
  217. put(indentation, '/** ', doc, ' */\n')
  218. end
  219. end
  220. -- todo: more specific types
  221. local type_map = {
  222. table = 'LuaTable',
  223. userdata = 'any',
  224. lightuserdata = 'any',
  225. ['function'] = '(this: void, ...args: any[]) => any',
  226. ['*'] = 'any',
  227. ['Object'] = 'LovrObject',
  228. ['nil'] = 'undefined'
  229. }
  230. local convert_type
  231. local function convert_table (table_fields)
  232. local result = '{ '
  233. for i, field in ipairs (table_fields) do
  234. local optional = field.default and '?' or ''
  235. result = result .. field.name .. optional .. ': ' .. convert_type(field.type, field.table)
  236. if i < #table_fields then
  237. result = result .. ', '
  238. end
  239. end
  240. return result .. '}'
  241. end
  242. local function make_array_type(t)
  243. if t:match '|' then
  244. t = '(' .. t .. ')'
  245. end
  246. return t .. '[]'
  247. end
  248. local function convert_individual_type (t, table_fields)
  249. if t == 'table' and table_fields then
  250. return convert_table (table_fields)
  251. else
  252. return type_map[t] or t
  253. end
  254. end
  255. function convert_type(t, table_fields)
  256. t = t:match('^%s*(.-)%s*$')
  257. local array_pattern = '^{(.*)}$'
  258. if t:match(array_pattern) then
  259. local inner = t:match(array_pattern)
  260. local converted = convert_type(inner)
  261. return make_array_type(converted)
  262. elseif t:match('|') then
  263. local result
  264. for subtype in t:gmatch('[^|]+') do
  265. result = result and (result .. ' | ') or ''
  266. result = result .. convert_type(subtype)
  267. end
  268. return result
  269. else
  270. return convert_individual_type(t, table_fields)
  271. end
  272. end
  273. local name_map = {
  274. ['*'] = 'rest',
  275. }
  276. local function convert_name (n)
  277. return name_map[n] or n
  278. end
  279. local function convert_param (arg, optional)
  280. local name = convert_name(arg.name)
  281. local t = convert_type(arg.type, arg.table)
  282. local pattern = '%.%.%.'
  283. if name:match(pattern) then
  284. name = '...' .. name:gsub(pattern, '')
  285. if name == '...' then
  286. name = name .. 'rest'
  287. end
  288. t = make_array_type(t)
  289. end
  290. return name .. (optional and '?' or '') .. ': ' .. t
  291. end
  292. local function convert_return_values (returns)
  293. local ret = 'void'
  294. if #returns == 1 and (not returns[1].name:match('%.%.%.')) then
  295. ret = convert_type(returns[1].type)
  296. elseif #returns >= 1 then
  297. ret = 'LuaMultiReturn<['
  298. for n, ret_n in ipairs (returns) do
  299. local name = convert_name(ret_n.name)
  300. local t = convert_type(ret_n.type)
  301. if name:match('%.%.%.') then
  302. if name == '...' then
  303. name = '...rest'
  304. end
  305. t = make_array_type(t)
  306. end
  307. ret = ret .. name .. ': ' .. t
  308. if n < #returns then
  309. ret = ret .. ', '
  310. end
  311. end
  312. ret = ret .. ']>'
  313. end
  314. return ret
  315. end
  316. local function put_fn_variant (fn, variant, is_interface)
  317. -- docs
  318. local doc = fn.description
  319. -- show call example
  320. doc = doc .. '\n\n`'
  321. if #variant.returns > 0 then
  322. if #variant.returns > 1 then
  323. doc = doc .. '['
  324. end
  325. for i, ret in ipairs (variant.returns) do
  326. doc = doc .. ret.name
  327. if i < #variant.returns then
  328. doc = doc .. ', '
  329. end
  330. end
  331. if #variant.returns > 1 then
  332. doc = doc .. ']'
  333. end
  334. doc = doc .. ' = '
  335. end
  336. doc = doc .. fn.key:gsub(':', '.')
  337. doc = doc .. '('
  338. for i, arg in ipairs (variant.arguments) do
  339. doc = doc .. arg.name
  340. if i < #variant.arguments then
  341. doc = doc .. ', '
  342. end
  343. end
  344. doc = doc .. ')'
  345. doc = doc .. '`'
  346. if variant.description then
  347. doc = doc .. '\n\n' .. variant.description
  348. end
  349. -- @params and @returns
  350. if #variant.arguments > 0 or #variant.returns > 0 then
  351. doc = doc .. '\n'
  352. if #variant.arguments > 0 then
  353. for _, arg in ipairs(variant.arguments) do
  354. doc = doc .. '\n@param ' .. arg.name .. ' - ' .. arg.description
  355. end
  356. end
  357. if #variant.returns > 0 then
  358. doc = doc .. '\n@returns '
  359. -- todo handle .table field in return value (for lovr.graphics.getDevice() it's there instead of description)
  360. if #variant.returns == 1 then
  361. doc = doc .. (variant.returns[1].description or '')
  362. else
  363. for _, ret in ipairs(variant.returns) do
  364. doc = doc .. '\n' .. ret.name .. ' - ' .. (ret.description or '')
  365. end
  366. end
  367. end
  368. end
  369. if fn.notes then
  370. doc = doc .. '\n\n' .. fn.notes
  371. end
  372. if include_references and fn.related then
  373. doc = doc .. '\n'
  374. for _, rel in ipairs(fn.related) do
  375. doc = doc .. '\n@see {@link ' .. rel:gsub(':', '.') .. '}'
  376. end
  377. end
  378. put_doc(doc)
  379. -- type
  380. put(indentation)
  381. if not is_interface then
  382. put('function ')
  383. end
  384. put(convert_name(fn.name))
  385. put('(')
  386. local last_required = 0
  387. for arg_index, arg in ipairs (variant.arguments) do
  388. if not arg.default then
  389. last_required = arg_index
  390. end
  391. end
  392. for arg_index, arg in ipairs (variant.arguments) do
  393. put(convert_param(arg, arg_index > last_required))
  394. if arg_index < #variant.arguments then
  395. put(', ')
  396. end
  397. end
  398. put('): ')
  399. put(convert_return_values(variant.returns))
  400. put('\n\n')
  401. end
  402. put ('/** @noSelf **/\n')
  403. put ('declare namespace lovr {\n')
  404. indent()
  405. -- callbacks
  406. for _, callback in ipairs (api.callbacks) do
  407. for _, variant in ipairs(callback.variants) do
  408. put_fn_variant (callback, variant)
  409. end
  410. end
  411. put('\n')
  412. -- module functions
  413. for module_index, module in ipairs(api.modules) do
  414. put (indentation, 'namespace ', module.name, ' {\n')
  415. indent()
  416. for _, fn in ipairs(module.functions) do
  417. for _, variant in ipairs (fn.variants) do
  418. put_fn_variant (fn, variant)
  419. end
  420. end
  421. unindent()
  422. put (indentation, '}\n')
  423. if module_index < #api.modules then
  424. put('\n')
  425. end
  426. end
  427. unindent()
  428. put ('}\n\n')
  429. -- module types
  430. for _, module in ipairs(api.modules) do
  431. for _, enum in ipairs(module.enums) do
  432. -- doc
  433. local doc = enum.description
  434. if enum.notes then
  435. doc = doc .. '\n\n' .. enum.notes
  436. end
  437. if include_references and enum.related then
  438. doc = doc .. '\n'
  439. for _, rel in ipairs(enum.related) do
  440. doc = doc .. '\n@see {@link ' .. rel:gsub(':', '.') .. '}'
  441. end
  442. end
  443. put_doc(doc)
  444. -- type
  445. put('declare type ', enum.name, ' = ')
  446. for value_index, value in ipairs(enum.values) do
  447. put(quote_string(value.name))
  448. if value_index < #enum.values then
  449. put(' | ')
  450. end
  451. end
  452. put('\n\n')
  453. end
  454. for _, object in ipairs(module.objects) do
  455. -- doc
  456. local doc = object.description
  457. if object.notes then
  458. doc = doc .. '\n\n' .. object.notes
  459. end
  460. if include_references and object.related then
  461. doc = doc .. '\n'
  462. for _, rel in ipairs(object.related) do
  463. doc = doc .. '\n@see {@link ' .. rel:gsub(':', '.') .. '}'
  464. end
  465. end
  466. put_doc(doc)
  467. -- interface
  468. local extends = object.extends and (' extends ' .. convert_type(object.extends)) or ''
  469. put ('declare interface ', convert_type(object.name), extends, ' {\n')
  470. indent()
  471. for _, method in ipairs (object.methods) do
  472. for _, variant in ipairs (method.variants) do
  473. put_fn_variant (method, variant, true)
  474. end
  475. end
  476. unindent()
  477. put ('}\n\n')
  478. end
  479. end
  480. put(vector_ops)
  481. out:close()
  482. end