This LTN depends on "loadfile," introduced in Lua 5.0
Lua 4.1 introduced the "require" function that loads and runs a file unless it already loaded. Lua 5.0 offers require as a built-in function in its base lib. The require command together with LTN 7 "Modules & packages" offers a basis for simple module support in Lua. This technical note proposes an improved version of require, dubbed "import." The proposed import scheme avoids direct access to the globals, corrects a globals related security loophole and handles cyclic module dependencies gracefully.
The import function could be implemented with the following Lua 5.0 code.
local imported = {} local function package_stub(name) local stub = {} local stub_meta = { __index = function(_, index) error(string.format("member `%s' is accessed before package `%s' is fully imported", index, name)) end, __newindex = function(_, index, _) error(string.format("member `%s' is assigned a value before package `%s' is fully imported", index, name)) end, } setmetatable(stub, stub_meta) return stub end local function locate(name) local path = LUA_PATH if type(path) ~= "string" then path = os.getenv "LUA_PATH" or "./?.lua" end for path in string.gfind(path, "[^;]+") do path = string.gsub(path, "?", name) local chunk = loadfile(path) if chunk then return chunk, path end end return nil, path end function import(name) local package = imported[name] if package then return package end local chunk, path = locate(name) if not chunk then error(string.format("could not locate package `%s' in `%s'", name, path)) end package = package_stub(name) imported[name] = package setglobals(chunk, getglobals(2)) chunk = chunk() setmetatable(package, nil) if type(chunk) == "function" then chunk(package, name, path) end return package end
Typical use of import is as follows:
-- import the complex package local complex = import "complex" -- complex now holds the public interface local x = 5 + 3*complex.I
A package should be structured as follows:
-- first import all other required packages. local a = import "a" local b = import "b" -- then define the package install function. -- the PIF more or less contains the code of a -- LTN 7 package. local function pif(Public, path) local Private = {} function Public.fun() -- public function end -- etc. end -- return the package install function return pif
Import is almost backward compatible with require. Import will however not define the _REQUIREDNAME global during loading. An "old style" package that does not return a PIF will still be loaded and run but import returns an empty public interface. This will not impact old style code because require has no return values.
Here is an example of two packages mutually importing each other. Because neither one actually uses the other during import, this will not be a problem.
Package "a.lua
":
local b = import "b" local function pif(pub, name, path) function pub.show() -- use a message from package b print("in " .. name .. ": " .. b.message) end pub.message = "this is package " .. name .. " at " .. path end return pif
Package "b.lua
":
local a = import "a" local function pif(pub, name, path) function pub.show() -- use a message from package a print("in " .. name .. ": " .. a.message) end pub.message = "this is package " .. name .. " at " .. path end return pif
And some code importing and running both:
local a = import "a" local b = import "b" a.show() -- prints "in a: this is package b at ./b.lua" b.show() -- prints "in b: this is package a at ./a.lua"