local debug = require "debug"
local error = error;
local getmetatable = getmetatable;
local rawset = rawset;
local rawget = rawget;
local setmetatable = setmetatable;
local type = type;
local getinfo = debug.getinfo;
_ENV = {};
local function what ()
  local d = getinfo(3, "S");
  return d and d.what or "C";
end
local function strict (env)
  local mt = getmetatable(env) or setmetatable(env, {}) and getmetatable(env);
  local _newindex, _index = mt.__newindex, mt.__index;
  mt.__declared = {};
  function mt.__newindex (t, n, v)
    if type(_newindex) == "function" then
      _newindex(t, n, v); -- hook it
    end
    if not mt.__declared[n] then
      local w = what();
      if w ~= "main" and w ~= "C" then
        error("assign to undeclared variable '"..n.."'", 2);
      end
      mt.__declared[n] = true;
    end
    rawset(t, n, v);
  end
  function mt.__index (t, n)
    if type(_index) == "function" then
      local v = _index(t, n); -- hook it
      if v ~= nil then return v end
    elseif _index ~= nil then
      local v = _index[n];
      if v ~= nil then return v end
    end
    if not mt.__declared[n] and what() ~= "C" then
      error("variable '"..n.."' is not declared", 2);
    end
    return rawget(t, n);
  end
  return env;
end
return strict;
