lume.lua 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780
  1. --
  2. -- lume
  3. --
  4. -- Copyright (c) 2020 rxi
  5. --
  6. -- Permission is hereby granted, free of charge, to any person obtaining a copy of
  7. -- this software and associated documentation files (the "Software"), to deal in
  8. -- the Software without restriction, including without limitation the rights to
  9. -- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  10. -- of the Software, and to permit persons to whom the Software is furnished to do
  11. -- so, subject to the following conditions:
  12. --
  13. -- The above copyright notice and this permission notice shall be included in all
  14. -- copies or substantial portions of the Software.
  15. --
  16. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. -- SOFTWARE.
  23. --
  24. local lume = { _version = "2.3.0" }
  25. local pairs, ipairs = pairs, ipairs
  26. local type, assert, unpack = type, assert, unpack or table.unpack
  27. local tostring, tonumber = tostring, tonumber
  28. local math_floor = math.floor
  29. local math_ceil = math.ceil
  30. local math_atan2 = math.atan2 or math.atan
  31. local math_sqrt = math.sqrt
  32. local math_abs = math.abs
  33. local noop = function()
  34. end
  35. local identity = function(x)
  36. return x
  37. end
  38. local patternescape = function(str)
  39. return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%1")
  40. end
  41. local absindex = function(len, i)
  42. return i < 0 and (len + i + 1) or i
  43. end
  44. local iscallable = function(x)
  45. if type(x) == "function" then return true end
  46. local mt = getmetatable(x)
  47. return mt and mt.__call ~= nil
  48. end
  49. local getiter = function(x)
  50. if lume.isarray(x) then
  51. return ipairs
  52. elseif type(x) == "table" then
  53. return pairs
  54. end
  55. error("expected table", 3)
  56. end
  57. local iteratee = function(x)
  58. if x == nil then return identity end
  59. if iscallable(x) then return x end
  60. if type(x) == "table" then
  61. return function(z)
  62. for k, v in pairs(x) do
  63. if z[k] ~= v then return false end
  64. end
  65. return true
  66. end
  67. end
  68. return function(z) return z[x] end
  69. end
  70. function lume.clamp(x, min, max)
  71. return x < min and min or (x > max and max or x)
  72. end
  73. function lume.round(x, increment)
  74. if increment then return lume.round(x / increment) * increment end
  75. return x >= 0 and math_floor(x + .5) or math_ceil(x - .5)
  76. end
  77. function lume.sign(x)
  78. return x < 0 and -1 or 1
  79. end
  80. function lume.lerp(a, b, amount)
  81. return a + (b - a) * lume.clamp(amount, 0, 1)
  82. end
  83. function lume.smooth(a, b, amount)
  84. local t = lume.clamp(amount, 0, 1)
  85. local m = t * t * (3 - 2 * t)
  86. return a + (b - a) * m
  87. end
  88. function lume.pingpong(x)
  89. return 1 - math_abs(1 - x % 2)
  90. end
  91. function lume.distance(x1, y1, x2, y2, squared)
  92. local dx = x1 - x2
  93. local dy = y1 - y2
  94. local s = dx * dx + dy * dy
  95. return squared and s or math_sqrt(s)
  96. end
  97. function lume.angle(x1, y1, x2, y2)
  98. return math_atan2(y2 - y1, x2 - x1)
  99. end
  100. function lume.vector(angle, magnitude)
  101. return math.cos(angle) * magnitude, math.sin(angle) * magnitude
  102. end
  103. function lume.random(a, b)
  104. if not a then a, b = 0, 1 end
  105. if not b then b = 0 end
  106. return a + math.random() * (b - a)
  107. end
  108. function lume.randomchoice(t)
  109. return t[math.random(#t)]
  110. end
  111. function lume.weightedchoice(t)
  112. local sum = 0
  113. for _, v in pairs(t) do
  114. assert(v >= 0, "weight value less than zero")
  115. sum = sum + v
  116. end
  117. assert(sum ~= 0, "all weights are zero")
  118. local rnd = lume.random(sum)
  119. for k, v in pairs(t) do
  120. if rnd < v then return k end
  121. rnd = rnd - v
  122. end
  123. end
  124. function lume.isarray(x)
  125. return type(x) == "table" and x[1] ~= nil
  126. end
  127. function lume.push(t, ...)
  128. local n = select("#", ...)
  129. for i = 1, n do
  130. t[#t + 1] = select(i, ...)
  131. end
  132. return ...
  133. end
  134. function lume.remove(t, x)
  135. local iter = getiter(t)
  136. for i, v in iter(t) do
  137. if v == x then
  138. if lume.isarray(t) then
  139. table.remove(t, i)
  140. break
  141. else
  142. t[i] = nil
  143. break
  144. end
  145. end
  146. end
  147. return x
  148. end
  149. function lume.clear(t)
  150. local iter = getiter(t)
  151. for k in iter(t) do
  152. t[k] = nil
  153. end
  154. return t
  155. end
  156. function lume.extend(t, ...)
  157. for i = 1, select("#", ...) do
  158. local x = select(i, ...)
  159. if x then
  160. for k, v in pairs(x) do
  161. t[k] = v
  162. end
  163. end
  164. end
  165. return t
  166. end
  167. function lume.shuffle(t)
  168. local rtn = {}
  169. for i = 1, #t do
  170. local r = math.random(i)
  171. if r ~= i then
  172. rtn[i] = rtn[r]
  173. end
  174. rtn[r] = t[i]
  175. end
  176. return rtn
  177. end
  178. function lume.sort(t, comp)
  179. local rtn = lume.clone(t)
  180. if comp then
  181. if type(comp) == "string" then
  182. table.sort(rtn, function(a, b) return a[comp] < b[comp] end)
  183. else
  184. table.sort(rtn, comp)
  185. end
  186. else
  187. table.sort(rtn)
  188. end
  189. return rtn
  190. end
  191. function lume.array(...)
  192. local t = {}
  193. for x in ... do t[#t + 1] = x end
  194. return t
  195. end
  196. function lume.each(t, fn, ...)
  197. local iter = getiter(t)
  198. if type(fn) == "string" then
  199. for _, v in iter(t) do v[fn](v, ...) end
  200. else
  201. for _, v in iter(t) do fn(v, ...) end
  202. end
  203. return t
  204. end
  205. function lume.map(t, fn)
  206. fn = iteratee(fn)
  207. local iter = getiter(t)
  208. local rtn = {}
  209. for k, v in iter(t) do rtn[k] = fn(v) end
  210. return rtn
  211. end
  212. function lume.all(t, fn)
  213. fn = iteratee(fn)
  214. local iter = getiter(t)
  215. for _, v in iter(t) do
  216. if not fn(v) then return false end
  217. end
  218. return true
  219. end
  220. function lume.any(t, fn)
  221. fn = iteratee(fn)
  222. local iter = getiter(t)
  223. for _, v in iter(t) do
  224. if fn(v) then return true end
  225. end
  226. return false
  227. end
  228. function lume.reduce(t, fn, first)
  229. local started = first ~= nil
  230. local acc = first
  231. local iter = getiter(t)
  232. for _, v in iter(t) do
  233. if started then
  234. acc = fn(acc, v)
  235. else
  236. acc = v
  237. started = true
  238. end
  239. end
  240. assert(started, "reduce of an empty table with no first value")
  241. return acc
  242. end
  243. function lume.unique(t)
  244. local rtn = {}
  245. for k in pairs(lume.invert(t)) do
  246. rtn[#rtn + 1] = k
  247. end
  248. return rtn
  249. end
  250. function lume.filter(t, fn, retainkeys)
  251. fn = iteratee(fn)
  252. local iter = getiter(t)
  253. local rtn = {}
  254. if retainkeys then
  255. for k, v in iter(t) do
  256. if fn(v) then rtn[k] = v end
  257. end
  258. else
  259. for _, v in iter(t) do
  260. if fn(v) then rtn[#rtn + 1] = v end
  261. end
  262. end
  263. return rtn
  264. end
  265. function lume.reject(t, fn, retainkeys)
  266. fn = iteratee(fn)
  267. local iter = getiter(t)
  268. local rtn = {}
  269. if retainkeys then
  270. for k, v in iter(t) do
  271. if not fn(v) then rtn[k] = v end
  272. end
  273. else
  274. for _, v in iter(t) do
  275. if not fn(v) then rtn[#rtn + 1] = v end
  276. end
  277. end
  278. return rtn
  279. end
  280. function lume.merge(...)
  281. local rtn = {}
  282. for i = 1, select("#", ...) do
  283. local t = select(i, ...)
  284. local iter = getiter(t)
  285. for k, v in iter(t) do
  286. rtn[k] = v
  287. end
  288. end
  289. return rtn
  290. end
  291. function lume.concat(...)
  292. local rtn = {}
  293. for i = 1, select("#", ...) do
  294. local t = select(i, ...)
  295. if t ~= nil then
  296. local iter = getiter(t)
  297. for _, v in iter(t) do
  298. rtn[#rtn + 1] = v
  299. end
  300. end
  301. end
  302. return rtn
  303. end
  304. function lume.find(t, value)
  305. local iter = getiter(t)
  306. for k, v in iter(t) do
  307. if v == value then return k end
  308. end
  309. return nil
  310. end
  311. function lume.match(t, fn)
  312. fn = iteratee(fn)
  313. local iter = getiter(t)
  314. for k, v in iter(t) do
  315. if fn(v) then return v, k end
  316. end
  317. return nil
  318. end
  319. function lume.count(t, fn)
  320. local count = 0
  321. local iter = getiter(t)
  322. if fn then
  323. fn = iteratee(fn)
  324. for _, v in iter(t) do
  325. if fn(v) then count = count + 1 end
  326. end
  327. else
  328. if lume.isarray(t) then
  329. return #t
  330. end
  331. for _ in iter(t) do count = count + 1 end
  332. end
  333. return count
  334. end
  335. function lume.slice(t, i, j)
  336. i = i and absindex(#t, i) or 1
  337. j = j and absindex(#t, j) or #t
  338. local rtn = {}
  339. for x = i < 1 and 1 or i, j > #t and #t or j do
  340. rtn[#rtn + 1] = t[x]
  341. end
  342. return rtn
  343. end
  344. function lume.first(t, n)
  345. if not n then return t[1] end
  346. return lume.slice(t, 1, n)
  347. end
  348. function lume.last(t, n)
  349. if not n then return t[#t] end
  350. return lume.slice(t, -n, -1)
  351. end
  352. function lume.invert(t)
  353. local rtn = {}
  354. for k, v in pairs(t) do rtn[v] = k end
  355. return rtn
  356. end
  357. function lume.pick(t, ...)
  358. local rtn = {}
  359. for i = 1, select("#", ...) do
  360. local k = select(i, ...)
  361. rtn[k] = t[k]
  362. end
  363. return rtn
  364. end
  365. function lume.keys(t)
  366. local rtn = {}
  367. local iter = getiter(t)
  368. for k in iter(t) do rtn[#rtn + 1] = k end
  369. return rtn
  370. end
  371. function lume.clone(t)
  372. local rtn = {}
  373. for k, v in pairs(t) do rtn[k] = v end
  374. return rtn
  375. end
  376. function lume.fn(fn, ...)
  377. assert(iscallable(fn), "expected a function as the first argument")
  378. local args = { ... }
  379. return function(...)
  380. local a = lume.concat(args, { ... })
  381. return fn(unpack(a))
  382. end
  383. end
  384. function lume.once(fn, ...)
  385. local f = lume.fn(fn, ...)
  386. local done = false
  387. return function(...)
  388. if done then return end
  389. done = true
  390. return f(...)
  391. end
  392. end
  393. local memoize_fnkey = {}
  394. local memoize_nil = {}
  395. function lume.memoize(fn)
  396. local cache = {}
  397. return function(...)
  398. local c = cache
  399. for i = 1, select("#", ...) do
  400. local a = select(i, ...) or memoize_nil
  401. c[a] = c[a] or {}
  402. c = c[a]
  403. end
  404. c[memoize_fnkey] = c[memoize_fnkey] or {fn(...)}
  405. return unpack(c[memoize_fnkey])
  406. end
  407. end
  408. function lume.combine(...)
  409. local n = select('#', ...)
  410. if n == 0 then return noop end
  411. if n == 1 then
  412. local fn = select(1, ...)
  413. if not fn then return noop end
  414. assert(iscallable(fn), "expected a function or nil")
  415. return fn
  416. end
  417. local funcs = {}
  418. for i = 1, n do
  419. local fn = select(i, ...)
  420. if fn ~= nil then
  421. assert(iscallable(fn), "expected a function or nil")
  422. funcs[#funcs + 1] = fn
  423. end
  424. end
  425. return function(...)
  426. for _, f in ipairs(funcs) do f(...) end
  427. end
  428. end
  429. function lume.call(fn, ...)
  430. if fn then
  431. return fn(...)
  432. end
  433. end
  434. function lume.time(fn, ...)
  435. local start = os.clock()
  436. local rtn = {fn(...)}
  437. return (os.clock() - start), unpack(rtn)
  438. end
  439. local lambda_cache = {}
  440. function lume.lambda(str)
  441. if not lambda_cache[str] then
  442. local args, body = str:match([[^([%w,_ ]-)%->(.-)$]])
  443. assert(args and body, "bad string lambda")
  444. local s = "return function(" .. args .. ")\nreturn " .. body .. "\nend"
  445. lambda_cache[str] = lume.dostring(s)
  446. end
  447. return lambda_cache[str]
  448. end
  449. local serialize
  450. local serialize_map = {
  451. [ "boolean" ] = tostring,
  452. [ "nil" ] = tostring,
  453. [ "string" ] = function(v) return string.format("%q", v) end,
  454. [ "number" ] = function(v)
  455. if v ~= v then return "0/0" -- nan
  456. elseif v == 1 / 0 then return "1/0" -- inf
  457. elseif v == -1 / 0 then return "-1/0" end -- -inf
  458. return tostring(v)
  459. end,
  460. [ "table" ] = function(t, stk)
  461. stk = stk or {}
  462. if stk[t] then error("circular reference") end
  463. local rtn = {}
  464. stk[t] = true
  465. for k, v in pairs(t) do
  466. rtn[#rtn + 1] = "[" .. serialize(k, stk) .. "]=" .. serialize(v, stk)
  467. end
  468. stk[t] = nil
  469. return "{" .. table.concat(rtn, ",") .. "}"
  470. end
  471. }
  472. setmetatable(serialize_map, {
  473. __index = function(_, k) error("unsupported serialize type: " .. k) end
  474. })
  475. serialize = function(x, stk)
  476. return serialize_map[type(x)](x, stk)
  477. end
  478. function lume.serialize(x)
  479. return serialize(x)
  480. end
  481. function lume.deserialize(str)
  482. return lume.dostring("return " .. str)
  483. end
  484. function lume.split(str, sep)
  485. if not sep then
  486. return lume.array(str:gmatch("([%S]+)"))
  487. else
  488. assert(sep ~= "", "empty separator")
  489. local psep = patternescape(sep)
  490. return lume.array((str..sep):gmatch("(.-)("..psep..")"))
  491. end
  492. end
  493. function lume.trim(str, chars)
  494. if not chars then return str:match("^[%s]*(.-)[%s]*$") end
  495. chars = patternescape(chars)
  496. return str:match("^[" .. chars .. "]*(.-)[" .. chars .. "]*$")
  497. end
  498. function lume.wordwrap(str, limit)
  499. limit = limit or 72
  500. local check
  501. if type(limit) == "number" then
  502. check = function(s) return #s >= limit end
  503. else
  504. check = limit
  505. end
  506. local rtn = {}
  507. local line = ""
  508. for word, spaces in str:gmatch("(%S+)(%s*)") do
  509. local s = line .. word
  510. if check(s) then
  511. table.insert(rtn, line .. "\n")
  512. line = word
  513. else
  514. line = s
  515. end
  516. for c in spaces:gmatch(".") do
  517. if c == "\n" then
  518. table.insert(rtn, line .. "\n")
  519. line = ""
  520. else
  521. line = line .. c
  522. end
  523. end
  524. end
  525. table.insert(rtn, line)
  526. return table.concat(rtn)
  527. end
  528. function lume.format(str, vars)
  529. if not vars then return str end
  530. local f = function(x)
  531. return tostring(vars[x] or vars[tonumber(x)] or "{" .. x .. "}")
  532. end
  533. return (str:gsub("{(.-)}", f))
  534. end
  535. function lume.trace(...)
  536. local info = debug.getinfo(2, "Sl")
  537. local t = { info.short_src .. ":" .. info.currentline .. ":" }
  538. for i = 1, select("#", ...) do
  539. local x = select(i, ...)
  540. if type(x) == "number" then
  541. x = string.format("%g", lume.round(x, .01))
  542. end
  543. t[#t + 1] = tostring(x)
  544. end
  545. print(table.concat(t, " "))
  546. end
  547. function lume.dostring(str)
  548. return assert((loadstring or load)(str))()
  549. end
  550. function lume.uuid()
  551. local fn = function(x)
  552. local r = math.random(16) - 1
  553. r = (x == "x") and (r + 1) or (r % 4) + 9
  554. return ("0123456789abcdef"):sub(r, r)
  555. end
  556. return (("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"):gsub("[xy]", fn))
  557. end
  558. function lume.hotswap(modname)
  559. local oldglobal = lume.clone(_G)
  560. local updated = {}
  561. local function update(old, new)
  562. if updated[old] then return end
  563. updated[old] = true
  564. local oldmt, newmt = getmetatable(old), getmetatable(new)
  565. if oldmt and newmt then update(oldmt, newmt) end
  566. for k, v in pairs(new) do
  567. if type(v) == "table" then update(old[k], v) else old[k] = v end
  568. end
  569. end
  570. local err = nil
  571. local function onerror(e)
  572. for k in pairs(_G) do _G[k] = oldglobal[k] end
  573. err = lume.trim(e)
  574. end
  575. local ok, oldmod = pcall(require, modname)
  576. oldmod = ok and oldmod or nil
  577. xpcall(function()
  578. package.loaded[modname] = nil
  579. local newmod = require(modname)
  580. if type(oldmod) == "table" then update(oldmod, newmod) end
  581. for k, v in pairs(oldglobal) do
  582. if v ~= _G[k] and type(v) == "table" then
  583. update(v, _G[k])
  584. _G[k] = v
  585. end
  586. end
  587. end, onerror)
  588. package.loaded[modname] = oldmod
  589. if err then return nil, err end
  590. return oldmod
  591. end
  592. local ripairs_iter = function(t, i)
  593. i = i - 1
  594. local v = t[i]
  595. if v ~= nil then
  596. return i, v
  597. end
  598. end
  599. function lume.ripairs(t)
  600. return ripairs_iter, t, (#t + 1)
  601. end
  602. function lume.color(str, mul)
  603. mul = mul or 1
  604. local r, g, b, a
  605. r, g, b = str:match("#(%x%x)(%x%x)(%x%x)")
  606. if r then
  607. r = tonumber(r, 16) / 0xff
  608. g = tonumber(g, 16) / 0xff
  609. b = tonumber(b, 16) / 0xff
  610. a = 1
  611. elseif str:match("rgba?%s*%([%d%s%.,]+%)") then
  612. local f = str:gmatch("[%d.]+")
  613. r = (f() or 0) / 0xff
  614. g = (f() or 0) / 0xff
  615. b = (f() or 0) / 0xff
  616. a = f() or 1
  617. else
  618. error(("bad color string '%s'"):format(str))
  619. end
  620. return r * mul, g * mul, b * mul, a * mul
  621. end
  622. local chain_mt = {}
  623. chain_mt.__index = lume.map(lume.filter(lume, iscallable, true),
  624. function(fn)
  625. return function(self, ...)
  626. self._value = fn(self._value, ...)
  627. return self
  628. end
  629. end)
  630. chain_mt.__index.result = function(x) return x._value end
  631. function lume.chain(value)
  632. return setmetatable({ _value = value }, chain_mt)
  633. end
  634. setmetatable(lume, {
  635. __call = function(_, ...)
  636. return lume.chain(...)
  637. end
  638. })
  639. return lume