Last active
April 29, 2022 00:08
-
-
Save alexshpilkin/23d3e0a8a6e6e6510874e99ffe5fc079 to your computer and use it in GitHub Desktop.
Condition handling in Lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local M = {} | |
local error, unpack = error, unpack or table.unpack | |
local running = coroutine.running | |
local stderr = io.stderr | |
local exit = os.exit | |
local insert, remove = table.insert, table.remove | |
-- conditions | |
local handlers = setmetatable({}, { | |
__mode = 'k', -- do not retain dead coroutines | |
__index = function (self, key) -- no handlers by default | |
self[key] = {}; return self[key] | |
end, | |
}) | |
local function removing(xs, x, ok, ...) | |
assert(remove(xs) == x) | |
if ok then return ... else error(...) end | |
end | |
-- establish a handler during call | |
function M.hcall(h, f, ...) | |
local hs = handlers[running()] | |
insert(hs, h) | |
return removing(hs, h, pcall(f, ...)) | |
end | |
-- signal the given condition to currently active handlers | |
function M.signal(...) | |
local hs = handlers[running()] | |
for i = #hs, 1, -1 do hs[i](...) end | |
end | |
local signal = M.signal | |
function M.error(...) | |
signal(...) | |
stderr:write("error: " .. tostring(...) .. "\n") | |
exit(1) | |
end | |
function M.warn(...) | |
signal(...) | |
stderr:write("warning: " .. tostring(...) .. "\n") | |
end | |
-- restarts | |
-- invoke the given restart | |
function M.restart(r, ...) | |
local n = select('#', ...); r.n = n | |
for i = 1, n do r[i] = select(i, ...) end | |
error(r) | |
end | |
local function continue(r, ok, ...) | |
if ok then return ok, ... end | |
if ... == r then return false, unpack(r, 1, r.n) end | |
error(...) | |
end | |
-- establish a restart during call | |
function M.rcall(f, ...) | |
local r = {} | |
return continue(r, pcall(f, r, ...)) | |
end | |
return M |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local cond = require 'cond' | |
-- common condition types (XXX should use proper dynamic variables instead) | |
local retry, use = nil, nil | |
-- I/O library | |
local function _gets() | |
if math.random() < 0.5 then cond.error 'lossage' end | |
return 'user input' | |
end | |
local function gets() | |
local ok, value = cond.rcall(function (_use) | |
use = _use | |
local ok, value | |
repeat ok, value = cond.rcall(function (_retry) | |
retry = _retry | |
return _gets() | |
end) until ok | |
return value | |
end) | |
-- ok or not, we got a value either way | |
return value | |
end | |
-- application (knows nothing about errors) | |
local function app() | |
for i = 1, 5 do print(string.format("got: %q", gets())) end | |
return "success" | |
end | |
-- shell | |
local ok, value = cond.rcall(function (abort) | |
return cond.hcall(function (err) | |
io.stderr:write("I/O error: " .. err .. "\n") | |
while true do | |
io.stderr:write("[a]bort, [r]etry, [u]se value? ") | |
local answer = io.read('*l') | |
if answer == 'a' then cond.restart(abort, "aborted") end | |
if answer == 'r' then cond.restart(retry) end | |
if answer == 'u' then | |
io.stderr:write("value? ") | |
cond.restart(use, io.read('*l')) | |
end | |
end | |
end, app) | |
end) | |
print(ok, value) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment