lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


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
>