包系统 |
|
这是一个用于 Lua 5 的早期包系统。它的主要功能是
use "packagename" {options}
{options}
是可选的 ;-) 该调用类似于 require
,不同之处在于
_init
函数,则会运行该函数;
_options
函数或默认选项函数来处理选项。
目前,当前选项函数仅处理 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