[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: RE: package proposal
- From: "Kevin Baca" <lualist@...>
- Date: Fri, 17 Sep 2004 11:02:46 -0700
This is very similar to something I'm doing - which means I like it very
much.
One thing I'll note - it seems that it's possible to have multiple
modules in a single file simply by calling module() multiple times.
I don't think this is a bad thing, but it violates the requirement of
one module per file. Perhaps that requirement should be removed?
-Kevin
> -----Original Message-----
> From: lua-bounces@bazar2.conectiva.com.br
> [mailto:lua-bounces@bazar2.conectiva.com.br] On Behalf Of
> Roberto Ierusalimschy
> Sent: Friday, September 17, 2004 9:50 AM
> To: Lua discussion list
> Subject: package proposal
>
>
>
> This is a proposal for a more standard package usage in Lua. It
> includes some changes in the `require' function, some changes in the
> `luaL_openlib' function, and a new function `module'. We already got
> previous comments about this proposal from Diego Nehab (LuaSocket),
> Andre Carregal (Kepler) and Tomas Gorham (Kepler).
>
> The following is a description of the new system. At the end of this
> text is an implementation for the functions `require' and `module' in
> Lua, which serves as a more precise specification.
>
>
> * A *package* is a collection of modules distributed together.
> Each *module* lives in single file, which contains a single module.
> (Of course, a package may comprise a single module.)
>
> * Modules can be loaded in (at least) three forms:
> pre-loaded (that is, the module is loaded in the Lua state
> during C initialization code, like the standard libraries);
> pre-linked (the module code is linked with the Lua executable,
> but must be loaded explicitly);
> and dynamically (the module code is found through a search in
> some path, and then linked and loaded).
>
>
> * Given a statement <<require"mod">>,
> require first checks whether it is already loaded,
> looking for a field "mod" in a control table `package.loaded'.
> (So, pre-loaded modules must register themselves in the
> package.loaded table.)
>
> Otherwise, `require' checks for a function in package.preload["mod"];
> if found, it calls that function to load the module.
> (So, pre-linked modules must register their open functions in
> the package.preload table.)
>
> Otherwise, require looks for a file "mod" in the path package.cpath
> (typically it will be something like
> "./?.so;/usr/local/lib/lua/5.0/?.so"
> or "./?.dll;C:windows/?.dll").
> If found, it tries to call function "luaopen_mod" on that
> library to open it.
> Otherwise, it looks for "mod" in the path package.path (old LUA_PATH),
> loads it with "loadfile" and runs it.
>
> * It is expected that a module "mod" will put all its stuff in a
> global table "mod". Moreover, it will arrange for the
> call <<require"mod">> to return that table, so that users can
> write
>
> local xl = require"mod"
>
> and use local xl to access the module.
>
>
> * Module names can be hierarquical (e.g., "socket.smtp").
> A collection of modules with a common root forms a package.
> Such hierarchy affects only module names.
> The fact that two modules A and B are in the same package
> does not create any special relation between A and B.
> A module named "mod.lili" should be in a file "mod/lili" in
> the path. Such module should be loaded in global mod.lili.
> (Like most strings in this proposal,
> the "/" separator can be changed in the luaconfig file.
> A system may choose to use a "_" as separator and therefore keep all
> modules in a flat directory.)
>
>
> * The `module' function provides an easy way to create Lua modules
> that conform to what is expected from a module.
> To write a module, the programmer simply writes
>
> module(...) -- ... is the vararg expression
>
> at the start of the chunk and then declares
> all module stuff as global values.
> The `module' function creates a global with the appropriate name
> if it does not exist and sets it as the metatable of
> the new module.
> It also sets that global as the value of package.loaded[given-name],
> so that the corresponding `require' call will return the module.
> It also arranges for the module to inherit from _G,
> so that it can access (but not change) global values directly.
>
> The `module' function also defines two names in the new namespace:
> _NAME has the module name.
> _PACK has the package name, which a module can use to require its
> own relatives (e.g., <<require _PACK.."mysiblingname">>).
>
>
> * The `luaL_openlib' function will be changed to put the table
> wherein it opens the library in package.loaded[libname].
> Therefore, whatever the way the library was open,
> a later call to `require' will detect that it is already
> open and will return it.
> (The standard libraries are a particular example: you can
> write <<local s = require"string">> to use the string library.)
>
>
> * Because everything is written on top of regular Lua stuff (tables,
> environments, metatables, etc.) it is easy to bend any rule when there
> is a good reason. For instance, if a module exports a single function,
> it can return it directly, instead of putting it into a table; a file
> may export its stuff into someone else's namespace; more complicated C
> modules may be imported through an auxiliary Lua file that calls
> loadlib; etc.
>
>
>
> --------------------------------------------------------------
>
> package = {}
> package.path = LUA_PATH or os.getenv("LUA_PATH") or
> ("./?.lua;" ..
> "/usr/local/share/lua/5.0/?.lua;" ..
> "/usr/local/share/lua/5.0/?/init.lua" )
>
> package.cpath = os.getenv("LUA_CPATH") or
> "./?.so;" ..
> "/usr/local/lib/lua/5.0/?.so;" ..
> "/usr/local/lib/lua/5.0/lib?.so"
>
> package.loaded = {}
>
> package.preload = {}
>
>
> --
> -- looks for a file `name' in given path
> --
> local function search (path, name)
> for c in string.gfind(path, "[^;]+") do
> c = string.gsub(c, "%?", name)
> local f = io.open(c)
> if f then -- file exist?
> f:close()
> return c
> end
> end
> return nil -- file not found
> end
>
>
> --
> -- new require
> --
> function _G.require (name)
> if not package.loaded[name] then
> package.loaded[name] = true
> local f = package.preload[name]
> if not f then
> local filename = string.gsub(name, "%.", "/")
> local fullname = search(package.cpath, filename)
> if fullname then
> local openfunc = "luaopen_" .. string.gsub(name, "%.", "")
> f = assert(loadlib(fullname, openfunc))
> else
> fullname = search(package.path, filename)
> if not fullname then
> error("cannot find "..name.." in path "..package.path, 2)
> end
> f = assert(loadfile(fullname))
> end
> end
> local res = f(name)
> if res then package.loaded[name] = res end
> end
> return package.loaded[name]
> end
>
>
> --
> -- auxiliar function to read "nested globals"
> --
> local function getfield (t, f)
> for w in string.gfind(f, "[%w_]+") do
> if not t then return nil end
> t = t[w]
> end
> return t
> end
>
>
> --
> -- auxiliar function to write "nested globals"
> --
> local function setfield (t, f, v)
> for w in string.gfind(f, "([%w_]+)%.") do
> t[w] = t[w] or {} -- create table if absent
> t = t[w] -- get the table
> end
> local w = string.gsub(f, "[%w_]+%.", "") -- get last field name
> t[w] = v -- do the assignment
> end
>
>
> --
> -- new module function
> --
> function _G.module (name)
> local ns = getfield(_G, name) -- search for namespace
> if not ns then
> ns = {} -- create new namespace
> setmetatable(ns, {__index = _G})
> setfield(_G, name, ns)
> ns._NAME = name
> ns._PACK = string.gsub(name, "[^.]*$", "")
> end
> package.loaded[name] = ns
> setfenv(2, ns)
> end
>
>
> --------------------------------------------------
>
> -- Roberto
>