Package System

lua-users home
wiki

Showing revision 3
This is an embryonic package system for Lua 5. Its main function is
   use "packagename" {options}
(where {options} is optional ;-) That call is similar to a require, except that

Currently, the current option function handles only the import option. import="*" means to declare all global names of the package into the global space of the importing package; import={"name1", "name2", ...} imports only the selected names.

It also defines a declare ("name1", "name2", ...) function, that turns on enforcing declaration of names, and also declares the given names. Any access to an undefined/undeclared global raises an error.

--
-- auxiliar error function
--

local function error (level, fmt, ...)
  _G.error(string.format(fmt, unpack(arg)), level+1)
end


--
-- this package cannot use the package system (itself!), so use old
-- package tricks (but most of its functions are global anyway...)
--

_G.Package = {}

local function loadfrompath (packname)
  LUA_PATH = LUA_PATH or os.getenv"LUA_PATH" or "?.lua;?"
  for k in string.gfind(LUA_PATH, "[^;]+") do
    local fname = string.gsub(k, "?", packname)
    local f, err = loadfile(fname)
    if f then return f end
    if not string.find(err, "^cannot read") then
      error(err)
    end
  end
  error(3, "cannot find package `%s' in path `%s'", packname, LUA_PATH)
end


--
-- Metatable for Global tables
-- Inherit absent fields from main global
--

local global_mt = {
  __index = function (t,n)
              local val = _G[n]   -- get value from main global
              rawset(t, n, val)   -- save it for next time
              return val
            end,
}


--
-- Alternative metatable, that enforces declarations
--

local Predefined = {}      -- table for predefined variables
setmode(Predefined, "k")

local req_global_mt = {
  __index = function (t,n)
               local val = global_mt.__index(t, n)
               if val then return val end
               if not Predefined[t][n] then
                 error(2, "attempt to read undeclared variable `%s'", n)
               end
               return nil
             end,

   __newindex = function (t,n, val)
                 if not Predefined[t][n] then
                   error(2, "attempt to write to undeclared variable `%s'", n)
                 end
                 rawset(t, n, val)
               end,
}


--
-- Declare variables (and turn on declaration enforcing)
--

function _G.declare (...)
  local predec = Predefined[getglobals(2)]
  if predec == nil then   -- package didn't enforce declarations
    local g = getglobals(2)  -- get package global table
    setmetatable(g, req_global_mt)
    predec = {}
    Predefined[g] = predec
  end
  for _, name in ipairs(arg) do
    predec[name] = true
  end
end


--
-- Default function to handle `use' options
-- (where `oldpack' is using `newpack')
--

function Package.defaultoptions (oldpack, newpack, options)
  for k, v in pairs(options) do
    if k == "version" then
      -- ???
    elseif k == "import" then
      if v == "*" then   -- import all?
        for k,v in pairs(newpack) do
          -- do not import names starting with `_'
          if not string.find(k, "^_") then oldpack[k] = v end
        end
      elseif type(v) == "table" then  -- import list?
        for _,n in ipairs(v) do oldpack[n] = newpack[n] end
      else error(3, "invalid value for `import' option")
      end
    else error(3, "invalid option `"..k.."'")
    end
  end
end


--
-- Import a package, initialize it, and install it in current package
--

function _G.use (packname)
  local g = _G[packname]
  if not g then
    local f = loadfrompath(packname)
    g = {_name = packname}   -- new global table
    g._self = g
    _G[packname] = g
    setmetatable(g, global_mt)
    setglobals(f, g)  -- change global table of calling function
    f()   -- run main
  end
  local init = rawget(g, "_init")
  if init then init(getglobals(2)) end
  return function (options)
    (rawget(g, "_options") or Package.defaultoptions)(getglobals(2), g, options)
  end
end


RecentChanges · preferences
edit · history · current revision
Edited August 22, 2002 9:33 pm GMT (diff)