lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Right, the one reason we need coroutines is to reimplement the standard library. Just as a warning, when asked to produce a license, this message was said:

> <Soni> TsT: if you wanna use it, contact me

So... Here's some code that's pretty useless that you need the author's permission to use.

On Fri, Feb 24, 2017, 09:17 Soni L. <fakedme@gmail.com> wrote:
Original post:
https://gist.github.com/SoniEx2/ab5a2caa5f41347db7b83a9b455d0192/28a20b6ead0a72a655b86646d7947a597e696d07#why-you-need-coroutines-pcall-and-error-without-pcall-and-error
(short link: https://git.io/vyJ2X)

This version is tweaked to be more complete (includes assert and xpcall).

# Why you need coroutines: pcall and error without pcall and error.

So, let's take Lua, remove pcall and error, and tweak coroutines a bit:

```lua
-- NOTE: THIS IMPLEMENTATION IS UNTESTED AND MAY CONTAIN BUGS.

-- get rid of native pcall and error (and stuff)
pcall = nil
error = nil
xpcall = nil
assert = nil

-- we're gonna wrap coroutines, so this is important
local cocreate, coresume, costatus, corunning = coroutine.create,
coroutine.resume, coroutine.status, coroutine.running
local isdead = {}
setmetatable(isdead, {__mode="k"})

-- reimplement coroutine.resume
coroutine.resume = function(...)
   return (function(ok, ...) if ok then return ... else return ok, ...
end end)(coresume(...))
end

-- reimplement coroutine.yield
coroutine.yield = function(...)
   return coyield(true, ...)
end

-- reimplement coroutine.status
coroutine.status = function(co)
   if isdead[co] == true then return "dead" else return costatus(co) end
end

-- reimplement coroutine.wrap
coroutine.wrap = function(f)
   local co = cocreate(f)
   return function(...)
     return (function(...)
       if ... then
         return select(2, ...)
       else
         -- note: using global. will be redefined below.
         error((select(2, ...)))
       end
     end)(coroutine.resume(co)) -- note that we replaced
coroutine.resume above
   end
end

-- reimplement error on top of coroutines
error = function(msg, level)
   local f = isdead[corunning()] -- ew, but eh it works \o/
   isdead[corunning()] = true
   if f then
     if not pcall(f, msg) then -- uses our own pcall
       level = 0
       msg = "error in message handler"
     end
   end
   coyield(false, msg, level or 1) -- uses raw/internal coyield, not the
wrapper we created above
   while true do coyield(false, "attempt to resume a dead coroutine") end
end

-- reimplement pcall on top of (our) xpcall
pcall = function(f, ...)
   return xpcall(f, nil, ...)
end

-- reimplement xpcall on top of coroutines
xpcall = function(f, msgh, ...)
   local co = cocreate(f)
   assert(msgh ~= true, "bad argument #2 (function expected, got
boolean)") -- lol
   isdead[co] = msgh
   local function recurse2(recurse, ...)
     return recurse(coroutine.resume(co, ...))
   end
   local function recurse(ok, ...)
     if coroutine.status(co) == "dead" then
       return ok, ...
     else
       return recurse2(recurse, coroutine.yield(...))
     end
   end
   return recurse2(recurse, ...)
end

-- assert is a simple wrapper around error
assert = function(...)
   local ok, err = ...
   if not ok then error(err or "assertion failed") end
   return ...
end
```

And now we have pcall and error implemented on top of coroutines (a
"high-level" pcall and error).

--
Disclaimer: these emails may be made public at any given time, with or without reason. If you don't agree with this, DO NOT REPLY.


--
--

Ryan <vandor2012@gmail.com>
Software Developer / System Administrator
https://hashbang.sh