Package System |
|
This is an embryonic package system for Lua 5. Its main function is
use "packagename" {options}
{options}
is optional ;-) That call is similar to
a require
, except that
_init
function, it is run;
_options
function or a default options function is called to handle options.
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