
local persist = {}

local require = nil
--------------------------------------------------------------------------------

--- Load and run a Lua file in an environment.
-- @param filename string: the name of the file.
-- @param env table: the environment table.
-- @return (true, any) or (nil, string, string): true and the return value
-- of the file, or nil, an error message and an error code ("open", "load"
-- or "run") in case of errors.
local function run_file(filename, env)
   local fd, err = io.open(filename)
   if not fd then
      return nil, err, "open"
   end
   local str, err = fd:read("*a")
   fd:close()
   if not str then
      return nil, err, "open"
   end
   str = str:gsub("^#![^\n]*\n", "")
   local chunk, ran
   if _VERSION == "Lua 5.1" then -- Lua 5.1
      chunk, err = loadstring(str, filename)
      if chunk then
         setfenv(chunk, env)
         ran, err = pcall(chunk)
      end
   else -- Lua 5.2
      chunk, err = load(str, filename, "t", env)
      if chunk then
         ran, err = pcall(chunk)
      end
   end
   if not chunk then
      return nil, "Error loading file: "..err, "load"
   end
   if not ran then
      return nil, "Error running file: "..err, "run"
   end
   return true, err
end

--- Load a Lua file containing assignments, storing them in a table.
-- The global environment is not propagated to the loaded file.
-- @param filename string: the name of the file.
-- @param tbl table or nil: if given, this table is used to store
-- loaded values.
-- @return (table, table) or (nil, string, string): a table with the file's assignments
-- as fields and set of undefined globals accessed in file,
-- or nil, an error message and an error code ("open"; couldn't open the file,
-- "load"; compile-time error, or "run"; run-time error)
-- in case of errors.
function persist.load_into_table(filename, tbl)
   assert(type(filename) == "string")
   assert(type(tbl) == "table" or not tbl)

   local result = tbl or {}
   local globals = {}
   local globals_mt = {
      __index = function(t, k)
         globals[k] = true
      end
   }
   local save_mt = getmetatable(result)
   setmetatable(result, globals_mt)
   
   local ok, err, errcode = run_file(filename, result)
   
   setmetatable(result, save_mt)

   if not ok then
      return nil, err, errcode
   end
   return result, globals
end

return persist

