satelito

Static [web] site (or page) generator (ssg) made with Lua script.
git clone git://soucy.cc/satelito.git
Log | Files | Refs | README

lume.lua (15772B)


      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 
     25 local lume = { _version = "2.3.0" }
     26 
     27 local pairs, ipairs = pairs, ipairs
     28 local type, assert, unpack = type, assert, unpack or table.unpack
     29 local tostring, tonumber = tostring, tonumber
     30 local math_floor = math.floor
     31 local math_ceil = math.ceil
     32 local math_atan2 = math.atan2 or math.atan
     33 local math_sqrt = math.sqrt
     34 local math_abs = math.abs
     35 
     36 local noop = function()
     37 end
     38 
     39 local identity = function(x)
     40   return x
     41 end
     42 
     43 local patternescape = function(str)
     44   return str:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%1")
     45 end
     46 
     47 local absindex = function(len, i)
     48   return i < 0 and (len + i + 1) or i
     49 end
     50 
     51 local iscallable = function(x)
     52   if type(x) == "function" then return true end
     53   local mt = getmetatable(x)
     54   return mt and mt.__call ~= nil
     55 end
     56 
     57 local getiter = function(x)
     58   if lume.isarray(x) then
     59     return ipairs
     60   elseif type(x) == "table" then
     61     return pairs
     62   end
     63   error("expected table", 3)
     64 end
     65 
     66 local iteratee = function(x)
     67   if x == nil then return identity end
     68   if iscallable(x) then return x end
     69   if type(x) == "table" then
     70     return function(z)
     71       for k, v in pairs(x) do
     72         if z[k] ~= v then return false end
     73       end
     74       return true
     75     end
     76   end
     77   return function(z) return z[x] end
     78 end
     79 
     80 
     81 
     82 function lume.clamp(x, min, max)
     83   return x < min and min or (x > max and max or x)
     84 end
     85 
     86 
     87 function lume.round(x, increment)
     88   if increment then return lume.round(x / increment) * increment end
     89   return x >= 0 and math_floor(x + .5) or math_ceil(x - .5)
     90 end
     91 
     92 
     93 function lume.sign(x)
     94   return x < 0 and -1 or 1
     95 end
     96 
     97 
     98 function lume.lerp(a, b, amount)
     99   return a + (b - a) * lume.clamp(amount, 0, 1)
    100 end
    101 
    102 
    103 function lume.smooth(a, b, amount)
    104   local t = lume.clamp(amount, 0, 1)
    105   local m = t * t * (3 - 2 * t)
    106   return a + (b - a) * m
    107 end
    108 
    109 
    110 function lume.pingpong(x)
    111   return 1 - math_abs(1 - x % 2)
    112 end
    113 
    114 
    115 function lume.distance(x1, y1, x2, y2, squared)
    116   local dx = x1 - x2
    117   local dy = y1 - y2
    118   local s = dx * dx + dy * dy
    119   return squared and s or math_sqrt(s)
    120 end
    121 
    122 
    123 function lume.angle(x1, y1, x2, y2)
    124   return math_atan2(y2 - y1, x2 - x1)
    125 end
    126 
    127 
    128 function lume.vector(angle, magnitude)
    129   return math.cos(angle) * magnitude, math.sin(angle) * magnitude
    130 end
    131 
    132 
    133 function lume.random(a, b)
    134   if not a then a, b = 0, 1 end
    135   if not b then b = 0 end
    136   return a + math.random() * (b - a)
    137 end
    138 
    139 
    140 function lume.randomchoice(t)
    141   return t[math.random(#t)]
    142 end
    143 
    144 
    145 function lume.weightedchoice(t)
    146   local sum = 0
    147   for _, v in pairs(t) do
    148     assert(v >= 0, "weight value less than zero")
    149     sum = sum + v
    150   end
    151   assert(sum ~= 0, "all weights are zero")
    152   local rnd = lume.random(sum)
    153   for k, v in pairs(t) do
    154     if rnd < v then return k end
    155     rnd = rnd - v
    156   end
    157 end
    158 
    159 
    160 function lume.isarray(x)
    161   return type(x) == "table" and x[1] ~= nil
    162 end
    163 
    164 
    165 function lume.push(t, ...)
    166   local n = select("#", ...)
    167   for i = 1, n do
    168     t[#t + 1] = select(i, ...)
    169   end
    170   return ...
    171 end
    172 
    173 
    174 function lume.remove(t, x)
    175   local iter = getiter(t)
    176   for i, v in iter(t) do
    177     if v == x then
    178       if lume.isarray(t) then
    179         table.remove(t, i)
    180         break
    181       else
    182         t[i] = nil
    183         break
    184       end
    185     end
    186   end
    187   return x
    188 end
    189 
    190 
    191 function lume.clear(t)
    192   local iter = getiter(t)
    193   for k in iter(t) do
    194     t[k] = nil
    195   end
    196   return t
    197 end
    198 
    199 
    200 function lume.extend(t, ...)
    201   for i = 1, select("#", ...) do
    202     local x = select(i, ...)
    203     if x then
    204       for k, v in pairs(x) do
    205         t[k] = v
    206       end
    207     end
    208   end
    209   return t
    210 end
    211 
    212 
    213 function lume.shuffle(t)
    214   local rtn = {}
    215   for i = 1, #t do
    216     local r = math.random(i)
    217     if r ~= i then
    218       rtn[i] = rtn[r]
    219     end
    220     rtn[r] = t[i]
    221   end
    222   return rtn
    223 end
    224 
    225 
    226 function lume.sort(t, comp)
    227   local rtn = lume.clone(t)
    228   if comp then
    229     if type(comp) == "string" then
    230       table.sort(rtn, function(a, b) return a[comp] < b[comp] end)
    231     else
    232       table.sort(rtn, comp)
    233     end
    234   else
    235     table.sort(rtn)
    236   end
    237   return rtn
    238 end
    239 
    240 
    241 function lume.array(...)
    242   local t = {}
    243   for x in ... do t[#t + 1] = x end
    244   return t
    245 end
    246 
    247 
    248 function lume.each(t, fn, ...)
    249   local iter = getiter(t)
    250   if type(fn) == "string" then
    251     for _, v in iter(t) do v[fn](v, ...) end
    252   else
    253     for _, v in iter(t) do fn(v, ...) end
    254   end
    255   return t
    256 end
    257 
    258 
    259 function lume.map(t, fn)
    260   fn = iteratee(fn)
    261   local iter = getiter(t)
    262   local rtn = {}
    263   for k, v in iter(t) do rtn[k] = fn(v) end
    264   return rtn
    265 end
    266 
    267 
    268 function lume.all(t, fn)
    269   fn = iteratee(fn)
    270   local iter = getiter(t)
    271   for _, v in iter(t) do
    272     if not fn(v) then return false end
    273   end
    274   return true
    275 end
    276 
    277 
    278 function lume.any(t, fn)
    279   fn = iteratee(fn)
    280   local iter = getiter(t)
    281   for _, v in iter(t) do
    282     if fn(v) then return true end
    283   end
    284   return false
    285 end
    286 
    287 
    288 function lume.reduce(t, fn, first)
    289   local started = first ~= nil
    290   local acc = first
    291   local iter = getiter(t)
    292   for _, v in iter(t) do
    293     if started then
    294       acc = fn(acc, v)
    295     else
    296       acc = v
    297       started = true
    298     end
    299   end
    300   assert(started, "reduce of an empty table with no first value")
    301   return acc
    302 end
    303 
    304 
    305 function lume.unique(t)
    306   local rtn = {}
    307   for k in pairs(lume.invert(t)) do
    308     rtn[#rtn + 1] = k
    309   end
    310   return rtn
    311 end
    312 
    313 
    314 function lume.filter(t, fn, retainkeys)
    315   fn = iteratee(fn)
    316   local iter = getiter(t)
    317   local rtn = {}
    318   if retainkeys then
    319     for k, v in iter(t) do
    320       if fn(v) then rtn[k] = v end
    321     end
    322   else
    323     for _, v in iter(t) do
    324       if fn(v) then rtn[#rtn + 1] = v end
    325     end
    326   end
    327   return rtn
    328 end
    329 
    330 
    331 function lume.reject(t, fn, retainkeys)
    332   fn = iteratee(fn)
    333   local iter = getiter(t)
    334   local rtn = {}
    335   if retainkeys then
    336     for k, v in iter(t) do
    337       if not fn(v) then rtn[k] = v end
    338     end
    339   else
    340     for _, v in iter(t) do
    341       if not fn(v) then rtn[#rtn + 1] = v end
    342     end
    343   end
    344   return rtn
    345 end
    346 
    347 
    348 function lume.merge(...)
    349   local rtn = {}
    350   for i = 1, select("#", ...) do
    351     local t = select(i, ...)
    352     local iter = getiter(t)
    353     for k, v in iter(t) do
    354       rtn[k] = v
    355     end
    356   end
    357   return rtn
    358 end
    359 
    360 
    361 function lume.concat(...)
    362   local rtn = {}
    363   for i = 1, select("#", ...) do
    364     local t = select(i, ...)
    365     if t ~= nil then
    366       local iter = getiter(t)
    367       for _, v in iter(t) do
    368         rtn[#rtn + 1] = v
    369       end
    370     end
    371   end
    372   return rtn
    373 end
    374 
    375 
    376 function lume.find(t, value)
    377   local iter = getiter(t)
    378   for k, v in iter(t) do
    379     if v == value then return k end
    380   end
    381   return nil
    382 end
    383 
    384 
    385 function lume.match(t, fn)
    386   fn = iteratee(fn)
    387   local iter = getiter(t)
    388   for k, v in iter(t) do
    389     if fn(v) then return v, k end
    390   end
    391   return nil
    392 end
    393 
    394 
    395 function lume.count(t, fn)
    396   local count = 0
    397   local iter = getiter(t)
    398   if fn then
    399     fn = iteratee(fn)
    400     for _, v in iter(t) do
    401       if fn(v) then count = count + 1 end
    402     end
    403   else
    404     if lume.isarray(t) then
    405       return #t
    406     end
    407     for _ in iter(t) do count = count + 1 end
    408   end
    409   return count
    410 end
    411 
    412 
    413 function lume.slice(t, i, j)
    414   i = i and absindex(#t, i) or 1
    415   j = j and absindex(#t, j) or #t
    416   local rtn = {}
    417   for x = i < 1 and 1 or i, j > #t and #t or j do
    418     rtn[#rtn + 1] = t[x]
    419   end
    420   return rtn
    421 end
    422 
    423 
    424 function lume.first(t, n)
    425   if not n then return t[1] end
    426   return lume.slice(t, 1, n)
    427 end
    428 
    429 
    430 function lume.last(t, n)
    431   if not n then return t[#t] end
    432   return lume.slice(t, -n, -1)
    433 end
    434 
    435 
    436 function lume.invert(t)
    437   local rtn = {}
    438   for k, v in pairs(t) do rtn[v] = k end
    439   return rtn
    440 end
    441 
    442 
    443 function lume.pick(t, ...)
    444   local rtn = {}
    445   for i = 1, select("#", ...) do
    446     local k = select(i, ...)
    447     rtn[k] = t[k]
    448   end
    449   return rtn
    450 end
    451 
    452 
    453 function lume.keys(t)
    454   local rtn = {}
    455   local iter = getiter(t)
    456   for k in iter(t) do rtn[#rtn + 1] = k end
    457   return rtn
    458 end
    459 
    460 
    461 function lume.clone(t)
    462   local rtn = {}
    463   for k, v in pairs(t) do rtn[k] = v end
    464   return rtn
    465 end
    466 
    467 
    468 function lume.fn(fn, ...)
    469   assert(iscallable(fn), "expected a function as the first argument")
    470   local args = { ... }
    471   return function(...)
    472     local a = lume.concat(args, { ... })
    473     return fn(unpack(a))
    474   end
    475 end
    476 
    477 
    478 function lume.once(fn, ...)
    479   local f = lume.fn(fn, ...)
    480   local done = false
    481   return function(...)
    482     if done then return end
    483     done = true
    484     return f(...)
    485   end
    486 end
    487 
    488 
    489 local memoize_fnkey = {}
    490 local memoize_nil = {}
    491 
    492 function lume.memoize(fn)
    493   local cache = {}
    494   return function(...)
    495     local c = cache
    496     for i = 1, select("#", ...) do
    497       local a = select(i, ...) or memoize_nil
    498       c[a] = c[a] or {}
    499       c = c[a]
    500     end
    501     c[memoize_fnkey] = c[memoize_fnkey] or {fn(...)}
    502     return unpack(c[memoize_fnkey])
    503   end
    504 end
    505 
    506 
    507 function lume.combine(...)
    508   local n = select('#', ...)
    509   if n == 0 then return noop end
    510   if n == 1 then
    511     local fn = select(1, ...)
    512     if not fn then return noop end
    513     assert(iscallable(fn), "expected a function or nil")
    514     return fn
    515   end
    516   local funcs = {}
    517   for i = 1, n do
    518     local fn = select(i, ...)
    519     if fn ~= nil then
    520       assert(iscallable(fn), "expected a function or nil")
    521       funcs[#funcs + 1] = fn
    522     end
    523   end
    524   return function(...)
    525     for _, f in ipairs(funcs) do f(...) end
    526   end
    527 end
    528 
    529 
    530 function lume.call(fn, ...)
    531   if fn then
    532     return fn(...)
    533   end
    534 end
    535 
    536 
    537 function lume.time(fn, ...)
    538   local start = os.clock()
    539   local rtn = {fn(...)}
    540   return (os.clock() - start), unpack(rtn)
    541 end
    542 
    543 
    544 local lambda_cache = {}
    545 
    546 function lume.lambda(str)
    547   if not lambda_cache[str] then
    548     local args, body = str:match([[^([%w,_ ]-)%->(.-)$]])
    549     assert(args and body, "bad string lambda")
    550     local s = "return function(" .. args .. ")\nreturn " .. body .. "\nend"
    551     lambda_cache[str] = lume.dostring(s)
    552   end
    553   return lambda_cache[str]
    554 end
    555 
    556 
    557 local serialize
    558 
    559 local serialize_map = {
    560   [ "boolean" ] = tostring,
    561   [ "nil"     ] = tostring,
    562   [ "string"  ] = function(v) return string.format("%q", v) end,
    563   [ "number"  ] = function(v)
    564     if      v ~=  v     then return  "0/0"      --  nan
    565     elseif  v ==  1 / 0 then return  "1/0"      --  inf
    566     elseif  v == -1 / 0 then return "-1/0" end  -- -inf
    567     return tostring(v)
    568   end,
    569   [ "table"   ] = function(t, stk)
    570     stk = stk or {}
    571     if stk[t] then error("circular reference") end
    572     local rtn = {}
    573     stk[t] = true
    574     for k, v in pairs(t) do
    575       rtn[#rtn + 1] = "[" .. serialize(k, stk) .. "]=" .. serialize(v, stk)
    576     end
    577     stk[t] = nil
    578     return "{" .. table.concat(rtn, ",") .. "}"
    579   end
    580 }
    581 
    582 setmetatable(serialize_map, {
    583   __index = function(_, k) error("unsupported serialize type: " .. k) end
    584 })
    585 
    586 serialize = function(x, stk)
    587   return serialize_map[type(x)](x, stk)
    588 end
    589 
    590 function lume.serialize(x)
    591   return serialize(x)
    592 end
    593 
    594 
    595 function lume.deserialize(str)
    596   return lume.dostring("return " .. str)
    597 end
    598 
    599 
    600 function lume.split(str, sep)
    601   if not sep then
    602     return lume.array(str:gmatch("([%S]+)"))
    603   else
    604     assert(sep ~= "", "empty separator")
    605     local psep = patternescape(sep)
    606     return lume.array((str..sep):gmatch("(.-)("..psep..")"))
    607   end
    608 end
    609 
    610 
    611 function lume.trim(str, chars)
    612   if not chars then return str:match("^[%s]*(.-)[%s]*$") end
    613   chars = patternescape(chars)
    614   return str:match("^[" .. chars .. "]*(.-)[" .. chars .. "]*$")
    615 end
    616 
    617 
    618 function lume.wordwrap(str, limit)
    619   limit = limit or 72
    620   local check
    621   if type(limit) == "number" then
    622     check = function(s) return #s >= limit end
    623   else
    624     check = limit
    625   end
    626   local rtn = {}
    627   local line = ""
    628   for word, spaces in str:gmatch("(%S+)(%s*)") do
    629     local s = line .. word
    630     if check(s) then
    631       table.insert(rtn, line .. "\n")
    632       line = word
    633     else
    634       line = s
    635     end
    636     for c in spaces:gmatch(".") do
    637       if c == "\n" then
    638         table.insert(rtn, line .. "\n")
    639         line = ""
    640       else
    641         line = line .. c
    642       end
    643     end
    644   end
    645   table.insert(rtn, line)
    646   return table.concat(rtn)
    647 end
    648 
    649 
    650 function lume.format(str, vars)
    651   if not vars then return str end
    652   local f = function(x)
    653     return tostring(vars[x] or vars[tonumber(x)] or "{" .. x .. "}")
    654   end
    655   return (str:gsub("{(.-)}", f))
    656 end
    657 
    658 
    659 function lume.trace(...)
    660   local info = debug.getinfo(2, "Sl")
    661   local t = { info.short_src .. ":" .. info.currentline .. ":" }
    662   for i = 1, select("#", ...) do
    663     local x = select(i, ...)
    664     if type(x) == "number" then
    665       x = string.format("%g", lume.round(x, .01))
    666     end
    667     t[#t + 1] = tostring(x)
    668   end
    669   print(table.concat(t, " "))
    670 end
    671 
    672 
    673 function lume.dostring(str)
    674   return assert((loadstring or load)(str))()
    675 end
    676 
    677 
    678 function lume.uuid()
    679   local fn = function(x)
    680     local r = math.random(16) - 1
    681     r = (x == "x") and (r + 1) or (r % 4) + 9
    682     return ("0123456789abcdef"):sub(r, r)
    683   end
    684   return (("xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"):gsub("[xy]", fn))
    685 end
    686 
    687 
    688 function lume.hotswap(modname)
    689   local oldglobal = lume.clone(_G)
    690   local updated = {}
    691   local function update(old, new)
    692     if updated[old] then return end
    693     updated[old] = true
    694     local oldmt, newmt = getmetatable(old), getmetatable(new)
    695     if oldmt and newmt then update(oldmt, newmt) end
    696     for k, v in pairs(new) do
    697       if type(v) == "table" then update(old[k], v) else old[k] = v end
    698     end
    699   end
    700   local err = nil
    701   local function onerror(e)
    702     for k in pairs(_G) do _G[k] = oldglobal[k] end
    703     err = lume.trim(e)
    704   end
    705   local ok, oldmod = pcall(require, modname)
    706   oldmod = ok and oldmod or nil
    707   xpcall(function()
    708     package.loaded[modname] = nil
    709     local newmod = require(modname)
    710     if type(oldmod) == "table" then update(oldmod, newmod) end
    711     for k, v in pairs(oldglobal) do
    712       if v ~= _G[k] and type(v) == "table" then
    713         update(v, _G[k])
    714         _G[k] = v
    715       end
    716     end
    717   end, onerror)
    718   package.loaded[modname] = oldmod
    719   if err then return nil, err end
    720   return oldmod
    721 end
    722 
    723 
    724 local ripairs_iter = function(t, i)
    725   i = i - 1
    726   local v = t[i]
    727   if v ~= nil then
    728     return i, v
    729   end
    730 end
    731 
    732 function lume.ripairs(t)
    733   return ripairs_iter, t, (#t + 1)
    734 end
    735 
    736 
    737 function lume.color(str, mul)
    738   mul = mul or 1
    739   local r, g, b, a
    740   r, g, b = str:match("#(%x%x)(%x%x)(%x%x)")
    741   if r then
    742     r = tonumber(r, 16) / 0xff
    743     g = tonumber(g, 16) / 0xff
    744     b = tonumber(b, 16) / 0xff
    745     a = 1
    746   elseif str:match("rgba?%s*%([%d%s%.,]+%)") then
    747     local f = str:gmatch("[%d.]+")
    748     r = (f() or 0) / 0xff
    749     g = (f() or 0) / 0xff
    750     b = (f() or 0) / 0xff
    751     a = f() or 1
    752   else
    753     error(("bad color string '%s'"):format(str))
    754   end
    755   return r * mul, g * mul, b * mul, a * mul
    756 end
    757 
    758 
    759 local chain_mt = {}
    760 chain_mt.__index = lume.map(lume.filter(lume, iscallable, true),
    761   function(fn)
    762     return function(self, ...)
    763       self._value = fn(self._value, ...)
    764       return self
    765     end
    766   end)
    767 chain_mt.__index.result = function(x) return x._value end
    768 
    769 function lume.chain(value)
    770   return setmetatable({ _value = value }, chain_mt)
    771 end
    772 
    773 setmetatable(lume,  {
    774   __call = function(_, ...)
    775     return lume.chain(...)
    776   end
    777 })
    778 
    779 
    780 return lume