包系统

lua-users home
wiki

版本通知: 此页面已过时。它是一个在 Lua 5.0 之上实现的模块系统的初始提案,而 Lua 5.0 缺乏标准的模块系统。标准模块系统正式集成到 Lua 5.1 中,该模块系统也通过 [LuaCompat] 反向移植到 Lua 5.0。我不确定此页面是否还有任何剩余的教育价值。如果它确实具有剩余价值,则可能需要根据 5.1 模块工作进行描述或重新编写。

这是一个用于 Lua 5 的早期包系统。它的主要功能是

use "packagename" {options}
(其中 {options} 是可选的 ;-) 该调用类似于 require,不同之处在于

目前,当前选项函数仅处理 import 选项。import="*" 表示将包的所有全局名称声明到导入包的全局空间中;import={"name1", "name2", ...} 仅导入选定的名称。

它还定义了一个 declare ("name1", "name2", ...) 函数,该函数打开强制声明名称的功能,并声明给定的名称。对未定义/未声明的全局变量的任何访问都会引发错误。

--
-- 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


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2007 年 1 月 2 日,格林威治标准时间上午 5:36 (差异)