lua-users home
lua-l archive

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


Mark Hamburg wrote:
On Jan 25, 2010, at 4:02 AM, Richard Hundt wrote:

David Burgess wrote:
On 25/1/2010 12:28 PM, Mark Hamburg wrote:
   function pushenv( newenv )
       local oldenv = getfenv( 1 ) -- However one gains access to it...
POUNCE.  Methinks that the indoend thing would aided by a currentenv() function...
here's a hackish version:

function currentenv()
  local getlocal = debug.getlocal
  local e = nil
  local x = 0
  while true do
     x = x + 1
     local n,v = getlocal(2, x)
     if not n then break end
     if n == "(environment)" then
        e = v
     end
  end
  return e
end

The need to scan for a specially named local demonstrates just how hard it is to implement and how potentially expensive it is to execute a function to get the current environment in the presence of the in-do-end construct.

Sure.

So we can take your idea just one step further and decorate the proxy with a currentenv() closure, a closure for pushenv() to make sure they're chained, and bootstrap it:

local function _pushenv(inner, outer)
   local proxy = { }
   setmetatable(proxy, {
      __index = function(_, k)
         local v = inner[k]
         return v ~= nil and v or outer[k]
      end;
      __newindex = inner
   })
   rawset(proxy, "currentenv", function() return inner end)
   rawset(proxy, "pushenv", function(env) return _pushenv(env, proxy) end)
   return proxy
end

_G.pushenv = function(env) return _pushenv(env, _G) end
_G.currentenv = function() return _G end

local env = { answer = 42 }
in pushenv(env) do
   print(answer) --> 42
   in pushenv({ question = "The number of petunias" }) do
      print(question, answer) --> The number of ... 42
   end
   print(question) --> nil
end
print(answer) --> nil


-Rich