246 lines
8.7 KiB
Lua
246 lines
8.7 KiB
Lua
|
|
||
|
-- Accesses the PotatOS Potatocloud(tm) Potatostore(tm). Used to implement Superglobals(tm) - like globals but on all computers.
|
||
|
-- To be honest I should swap this out for a self-hosted thing like Kinto.
|
||
|
--[[
|
||
|
Fix for PS#4F329133
|
||
|
JSONBin (https://jsonbin.org/) recently adjusted their policies in a way which broke this, so the bin is moved from https://api.jsonbin.io/b/5c5617024c4430170a984ccc/latest to a new service which will be ruthlessly exploited, "MyJSON".
|
||
|
|
||
|
Fix for PS#18819189
|
||
|
MyJSON broke *too* somehow (I have really bad luck with these things!) so move from https://api.myjson.com/bins/150r92 to "JSONBin".
|
||
|
|
||
|
Fix for PS#8C4CB942
|
||
|
The other JSONBin thing broke too so just implement it in RSAPI
|
||
|
]]
|
||
|
|
||
|
return function(add_log, report_incident)
|
||
|
function fetch(u, ...)
|
||
|
if not http then error "No HTTP access" end
|
||
|
local h,e = http.get(u, ...)
|
||
|
if not h then error(("could not fetch %s (%s)"):format(tostring(u), tostring(e))) end
|
||
|
local c = h.readAll()
|
||
|
h.close()
|
||
|
return c
|
||
|
end
|
||
|
|
||
|
local bin_URL = "https://r.osmarks.net/superglobals/"
|
||
|
local bin = {}
|
||
|
local localbin = {}
|
||
|
|
||
|
function bin.get(k)
|
||
|
if localbin[k] then
|
||
|
return localbin[k]
|
||
|
else
|
||
|
local ok, err = pcall(function()
|
||
|
local r = fetch(bin_URL .. textutils.urlEncode(tostring(k)), nil, true)
|
||
|
local ok, err = pcall(json.decode, r)
|
||
|
if not ok then return r end
|
||
|
return err
|
||
|
end)
|
||
|
if not ok then add_log("superglobals fetch failed %s", tostring(err)) return nil end
|
||
|
return err
|
||
|
end
|
||
|
end
|
||
|
|
||
|
function bin.set(k, v)
|
||
|
local ok, err = pcall(function()
|
||
|
b[k] = v
|
||
|
local h, err = http.post(bin_URL .. textutils.urlEncode(tostring(k)), json.encode(v), nil, true)
|
||
|
if not h then error(err) end
|
||
|
end)
|
||
|
if not ok then localbin[k] = v add_log("superglobals set failed %s", tostring(err)) end
|
||
|
end
|
||
|
|
||
|
local bin_mt = {
|
||
|
__index = function(_, k) return bin.get(k) end,
|
||
|
__newindex = function(_, k, v) return bin.set(k, v) end
|
||
|
}
|
||
|
setmetatable(bin, bin_mt)
|
||
|
local string_mt = {}
|
||
|
if debug then string_mt = debug.getmetatable "" end
|
||
|
|
||
|
local function define_operation(mt, name, fn)
|
||
|
mt[name] = function(a, b)
|
||
|
if getmetatable(a) == mt then return fn(a, b)
|
||
|
else return fn(b, a) end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local frac_mt = {}
|
||
|
function frac_mt.__tostring(x)
|
||
|
return ("[Fraction] %s/%s"):format(textutils.serialise(x.numerator), textutils.serialise(x.denominator))
|
||
|
end
|
||
|
define_operation(frac_mt, "__mul", function (a, b)
|
||
|
return (a.numerator * b) / a.denominator
|
||
|
end)
|
||
|
|
||
|
-- Add exciting random stuff to the string metatable.
|
||
|
-- Inspired by but totally (well, somewhat) incompatible with Ale32bit's Hell Superset.
|
||
|
function string_mt.__index(s, k)
|
||
|
if type(k) == "number" then
|
||
|
local c = string.sub(s, k, k)
|
||
|
if c == "" then return nil else return c end
|
||
|
end
|
||
|
return _ENV.string[k] or bin.get(k)
|
||
|
end
|
||
|
function string_mt.__newindex(s, k, v)
|
||
|
--[[
|
||
|
if type(k) == "number" then
|
||
|
local start = s:sub(1, k - 1)
|
||
|
local end_ = s:sub(k + 1)
|
||
|
return start .. v .. end_
|
||
|
end
|
||
|
]]
|
||
|
return bin.set(k, v)
|
||
|
end
|
||
|
function string_mt.__add(lhs, rhs)
|
||
|
return tostring(lhs) .. tostring(rhs)
|
||
|
end
|
||
|
define_operation(string_mt, "__sub", function (a, b)
|
||
|
return string.gsub(a, b, "")
|
||
|
end)
|
||
|
function string_mt.__unm(a)
|
||
|
return string.reverse(a)
|
||
|
end
|
||
|
-- http://lua-users.org/wiki/SplitJoin
|
||
|
function string.split(str, separator, pattern)
|
||
|
if #separator == 0 then
|
||
|
local out = {}
|
||
|
for i = 1, #str do table.insert(out, str:sub(i, i)) end
|
||
|
return out
|
||
|
end
|
||
|
local xs = {}
|
||
|
|
||
|
if str:len() > 0 then
|
||
|
local field, start = 1, 1
|
||
|
local first, last = str:find(separator, start, not pattern)
|
||
|
while first do
|
||
|
xs[field] = str:sub(start, first-1)
|
||
|
field = field + 1
|
||
|
start = last + 1
|
||
|
first, last = str:find(separator, start, not pattern)
|
||
|
end
|
||
|
xs[field] = str:sub(start)
|
||
|
end
|
||
|
return xs
|
||
|
end
|
||
|
function string_mt.__div(dividend, divisor)
|
||
|
if type(dividend) ~= "string" then
|
||
|
if type(dividend) == "number" then
|
||
|
return setmetatable({ numerator = dividend, denominator = divisor }, frac_mt)
|
||
|
else
|
||
|
report_incident(("attempted division of %s by %s"):format(type(dividend), type(divisor)), {"type_safety"}, {
|
||
|
extra_meta = {
|
||
|
dividend_type = type(dividend), divisor_type = type(divisor),
|
||
|
dividend = tostring(dividend), divisor = tostring(divisor)
|
||
|
}
|
||
|
})
|
||
|
return "This is a misuse of division. This incident has been reported."
|
||
|
end
|
||
|
end
|
||
|
if type(divisor) == "string" then return string.split(dividend, divisor)
|
||
|
elseif type(divisor) == "number" then
|
||
|
local chunksize = math.ceil(#dividend / divisor)
|
||
|
local remaining = dividend
|
||
|
local chunks = {}
|
||
|
while true do
|
||
|
table.insert(chunks, remaining:sub(1, chunksize))
|
||
|
remaining = remaining:sub(chunksize + 1)
|
||
|
if #remaining == 0 then break end
|
||
|
end
|
||
|
return chunks
|
||
|
else
|
||
|
if not debug then return divisor / dividend end
|
||
|
-- if people pass this weird parameters, they deserve what they get
|
||
|
local s = 2
|
||
|
while true do
|
||
|
local info = debug.getinfo(s)
|
||
|
if not info then return -dividend / "" end
|
||
|
if info.short_src ~= "[C]" then
|
||
|
local ok, res = pcall(string.dump, info.func)
|
||
|
if ok then
|
||
|
return res / s
|
||
|
end
|
||
|
end
|
||
|
s = s + 1
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
local cache = {}
|
||
|
function string_mt.__call(s, ...)
|
||
|
if cache[s] then return cache[s](...)
|
||
|
else
|
||
|
local f, err = load(s)
|
||
|
if err then error(err) end
|
||
|
cache[s] = f
|
||
|
return f(...)
|
||
|
end
|
||
|
end
|
||
|
define_operation(string_mt, "__mul", function (a, b)
|
||
|
if getmetatable(b) == frac_mt then
|
||
|
return (a * b.numerator) / b.denominator
|
||
|
end
|
||
|
if type(b) == "number" then
|
||
|
return string.rep(a, b)
|
||
|
elseif type(b) == "table" then
|
||
|
local z = {}
|
||
|
for _, v in pairs(b) do
|
||
|
table.insert(z, tostring(v))
|
||
|
end
|
||
|
return table.concat(z, a)
|
||
|
elseif type(b) == "function" then
|
||
|
local out = {}
|
||
|
for i = 1, #a do
|
||
|
table.insert(out, b(a:sub(i, i)))
|
||
|
end
|
||
|
return table.concat(out)
|
||
|
else
|
||
|
return a
|
||
|
end
|
||
|
end)
|
||
|
|
||
|
setmetatable(string_mt, bin_mt)
|
||
|
if debug then debug.setmetatable(nil, bin_mt) end
|
||
|
|
||
|
-- Similar stuff for functions.
|
||
|
local func_funcs = {}
|
||
|
local func_mt = {__index=func_funcs}
|
||
|
if debug then debug.setmetatable(function() end, func_mt) end
|
||
|
function func_mt.__sub(lhs, rhs)
|
||
|
return function(...) return lhs(rhs(...)) end
|
||
|
end
|
||
|
function func_mt.__add(lhs, rhs)
|
||
|
return function(...) return rhs(lhs(...)) end
|
||
|
end
|
||
|
function func_mt.__concat(lhs, rhs)
|
||
|
return function(...)
|
||
|
return lhs(...), rhs(...), nil -- limit to two return values
|
||
|
end
|
||
|
end
|
||
|
function func_mt.__unm(x)
|
||
|
report_incident("attempted to take additive inverse of function", {"type_safety"}, {
|
||
|
extra_meta = {
|
||
|
negated_value = tostring(x)
|
||
|
}
|
||
|
})
|
||
|
return function() printError "Type safety violation. This incident has been reported." end
|
||
|
end
|
||
|
function func_funcs.dump(x) return string.dump(x) end
|
||
|
function func_funcs.info(x) return debug.getinfo(x) end
|
||
|
function func_funcs.address(x) return (string.match(tostring(x), "%w+$")) end
|
||
|
|
||
|
-- Similar stuff for numbers too! NOBODY CAN ESCAPE!
|
||
|
-- TODO: implement alternative mathematics.
|
||
|
local num_funcs = {}
|
||
|
local num_mt = {__index=num_funcs}
|
||
|
num_mt.__call = function(x, ...)
|
||
|
local out = x
|
||
|
for _, y in pairs {...} do
|
||
|
out = out + y
|
||
|
end
|
||
|
return out
|
||
|
end
|
||
|
if debug then debug.setmetatable(0, num_mt) end
|
||
|
function num_funcs.tostring(x) return tostring(x) end
|
||
|
function num_funcs.isNaN(x) return x ~= x end
|
||
|
function num_funcs.isInf(x) return math.abs(x) == math.huge end
|
||
|
end
|