luau.lua 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. local preamble = [[
  2. declare extern type userdata with end
  3. declare extern type lightuserdata with end
  4. declare extern type quaternion with
  5. x: number
  6. y: number
  7. z: number
  8. w: number
  9. end
  10. type Vec2 = {number}
  11. type Vec3 = {number} | vector
  12. type Vec4 = {number}
  13. type Quat = {number} | quaternion
  14. type Mat4 = {number}
  15. declare class Joint end
  16. declare class Shape end
  17. ]]
  18. local genFunctionType
  19. local function genType(info)
  20. local types = {}
  21. for t in info.type:gmatch('[%w{}%*%.]+') do
  22. if t == 'function' then -- TODO
  23. table.insert(types, genFunctionType(info))
  24. elseif t == '*' then
  25. table.insert(types, 'any')
  26. elseif t == 'table' then
  27. table.insert(types, '{}')
  28. else
  29. table.insert(types, t)
  30. end
  31. end
  32. if #types == 1 then
  33. return types[1] .. (info.default and '?' or '')
  34. else
  35. return table.concat(types, ' | ') .. (info.default and ' | nil' or '')
  36. end
  37. end
  38. local function genArguments(arguments, ismethod)
  39. local t = {}
  40. for _, arg in ipairs(arguments) do
  41. local name, type = arg.name, genType(arg)
  42. if name:match('%.%.%.') then
  43. if ismethod then
  44. table.insert(t, '...: ' .. type)
  45. else
  46. table.insert(t, '...' .. type)
  47. end
  48. else
  49. table.insert(t, ('%s: %s'):format(name, type))
  50. end
  51. end
  52. return table.concat(t, ', ')
  53. end
  54. local function genReturns(returns)
  55. local t = {}
  56. for _, ret in ipairs(returns) do
  57. table.insert(t, genType(ret))
  58. end
  59. return table.concat(t, ', ')
  60. end
  61. genFunctionType = function(fn)
  62. if not fn.arguments or not fn.returns then
  63. return '() -> ()'
  64. end
  65. local args = genArguments(fn.arguments)
  66. local rets = genReturns(fn.returns)
  67. if #fn.returns == 1 and fn.returns[1].type ~= 'function' then
  68. return ('(%s) -> %s'):format(args, rets)
  69. else
  70. return ('(%s) -> (%s)'):format(args, rets)
  71. end
  72. end
  73. local function genMethod(method, variant)
  74. local args = genArguments(variant.arguments, true)
  75. local rets = genReturns(variant.returns)
  76. if args == '' then
  77. args = 'self'
  78. else
  79. args = 'self, ' .. args
  80. end
  81. if #variant.returns > 1 or rets:match('%(') then
  82. rets = (': (%s)'):format(rets)
  83. elseif #variant.returns == 1 then
  84. rets = ': ' .. rets
  85. end
  86. return (' function %s(%s)%s'):format(method.name, args, rets)
  87. end
  88. return function(api)
  89. local directory = lovr.filesystem.getSource() .. '/luau'
  90. if lovr.system.getOS() == 'Windows' then
  91. os.execute('mkdir ' .. directory:gsub('/', '\\'))
  92. else
  93. os.execute('mkdir -p ' .. directory)
  94. end
  95. local out = {}
  96. local function write(s, ...)
  97. table.insert(out, s:format(...))
  98. end
  99. write(preamble:gsub('^%s*', ''))
  100. write('\n')
  101. local function writeFunction(fn)
  102. if #fn.variants > 1 then
  103. write(' %s:\n', fn.name)
  104. for i, variant in ipairs(fn.variants) do
  105. write(' & (%s)%s\n', genFunctionType(variant), i == #fn.variants and ',' or '')
  106. end
  107. else
  108. write(' %s: %s,\n', fn.name, genFunctionType(fn.variants[1]))
  109. end
  110. end
  111. for _, module in ipairs(api.modules) do
  112. for _, enum in ipairs(module.enums) do
  113. write('type %s =\n', enum.name)
  114. for _, value in ipairs(enum.values) do
  115. write(' | %q\n', value.name)
  116. end
  117. write('\n')
  118. end
  119. local ignore = {
  120. Vec2 = true,
  121. Vec3 = true,
  122. Vec4 = true,
  123. Quat = true,
  124. Mat4 = true,
  125. Vectors = true
  126. }
  127. for _, object in ipairs(module.objects) do
  128. if not ignore[object.name] then
  129. write('declare class %s', object.name)
  130. if object.extends then
  131. write(' extends %s', object.extends)
  132. end
  133. write('\n')
  134. for _, method in ipairs(object.methods) do
  135. for _, variant in ipairs(method.variants) do
  136. write('%s\n', genMethod(method, variant))
  137. end
  138. end
  139. write('end\n\n')
  140. end
  141. end
  142. if module.name ~= 'lovr' and #module.functions > 0 then
  143. write('type %sModule = {\n', module.name:gsub('^%l', string.upper))
  144. for _, fn in ipairs(module.functions) do
  145. writeFunction(fn)
  146. end
  147. write('}\n\n')
  148. end
  149. end
  150. write('declare lovr: {\n')
  151. for _, module in ipairs(api.modules) do
  152. if module.name == 'lovr' then
  153. for _, fn in ipairs(module.functions) do
  154. writeFunction(fn)
  155. end
  156. end
  157. end
  158. write('\n')
  159. for _, callback in ipairs(api.callbacks) do
  160. writeFunction(callback)
  161. end
  162. write('\n')
  163. for _, module in ipairs(api.modules) do
  164. if module.name ~= 'lovr' and #module.functions > 0 then
  165. write(' %s: %sModule,\n', module.name, module.name:gsub('^%l', string.upper))
  166. end
  167. end
  168. write('}\n')
  169. local file = assert(io.open(directory .. '/lovr.d.luau', 'w'))
  170. file:write(table.concat(out):sub(1, -2))
  171. file:close()
  172. end