You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

290 lines
8.6KB

  1. local doc = require "wslua.doc"
  2. local fndef = require "wslua.fndef"
  3. local ty = require "wslua.ty"
  4. local misc = require "wslua.misc"
  5. doc.module{
  6. name = "wslua.class",
  7. what = "Class functionality in Lua",
  8. }
  9. local class = {}
  10. setmetatable(class, {__call = fndef({
  11. name = "class",
  12. what = "Create a class.",
  13. args = {
  14. {},
  15. {
  16. name = "data",
  17. type = ty.key{
  18. name = ty.string,
  19. base = #ty.table,
  20. what = ty.string,
  21. descr = #ty.string,
  22. fields = #ty.list(ty.key{
  23. name = ty.string,
  24. what = ty.string,
  25. descr = #ty.string,
  26. }),
  27. },
  28. what = "Class data.",
  29. fields = {
  30. {name = "name", what = "Human readable class name."},
  31. {name = "base", what = "Base class to inherit, defaults to `class.object`."},
  32. {name = "what", what = "Short description."},
  33. {name = "descr", what = "Longer class details."},
  34. {name = "fields", what = "Description of member variables."},
  35. },
  36. },
  37. },
  38. ret = {
  39. {
  40. type = ty.table,
  41. what = "Class table to be filled with functions.",
  42. descr = [[
  43. Every value assigned into this table is read-accessible through any object created from this class. Additionally,
  44. metamethods with well-known names are automatically inserted into the object metatable. All fields listed below are
  45. initially `nil` and intended to be provided by the user if desired, unless explicitly noted otherwise. Note that all
  46. values should be set before the first object is constructed or derived classes are created.
  47. ]],
  48. fields = {
  49. {
  50. name = "__new",
  51. type = #ty.Function,
  52. what = "Constructor function.",
  53. descr = [[
  54. Receives the arguments passed to the class constructor, and is then expected to modify the return value of a `__super`
  55. call to create and return an approriate object. Defaults to `function(...) return myclass.__super(...) end` if not
  56. provided.
  57. ]],
  58. },
  59. {
  60. name = "__super",
  61. type = #ty.Function,
  62. what = "Wrapped base class constructor to be called inside `__new`. Already provided.",
  63. descr = [[
  64. Passes all of its arguments to the constructor of the base class, and modifies the returned result to be an instance of
  65. the correct class. `__new` should assign relevant fields to the returned object to bring it into a valid state before
  66. returning it.
  67. ]],
  68. },
  69. {name = "__add" , type = #ty.Function, what = "`__add` metamethod." },
  70. {name = "__band" , type = #ty.Function, what = "`__band` metamethod." },
  71. {name = "__bnot" , type = #ty.Function, what = "`__bnot` metamethod." },
  72. {name = "__bor" , type = #ty.Function, what = "`__bor` metamethod." },
  73. {name = "__bxor" , type = #ty.Function, what = "`__bxor` metamethod." },
  74. {name = "__call" , type = #ty.Function, what = "`__call` metamethod." },
  75. {name = "__concat" , type = #ty.Function, what = "`__concat` metamethod." },
  76. {name = "__div" , type = #ty.Function, what = "`__div` metamethod." },
  77. {name = "__eq" , type = #ty.Function, what = "`__eq` metamethod." },
  78. {name = "__idiv" , type = #ty.Function, what = "`__idiv` metamethod." },
  79. {
  80. name = "__index",
  81. type = #ty.Function,
  82. what = "`__index` metamethod.",
  83. descr = [[
  84. This function is called **before** class indexing and inheritance have taken place, be careful not to overwrite class
  85. functionality with this!
  86. ]],
  87. },
  88. {name = "__le" , type = #ty.Function, what = "`__le` metamethod." },
  89. {name = "__len" , type = #ty.Function, what = "`__len` metamethod." },
  90. {name = "__lt" , type = #ty.Function, what = "`__lt` metamethod." },
  91. {name = "__mod" , type = #ty.Function, what = "`__mod` metamethod." },
  92. {name = "__mul" , type = #ty.Function, what = "`__mul` metamethod." },
  93. {name = "__newindex", type = #ty.Function, what = "`__newindex` metamethod."},
  94. {name = "__pow" , type = #ty.Function, what = "`__pow` metamethod." },
  95. {name = "__shl" , type = #ty.Function, what = "`__shl` metamethod." },
  96. {name = "__shr" , type = #ty.Function, what = "`__shr` metamethod." },
  97. {name = "__show" , type = #ty.Function, what = "`__show` metamethod." },
  98. {name = "__sub" , type = #ty.Function, what = "`__sub` metamethod." },
  99. {name = "__tostring", type = #ty.Function, what = "`__tostring` metamethod."},
  100. {name = "__unm" , type = #ty.Function, what = "`__unm` metamethod." },
  101. },
  102. },
  103. },
  104. ex = [[
  105. local class = require "wslua.class"
  106. local animal = class{name = "animal")
  107. function animal.__new(name) return animal.__super():set{name = name} end
  108. function animal:getName() return self.name end
  109. local cat = class{name = "cat", base = animal}
  110. function cat.__new(name) return cat.__super(name) end
  111. function cat:noise() return "meow" end
  112. local dog = class{name = "dog", base = animal}
  113. function dog.__new(name) return dog.__super(name) end
  114. function dog:noise() return "woof" end
  115. local garfield = cat("garfield")
  116. print(garfield:getName(), garfield:noise())
  117. local snoopy = dog("snoopy")
  118. print(snoopy:getName(), snoopy:noise())
  119. ]],
  120. },
  121. function(_, data)
  122. data.base = data.base or (not data.__is_object and class.object or false)
  123. require "wslua.core.doc" .handler.class(data)
  124. local ret = {}
  125. ret.name = data.name
  126. ret.base = data.base
  127. local mt = {}
  128. mt.__index = ret.base or nil
  129. mt.__tostring = function() return data.name end
  130. ret.__super = function(...)
  131. return setmetatable(ret.base and ret.base(...) or {}, {
  132. class = ret ,
  133. __add = ret.__add ,
  134. __sub = ret.__sub ,
  135. __mul = ret.__mul ,
  136. __div = ret.__div ,
  137. __mod = ret.__mod ,
  138. __pow = ret.__pow ,
  139. __unm = ret.__unm ,
  140. __idiv = ret.__idiv ,
  141. __band = ret.__band ,
  142. __bor = ret.__bor ,
  143. __bxor = ret.__bxor ,
  144. __bnot = ret.__bnot ,
  145. __shl = ret.__shl ,
  146. __shr = ret.__shr ,
  147. __concat = ret.__concat ,
  148. __len = ret.__len ,
  149. __eq = ret.__eq ,
  150. __lt = ret.__lt ,
  151. __le = ret.__le ,
  152. __index = ret.__index
  153. and function(obj, k)
  154. local r = ret.__index(obj, k)
  155. if r ~= nil then return r else return ret[k] end
  156. end
  157. or ret,
  158. __newindex = ret.__newindex,
  159. __call = ret.__call ,
  160. __tostring = ret.__tostring,
  161. __show = ret.__show ,
  162. })
  163. end
  164. function mt:__call(...)
  165. return (rawget(self, "__new") or self.__super)(...)
  166. end
  167. return setmetatable(ret, mt)
  168. end)})
  169. doc.section "The `class.object` common superclass"
  170. class.object = class{
  171. name = "class.object",
  172. what = "Common superclass for all classes.",
  173. descr = "Provides some basic functionality.",
  174. __is_object = true,
  175. }
  176. class.object.class = fndef({
  177. name = "class.object:class",
  178. what = "Returns the class table of an object.",
  179. args = {
  180. {name = "self", type = ty.class(class.object)},
  181. },
  182. ret = {
  183. {type = ty.table, what = "Class table of `self`."},
  184. },
  185. },
  186. function(self)
  187. return getmetatable(self).class
  188. end)
  189. class.object.instanceof = fndef({
  190. name = "class.object:instanceof",
  191. what = "Checks whether `self` is an instance of `x` or its derived classes.",
  192. args = {
  193. {name = "self", type = ty.class(class.object)},
  194. {name = "x", type = ty.table, what = "Class table."},
  195. },
  196. ret = {
  197. {type = ty.boolean},
  198. },
  199. },
  200. function(self, x)
  201. local class = self:class()
  202. while class do
  203. if x == class then return true end
  204. class = class.base
  205. end
  206. return false
  207. end)
  208. class.object.set = fndef({
  209. name = "class.object:set",
  210. what = "Update fields of the object using the fields from the table.",
  211. args = {
  212. {name = "self", type = ty.class(class.object)},
  213. {name = "values", type = ty.table, what = "Table to take update values from."},
  214. },
  215. ret = {
  216. {type = ty.class(class.object), what = "Updated `self`."},
  217. },
  218. },
  219. function(self, values)
  220. for k, v in pairs(values) do self[k] = v end
  221. return self
  222. end)
  223. class.object.dup = fndef({
  224. name = "class.object:dup",
  225. what = "Create a shallow copy of `self`.",
  226. args = {
  227. {name = "self", type = ty.class(class.object)},
  228. },
  229. ret = {
  230. {type = ty.class(class.object)},
  231. },
  232. },
  233. function(self)
  234. local ret = {}
  235. for k, v in pairs(self) do
  236. ret[k] = v
  237. end
  238. return setmetatable(ret, getmetatable(self))
  239. end)
  240. class.object.show = fndef({
  241. name = "class.object:show",
  242. what = "Convert the object to a string using `wslua.misc.show`.",
  243. args = {
  244. {name = "self", type = ty.class(class.object)},
  245. {name = "opts", type = misc.showopts, what = "Conversion options."},
  246. },
  247. ret = {
  248. {type = ty.string},
  249. },
  250. see = {
  251. {module = "wslua.misc", name = "misc.show"},
  252. }
  253. },
  254. function(self, opts)
  255. return misc.show(self, opts)
  256. end)
  257. return class