* luci: add modified luadoc source to contrib
authorJo-Philipp Wich <jow@openwrt.org>
Wed, 23 Jul 2008 13:27:25 +0000 (13:27 +0000)
committerJo-Philipp Wich <jow@openwrt.org>
Wed, 23 Jul 2008 13:27:25 +0000 (13:27 +0000)
17 files changed:
contrib/luadoc/lua/luadoc/config.lua [new file with mode: 0644]
contrib/luadoc/lua/luadoc/doclet/debug.lua [new file with mode: 0644]
contrib/luadoc/lua/luadoc/doclet/formatter.lua [new file with mode: 0644]
contrib/luadoc/lua/luadoc/doclet/html.lua [new file with mode: 0644]
contrib/luadoc/lua/luadoc/doclet/html/file.lp [new file with mode: 0644]
contrib/luadoc/lua/luadoc/doclet/html/function.lp [new file with mode: 0644]
contrib/luadoc/lua/luadoc/doclet/html/index.lp [new file with mode: 0644]
contrib/luadoc/lua/luadoc/doclet/html/luadoc.css [new file with mode: 0644]
contrib/luadoc/lua/luadoc/doclet/html/menu.lp [new file with mode: 0644]
contrib/luadoc/lua/luadoc/doclet/html/module.lp [new file with mode: 0644]
contrib/luadoc/lua/luadoc/doclet/html/table.lp [new file with mode: 0644]
contrib/luadoc/lua/luadoc/doclet/raw.lua [new file with mode: 0644]
contrib/luadoc/lua/luadoc/init.lua [new file with mode: 0644]
contrib/luadoc/lua/luadoc/lp.lua [new file with mode: 0644]
contrib/luadoc/lua/luadoc/taglet/standard.lua [new file with mode: 0644]
contrib/luadoc/lua/luadoc/taglet/standard/tags.lua [new file with mode: 0644]
contrib/luadoc/lua/luadoc/util.lua [new file with mode: 0644]

diff --git a/contrib/luadoc/lua/luadoc/config.lua b/contrib/luadoc/lua/luadoc/config.lua
new file mode 100644 (file)
index 0000000..9e4b9de
--- /dev/null
@@ -0,0 +1,34 @@
+-------------------------------------------------------------------------------
+-- LuaDoc configuration file. This file contains the default options for 
+-- luadoc operation. These options can be overriden by the command line tool
+-- @see luadoc.print_help
+-- @release $Id: config.lua,v 1.6 2007/04/18 14:28:39 tomas Exp $
+-------------------------------------------------------------------------------
+
+module "luadoc.config"
+
+-------------------------------------------------------------------------------
+-- Default options
+-- @class table
+-- @name default_options
+-- @field output_dir default output directory for generated documentation, used
+-- by several doclets
+-- @field taglet parser used to analyze source code input
+-- @field doclet documentation generator
+-- @field template_dir directory with documentation templates, used by the html
+-- doclet
+-- @field verbose command line tool configuration to output processing 
+-- information
+
+local default_options = {
+       output_dir = "",
+       taglet = "luadoc.taglet.standard",
+       doclet = "luadoc.doclet.html",
+       -- TODO: find a way to define doclet specific options
+       template_dir = "luadoc/doclet/html/",
+       nomodules = false,
+       nofiles = false,
+       verbose = true,
+}
+
+return default_options
diff --git a/contrib/luadoc/lua/luadoc/doclet/debug.lua b/contrib/luadoc/lua/luadoc/doclet/debug.lua
new file mode 100644 (file)
index 0000000..0b75f84
--- /dev/null
@@ -0,0 +1,46 @@
+-----------------------------------------------------------------
+-- LuaDoc debugging facilities.
+-- @release $Id: debug.lua,v 1.3 2007/04/18 14:28:39 tomas Exp $
+-----------------------------------------------------------------
+
+module "luadoc.doclet.debug"
+
+function printline()
+       print(string.rep('-', 79))
+end
+
+-----------------------------------------------------------------
+-- Print debug information about document
+-- @param doc Table with the structured documentation.
+
+function start (doc)
+       print("Files:")
+       for _, filepath in ipairs(doc.files) do
+               print('\t', filepath)
+       end
+       printline()
+
+       print("Modules:")
+       for _, modulename in ipairs(doc.modules) do
+               print('\t', modulename)
+       end
+       printline()
+       
+       for i, v in pairs(doc.files) do
+               print('\t', i, v)
+       end
+       printline()
+       for i, v in pairs(doc.files[doc.files[1]]) do
+               print(i, v)
+       end
+       
+       printline()
+       for i, v in pairs(doc.files[doc.files[1]].doc[1]) do
+               print(i, v)
+       end
+       printline()
+       print("Params")
+       for i, v in pairs(doc.files[doc.files[1]].doc[1].param) do
+               print(i, v)
+       end
+end
diff --git a/contrib/luadoc/lua/luadoc/doclet/formatter.lua b/contrib/luadoc/lua/luadoc/doclet/formatter.lua
new file mode 100644 (file)
index 0000000..af070d5
--- /dev/null
@@ -0,0 +1,84 @@
+-------------------------------------------------------------------------------
+-- Doclet to format source code according to LuaDoc standard tags. This doclet
+-- (re)write .lua files adding missing standard tags. Texts are formatted to
+-- 80 columns and function parameters are added based on code analysis.
+--
+-- @release $Id: formatter.lua,v 1.5 2007/04/18 14:28:39 tomas Exp $
+-------------------------------------------------------------------------------
+
+local util = require "luadoc.util"
+local assert, ipairs, pairs, type = assert, ipairs, pairs, type
+local string = require"string"
+local table = require"table"
+
+module "luadoc.doclet.formatter"
+
+options = {
+       output_dir = "./",
+}
+
+-------------------------------------------------------------------------------
+-- Assembly the output filename for an input file.
+-- TODO: change the name of this function
+function out_file (filename)
+       local h = filename
+       h = options.output_dir..h
+       return h
+end
+
+-------------------------------------------------------------------------------
+-- Generate a new lua file for each input lua file. If the user does not 
+-- specify a different output directory input files will be rewritten.
+-- @param doc documentation table
+
+function start (doc)
+       local todo = "<TODO>"
+       
+       -- Process files
+       for i, file_doc in ipairs(doc.files) do
+               -- assembly the filename
+               local filename = out_file(file_doc.name)
+               luadoc.logger:info(string.format("generating file `%s'", filename))
+
+               -- TODO: confirm file overwrite
+               local f = lfs.open(filename, "w")
+               assert(f, string.format("could not open `%s' for writing", filename))
+
+               for _, block in ipairs(file_doc.doc) do
+
+                       -- write reorganized comments
+                       f:write(string.rep("-", 80).."\n")
+                       
+                       -- description
+                       f:write(util.comment(util.wrap(block.description, 77)))
+                       f:write("\n")
+                       
+                       if block.class == "function" then
+                               -- parameters
+                               table.foreachi(block.param, function (_, param_name)
+                                       f:write(util.comment(util.wrap(string.format("@param %s %s", param_name, block.param[param_name] or todo), 77)))
+                                       f:write("\n")
+                               end)
+                               
+                               -- return
+                               if type(block.ret) == "table" then
+                                       table.foreachi(block.ret, function (_, ret)
+                                               f:write(util.comment(util.wrap(string.format("@return %s", ret), 77)).."\n")
+                                       end)
+                               else
+                                       f:write(util.comment(util.wrap(string.format("@return %s", block.ret or todo), 77)).."\n")
+                               end
+                       end
+                       
+                       -- TODO: usage
+                       -- TODO: see
+
+                       -- write code
+                       for _, line in ipairs(block.code) do
+                               f:write(line.."\n")
+                       end
+               end
+               
+               f:close()
+       end
+end
diff --git a/contrib/luadoc/lua/luadoc/doclet/html.lua b/contrib/luadoc/lua/luadoc/doclet/html.lua
new file mode 100644 (file)
index 0000000..b736a9d
--- /dev/null
@@ -0,0 +1,267 @@
+-------------------------------------------------------------------------------
+-- Doclet that generates HTML output. This doclet generates a set of html files
+-- based on a group of templates. The main templates are: 
+-- <ul>
+-- <li>index.lp: index of modules and files;</li>
+-- <li>file.lp: documentation for a lua file;</li>
+-- <li>module.lp: documentation for a lua module;</li>
+-- <li>function.lp: documentation for a lua function. This is a 
+-- sub-template used by the others.</li>
+-- </ul>
+--
+-- @release $Id: html.lua,v 1.29 2007/12/21 17:50:48 tomas Exp $
+-------------------------------------------------------------------------------
+
+local assert, getfenv, ipairs, loadstring, pairs, setfenv, tostring, tonumber, type = assert, getfenv, ipairs, loadstring, pairs, setfenv, tostring, tonumber, type
+local io = require"io"
+local lfs = require "lfs"
+local lp = require "luadoc.lp"
+local luadoc = require"luadoc"
+local package = package
+local string = require"string"
+local table = require"table"
+
+module "luadoc.doclet.html"
+
+-------------------------------------------------------------------------------
+-- Looks for a file `name' in given path. Removed from compat-5.1
+-- @param path String with the path.
+-- @param name String with the name to look for.
+-- @return String with the complete path of the file found
+--     or nil in case the file is not found.
+
+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
+
+-------------------------------------------------------------------------------
+-- Include the result of a lp template into the current stream.
+
+function include (template, env)
+       -- template_dir is relative to package.path
+       local templatepath = options.template_dir .. template
+       
+       -- search using package.path (modified to search .lp instead of .lua
+       local search_path = string.gsub(package.path, "%.lua", "")
+       local templatepath = search(search_path, templatepath)
+       assert(templatepath, string.format("template `%s' not found", template))
+       
+       env = env or {}
+       env.table = table
+       env.io = io
+       env.lp = lp
+       env.ipairs = ipairs
+       env.tonumber = tonumber
+       env.tostring = tostring
+       env.type = type
+       env.luadoc = luadoc
+       env.options = options
+       
+       return lp.include(templatepath, env)
+end
+
+-------------------------------------------------------------------------------
+-- Returns a link to a html file, appending "../" to the link to make it right.
+-- @param html Name of the html file to link to
+-- @return link to the html file
+
+function link (html, from)
+       local h = html
+       from = from or ""
+       string.gsub(from, "/", function () h = "../" .. h end)
+       return h
+end
+
+-------------------------------------------------------------------------------
+-- Returns the name of the html file to be generated from a module.
+-- Files with "lua" or "luadoc" extensions are replaced by "html" extension.
+-- @param modulename Name of the module to be processed, may be a .lua file or
+-- a .luadoc file.
+-- @return name of the generated html file for the module
+
+function module_link (modulename, doc, from)
+       -- TODO: replace "." by "/" to create directories?
+       -- TODO: how to deal with module names with "/"?
+       assert(modulename)
+       assert(doc)
+       from = from or ""
+       
+       if doc.modules[modulename] == nil then
+--             logger:error(string.format("unresolved reference to module `%s'", modulename))
+               return
+       end
+       
+       local href = "modules/" .. modulename .. ".html"
+       string.gsub(from, "/", function () href = "../" .. href end)
+       return href
+end
+
+-------------------------------------------------------------------------------
+-- Returns the name of the html file to be generated from a lua(doc) file.
+-- Files with "lua" or "luadoc" extensions are replaced by "html" extension.
+-- @param to Name of the file to be processed, may be a .lua file or
+-- a .luadoc file.
+-- @param from path of where am I, based on this we append ..'s to the
+-- beginning of path
+-- @return name of the generated html file
+
+function file_link (to, from)
+       assert(to)
+       from = from or ""
+       
+       local href = to
+       href = string.gsub(href, "lua$", "html")
+       href = string.gsub(href, "luadoc$", "html")
+       href = "files/" .. href
+       string.gsub(from, "/", function () href = "../" .. href end)
+       return href
+end
+
+-------------------------------------------------------------------------------
+-- Returns a link to a function or to a table
+-- @param fname name of the function or table to link to.
+-- @param doc documentation table
+-- @param kind String specying the kinf of element to link ("functions" or "tables").
+
+function link_to (fname, doc, module_doc, file_doc, from, kind)
+       assert(fname)
+       assert(doc)
+       from = from or ""
+       kind = kind or "functions"
+       
+       if file_doc then
+               for _, func_name in pairs(file_doc[kind]) do
+                       if func_name == fname then
+                               return file_link(file_doc.name, from) .. "#" .. fname
+                       end
+               end
+       end
+       
+       local _, _, modulename, fname = string.find(fname, "^(.-)[%.%:]?([^%.%:]*)$")
+       assert(fname)
+
+       -- if fname does not specify a module, use the module_doc
+       if string.len(modulename) == 0 and module_doc then
+               modulename = module_doc.name
+       end
+
+       local module_doc = doc.modules[modulename]
+       if not module_doc then
+--             logger:error(string.format("unresolved reference to function `%s': module `%s' not found", fname, modulename))
+               return
+       end
+       
+       for _, func_name in pairs(module_doc[kind]) do
+               if func_name == fname then
+                       return module_link(modulename, doc, from) .. "#" .. fname
+               end
+       end
+       
+--     logger:error(string.format("unresolved reference to function `%s' of module `%s'", fname, modulename))
+end
+
+-------------------------------------------------------------------------------
+-- Make a link to a file, module or function
+
+function symbol_link (symbol, doc, module_doc, file_doc, from)
+       assert(symbol)
+       assert(doc)
+       
+       local href = 
+--             file_link(symbol, from) or
+               module_link(symbol, doc, from) or 
+               link_to(symbol, doc, module_doc, file_doc, from, "functions") or
+               link_to(symbol, doc, module_doc, file_doc, from, "tables")
+       
+       if not href then
+               logger:error(string.format("unresolved reference to symbol `%s'", symbol))
+       end
+       
+       return href or ""
+end
+
+-------------------------------------------------------------------------------
+-- Assembly the output filename for an input file.
+-- TODO: change the name of this function
+function out_file (filename)
+       local h = filename
+       h = string.gsub(h, "lua$", "html")
+       h = string.gsub(h, "luadoc$", "html")
+       h = "files/" .. h
+--     h = options.output_dir .. string.gsub (h, "^.-([%w_]+%.html)$", "%1")
+       h = options.output_dir .. h
+       return h
+end
+
+-------------------------------------------------------------------------------
+-- Assembly the output filename for a module.
+-- TODO: change the name of this function
+function out_module (modulename)
+       local h = modulename .. ".html"
+       h = "modules/" .. h
+       h = options.output_dir .. h
+       return h
+end
+
+-----------------------------------------------------------------
+-- Generate the output.
+-- @param doc Table with the structured documentation.
+
+function start (doc)
+       -- Generate index file
+       if (#doc.files > 0 or #doc.modules > 0) and (not options.noindexpage) then
+               local filename = options.output_dir.."index.html"
+               logger:info(string.format("generating file `%s'", filename))
+               local f = lfs.open(filename, "w")
+               assert(f, string.format("could not open `%s' for writing", filename))
+               io.output(f)
+               include("index.lp", { doc = doc })
+               f:close()
+       end
+       
+       -- Process modules
+       if not options.nomodules then
+               for _, modulename in ipairs(doc.modules) do
+                       local module_doc = doc.modules[modulename]
+                       -- assembly the filename
+                       local filename = out_module(modulename)
+                       logger:info(string.format("generating file `%s'", filename))
+                       
+                       local f = lfs.open(filename, "w")
+                       assert(f, string.format("could not open `%s' for writing", filename))
+                       io.output(f)
+                       include("module.lp", { doc = doc, module_doc = module_doc })
+                       f:close()
+               end
+       end
+
+       -- Process files
+       if not options.nofiles then
+               for _, filepath in ipairs(doc.files) do
+                       local file_doc = doc.files[filepath]
+                       -- assembly the filename
+                       local filename = out_file(file_doc.name)
+                       logger:info(string.format("generating file `%s'", filename))
+                       
+                       local f = lfs.open(filename, "w")
+                       assert(f, string.format("could not open `%s' for writing", filename))
+                       io.output(f)
+                       include("file.lp", { doc = doc, file_doc = file_doc} )
+                       f:close()
+               end
+       end
+       
+       -- copy extra files
+       local f = lfs.open(options.output_dir.."luadoc.css", "w")
+       io.output(f)
+       include("luadoc.css")
+       f:close()
+end
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/file.lp b/contrib/luadoc/lua/luadoc/doclet/html/file.lp
new file mode 100644 (file)
index 0000000..67926b4
--- /dev/null
@@ -0,0 +1,112 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+    <title>Reference</title>
+    <link rel="stylesheet" href="<%=luadoc.doclet.html.link('luadoc.css', 'files/'..file_doc.name)%>" type="text/css" />
+       <!--meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/-->
+</head>
+
+<body>
+<div id="container">
+
+<div id="product">
+       <div id="product_logo"></div>
+       <div id="product_name"><big><b></b></big></div>
+       <div id="product_description"></div>
+</div> <!-- id="product" -->
+
+<div id="main">
+
+<div id="navigation">
+<%=luadoc.doclet.html.include("menu.lp", { doc=doc, file_doc=file_doc })%>
+
+</div> <!-- id="navigation" -->
+
+<div id="content">
+
+<h1>File <code><%=file_doc.name%></code></h1>
+
+<%if file_doc.description then%>
+<p><%=file_doc.description%></p>
+<%end%>
+<%if file_doc.author then%>
+<p><b><%= #file_doc.author>1 and "Authors" or "Author" %>:</b>
+<table class="authors_list">
+<%for _, author in ipairs(file_doc.author) do%>
+       <tr><td class="name"><%= author %></td></tr>
+<%end%>
+</table>
+</p>
+<%end%>
+<%if file_doc.copyright then%>
+<p>Copyright &copy;<%=file_doc.copyright%></p>
+<%end%>
+<%if file_doc.release then%>
+<p><small><b>Release:</b> <%=file_doc.release%></small></p>
+<%end%>
+
+<%if #file_doc.functions > 0 then%>
+<h2>Functions</h2>
+<table class="function_list">
+<%for _, func_name in ipairs(file_doc.functions) do
+  local func_data = file_doc.functions[func_name]%>
+       <tr>
+       <td class="name" nowrap><%=func_data.private and "local " or ""%><a href="#<%=func_name%>"><%=func_name%></a>&nbsp;(<%=table.concat(func_data.param, ", ")%>)</td>
+       <td class="summary"><%=func_data.summary%></td>
+       </tr>
+<%end%>
+</table>
+<%end%>
+
+
+<%if #file_doc.tables > 0 then%>
+<h2>Tables</h2>
+<table class="table_list">
+<%for _, tab_name in ipairs(file_doc.tables) do%>
+       <tr>
+       <td class="name" nowrap><a href="#<%=tab_name%>"><%=tab_name%></a></td>
+       <td class="summary"><%=file_doc.tables[tab_name].summary%></td>
+       </tr>
+<%end%>
+</table>
+<%end%>
+
+
+<br/>
+<br/>
+
+
+
+<%if #file_doc.functions > 0 then%>
+<h2><a name="functions"></a>Functions</h2>
+<dl class="function">
+<%for _, func_name in ipairs(file_doc.functions) do%>
+<%=luadoc.doclet.html.include("function.lp", { doc=doc, file_doc=file_doc, func=file_doc.functions[func_name] })%>
+<%end%>
+</dl>
+<%end%>
+
+
+<%if #file_doc.tables > 0 then%>
+<h2><a name="tables"></a>Tables</h2>
+<dl class="table">
+<%for _, tab_name in ipairs(file_doc.tables) do%>
+<%=luadoc.doclet.html.include("table.lp", { doc=doc, file_doc=file_doc, tab=file_doc.tables[tab_name] })%>
+<%end%>
+</dl>
+<%end%>
+
+
+
+</div> <!-- id="content" -->
+
+</div> <!-- id="main" -->
+
+<div id="about">
+       <p><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></p>
+</div> <!-- id="about" -->
+
+</div> <!-- id="container" --> 
+</body>
+</html>
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/function.lp b/contrib/luadoc/lua/luadoc/doclet/html/function.lp
new file mode 100644 (file)
index 0000000..aa0e25b
--- /dev/null
@@ -0,0 +1,64 @@
+<%
+if module_doc then
+       from = "modules/"..module_doc.name
+elseif file_doc then
+       from = "files/.."..file_doc.name
+else
+       from = ""
+end
+%>
+
+<dt><%=func.private and "local " or ""%><a name="<%=func.name%>"></a><strong><%=func.name%></strong>&nbsp;(<%=table.concat(func.param, ", ")%>)</dt>
+<dd>
+<%=func.description or ""%>
+
+<%if type(func.param) == "table" and #func.param > 0 then%>
+<h3>Parameters</h3>
+<ul>
+       <%for p = 1, #func.param do%>
+       <li>
+         <%=func.param[p]%>: <%=func.param[func.param[p]] or ""%>
+       </li>
+       <%end%>
+</ul>
+<%end%>
+
+
+<%if type(func.usage) == "string" then%>
+<h3>Usage:</h3>
+<%=func.usage%>
+<%elseif type(func.usage) == "table" then%>
+<h3>Usage</h3>
+<ul>
+       <%for _, usage in ipairs(func.usage) do%>
+       <li><%= usage %>
+       <%end%>
+</ul>
+<%end%>
+
+<%if type(func.ret) == "string" then%>
+<h3>Return value:</h3>
+<%=func.ret%>
+<%elseif type(func.ret) == "table" then%>
+<h3>Return values:</h3>
+<ol>
+       <%for _, ret in ipairs(func.ret) do%>
+       <li><%= ret %>
+       <%end%>
+</ol>
+<%end%>
+
+<%if type(func.see) == "string" then %>
+<h3>See also:</h3>
+       <a href="<%=func.see%>"><%=func.see%></a>
+<%elseif type(func.see) == "table" and #func.see > 0 then %>
+<h3>See also:</h3>
+<ul>
+       <%for i = 1, #func.see do%>
+       <li><a href="<%=luadoc.doclet.html.symbol_link(func.see[i], doc, module_doc, file_doc, from)%>">
+               <%=func.see[i]%>
+       </a>
+       <%end%>
+</ul>
+<%end%>
+</dd>
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/index.lp b/contrib/luadoc/lua/luadoc/doclet/html/index.lp
new file mode 100644 (file)
index 0000000..b4b9f5c
--- /dev/null
@@ -0,0 +1,67 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+    <title>Reference</title>
+    <link rel="stylesheet" href="<%=luadoc.doclet.html.link("luadoc.css")%>" type="text/css" />
+       <!--meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/-->
+</head>
+
+<body>
+<div id="container">
+
+<div id="product">
+       <div id="product_logo"></div>
+       <div id="product_name"><big><b></b></big></div>
+       <div id="product_description"></div>
+</div> <!-- id="product" -->
+
+<div id="main">
+
+<div id="navigation">
+<%=luadoc.doclet.html.include("menu.lp", { doc=doc })%>
+
+</div> <!-- id="navigation" -->
+
+<div id="content">
+
+
+<%if not options.nomodules and #doc.modules > 0 then%>
+<h2>Modules</h2>
+<table class="module_list">
+<!--<tr><td colspan="2">Modules</td></tr>-->
+<%for _, modulename in ipairs(doc.modules) do%>
+       <tr>
+               <td class="name"><a href="<%=luadoc.doclet.html.module_link(modulename, doc)%>"><%=modulename%></a></td>
+               <td class="summary"><%=doc.modules[modulename].summary%></td>
+       </tr>
+<%end%>
+</table>
+<%end%>
+
+
+
+<%if not options.nofiles and #doc.files > 0 then%>
+<h2>Files</h2>
+<table class="file_list">
+<!--<tr><td colspan="2">Files</td></tr>-->
+<%for _, filepath in ipairs(doc.files) do%>
+       <tr>
+               <td class="name"><a href="<%=luadoc.doclet.html.file_link(filepath)%>"><%=filepath%></a></td>
+               <td class="summary"></td>
+       </tr>
+<%end%>
+</table>
+<%end%>
+
+</div> <!-- id="content" -->
+
+</div> <!-- id="main" -->
+
+<div id="about">
+       <p><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></p>
+</div> <!-- id="about" -->
+
+</div> <!-- id="container" --> 
+</body>
+</html>
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/luadoc.css b/contrib/luadoc/lua/luadoc/doclet/html/luadoc.css
new file mode 100644 (file)
index 0000000..bc0f98a
--- /dev/null
@@ -0,0 +1,286 @@
+body { 
+    margin-left: 1em; 
+    margin-right: 1em; 
+    font-family: arial, helvetica, geneva, sans-serif;
+       background-color:#ffffff; margin:0px;
+}
+
+code {
+    font-family: "Andale Mono", monospace; 
+}
+
+tt {
+    font-family: "Andale Mono", monospace; 
+}
+
+body, td, th { font-size: 11pt; }
+
+h1, h2, h3, h4 { margin-left: 0em; }
+
+textarea, pre, tt { font-size:10pt; }
+body, td, th { color:#000000; }
+small { font-size:0.85em; }
+h1 { font-size:1.5em; }
+h2 { font-size:1.25em; }
+h3 { font-size:1.15em; }
+h4 { font-size:1.06em; }
+
+a:link { font-weight:bold; color: #004080; text-decoration: none; }
+a:visited { font-weight:bold; color: #006699; text-decoration: none; }
+a:link:hover { text-decoration:underline; }
+hr { color:#cccccc }
+img { border-width: 0px; }
+
+
+h3 { padding-top: 1em; }
+
+p { margin-left: 1em; }
+
+p.name { 
+    font-family: "Andale Mono", monospace; 
+    padding-top: 1em;
+    margin-left: 0em; 
+}
+
+blockquote { margin-left: 3em; }
+
+pre.example {
+    background-color: rgb(245, 245, 245);
+    border-top-width: 1px;
+    border-right-width: 1px;
+    border-bottom-width: 1px;
+    border-left-width: 1px;
+    border-top-style: solid;
+    border-right-style: solid;
+    border-bottom-style: solid;
+    border-left-style: solid;
+    border-top-color: silver;
+    border-right-color: silver;
+    border-bottom-color: silver;
+    border-left-color: silver;
+    padding: 1em;
+    margin-left: 1em;
+    margin-right: 1em;
+    font-family: "Andale Mono", monospace; 
+    font-size: smaller;
+}
+
+
+hr { 
+    margin-left: 0em;
+       background: #00007f; 
+       border: 0px;
+       height: 1px;
+}
+
+ul { list-style-type: disc; }
+
+table.index { border: 1px #00007f; }
+table.index td { text-align: left; vertical-align: top; }
+table.index ul { padding-top: 0em; margin-top: 0em; }
+
+table {
+    border: 1px solid black;
+       border-collapse: collapse;
+    margin-left: auto;
+    margin-right: auto;
+}
+th {
+    border: 1px solid black;
+    padding: 0.5em;
+}
+td {
+    border: 1px solid black;
+    padding: 0.5em;
+}
+div.header, div.footer { margin-left: 0em; }
+
+#container
+{
+       margin-left: 1em;
+       margin-right: 1em;
+       background-color: #f0f0f0;
+}
+
+#product
+{
+       text-align: center;
+       border-bottom: 1px solid #cccccc;
+       background-color: #ffffff;
+}
+
+#product big {
+       font-size: 2em;
+}
+
+#product_logo
+{
+}
+
+#product_name
+{
+}
+
+#product_description
+{
+}
+
+#main
+{
+       background-color: #f0f0f0;
+       border-left: 2px solid #cccccc;
+}
+
+#navigation
+{
+       float: left;
+       width: 18em;
+       margin: 0;
+       vertical-align: top;
+       background-color: #f0f0f0;
+       overflow:visible;
+}
+
+#navigation h1 {
+       background-color:#e7e7e7;
+       font-size:1.1em;
+       color:#000000;
+       text-align:left;
+       margin:0px;
+       padding:0.2em;
+       border-top:1px solid #dddddd;
+       border-bottom:1px solid #dddddd;
+}
+
+#navigation ul
+{
+       font-size:1em;
+       list-style-type: none;
+       padding: 0;
+       margin: 1px;
+}
+
+#navigation li
+{
+       text-indent: -1em;
+       margin: 0em 0em 0em 0.5em;
+       display: block;
+       padding: 3px 0px 0px 12px;
+}
+
+#navigation li li a
+{
+       padding: 0px 3px 0px -1em;
+}
+
+#content
+{
+       margin-left: 18em;
+       padding: 1em;
+       border-left: 2px solid #cccccc;
+       border-right: 2px solid #cccccc;
+       background-color: #ffffff;
+}
+
+#about
+{
+       clear: both;
+       margin: 0;
+       padding: 5px;
+       border-top: 2px solid #cccccc;
+       background-color: #ffffff;
+}
+
+@media print {
+       body { 
+               font: 12pt "Times New Roman", "TimeNR", Times, serif;
+       }
+       a { font-weight:bold; color: #004080; text-decoration: underline; }
+       
+       #main\r  {\r              background-color: #ffffff;\r             border-left: 0px;\r      }\r      
+       #container\r     {\r              margin-left: 2%;\r               margin-right: 2%;\r              background-color: #ffffff;\r     }
+       
+       #content\r       {\r              margin-left: 0px;\r              padding: 1em;\r          border-left: 0px;\r              border-right: 0px;\r             background-color: #ffffff;\r     }
+       
+       #navigation\r    {\r              display: none;
+       }
+       pre.example {
+               font-family: "Andale Mono", monospace; 
+               font-size: 10pt;
+               page-break-inside: avoid;
+       }
+}
+
+table.module_list td
+{
+       border-width: 1px;
+       padding: 3px;
+       border-style: solid;
+       border-color: #cccccc;
+}
+table.module_list td.name { background-color: #f0f0f0; }
+table.module_list td.summary { width: 100%; }
+
+table.file_list
+{
+       border-width: 1px;
+       border-style: solid;
+       border-color: #cccccc;
+       border-collapse: collapse;
+}
+table.file_list td
+{
+       border-width: 1px;
+       padding: 3px;
+       border-style: solid;
+       border-color: #cccccc;
+}
+table.file_list td.name { background-color: #f0f0f0; }
+table.file_list td.summary { width: 100%; }
+
+
+table.function_list
+{
+       border-width: 1px;
+       border-style: solid;
+       border-color: #cccccc;
+       border-collapse: collapse;
+}
+table.function_list td
+{
+       border-width: 1px;
+       padding: 3px;
+       border-style: solid;
+       border-color: #cccccc;
+}
+table.function_list td.name { background-color: #f0f0f0; }
+table.function_list td.summary { width: 100%; }
+
+
+table.table_list
+{
+       border-width: 1px;
+       border-style: solid;
+       border-color: #cccccc;
+       border-collapse: collapse;
+}
+table.table_list td
+{
+       border-width: 1px;
+       padding: 3px;
+       border-style: solid;
+       border-color: #cccccc;
+}
+table.table_list td.name { background-color: #f0f0f0; }
+table.table_list td.summary { width: 100%; }
+
+dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
+dl.function dd {padding-bottom: 1em;}
+dl.function h3 {padding: 0; margin: 0; font-size: medium;}
+
+dl.table dt {border-top: 1px solid #ccc; padding-top: 1em;}
+dl.table dd {padding-bottom: 1em;}
+dl.table h3 {padding: 0; margin: 0; font-size: medium;}
+
+#TODO: make module_list, file_list, function_list, table_list inherit from a list
+
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/menu.lp b/contrib/luadoc/lua/luadoc/doclet/html/menu.lp
new file mode 100644 (file)
index 0000000..0fe3652
--- /dev/null
@@ -0,0 +1,55 @@
+<%
+if module_doc then
+       from = "modules/"..module_doc.name
+elseif file_doc then
+       from = "files/.."..file_doc.name
+else
+       from = ""
+end
+%>
+
+<h1>LuaDoc</h1>
+<ul>
+       <%if not module_doc and not file_doc then%>
+       <li><strong>Index</strong></li>
+       <%else%>
+       <li><a href="<%=luadoc.doclet.html.link("index.html", from)%>">Index</a></li>
+       <%end%>
+</ul>
+
+
+<!-- Module list -->
+<%if not options.nomodules and #doc.modules > 0 then%>
+<h1>Modules</h1>
+<ul>
+<%for _, modulename in ipairs(doc.modules) do
+       if module_doc and module_doc.name == modulename then%>
+       <li><strong><%=modulename%></strong></li>
+       <%else%>
+       <li>
+               <a href="<%=luadoc.doclet.html.module_link(modulename, doc, from)%>"><%=modulename%></a>
+       </li>
+<%     end
+end%>
+</ul>
+<%end%>
+
+
+<!-- File list -->
+<%if not options.nofiles and #doc.files > 0 then%>
+<h1>Files</h1>
+<ul>
+<%for _, filepath in ipairs(doc.files) do
+       if file_doc and file_doc.name == filepath then%>
+       <li><strong><%=filepath%></strong></li>
+       <%else%>
+       <li>
+               <a href="<%=luadoc.doclet.html.file_link(filepath, from)%>"><%=filepath%></a>
+       </li>
+<%     end
+end%>
+</ul>
+<%end%>
+
+
+
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/module.lp b/contrib/luadoc/lua/luadoc/doclet/html/module.lp
new file mode 100644 (file)
index 0000000..6dca9a9
--- /dev/null
@@ -0,0 +1,108 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+    <title>Reference</title>
+    <link rel="stylesheet" href="<%=luadoc.doclet.html.link('luadoc.css', 'modules/'..module_doc.name)%>" type="text/css" />
+       <!--meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/-->
+</head>
+
+<body>
+<div id="container">
+
+<div id="product">
+       <div id="product_logo"></div>
+       <div id="product_name"><big><b></b></big></div>
+       <div id="product_description"></div>
+</div> <!-- id="product" -->
+
+<div id="main">
+
+<div id="navigation">
+<%=luadoc.doclet.html.include("menu.lp", { doc=doc, module_doc=module_doc })%>
+
+</div><!-- id="navigation" -->
+
+<div id="content">
+
+<h1>Module <code><%=module_doc.name%></code></h1>
+
+<p><%=module_doc.description%></p>
+<%if module_doc.author then%>
+<p><b><%= #module_doc.author>1 and "Authors" or "Author" %>:</b>
+<table class="authors_list">
+<%for _, author in ipairs(module_doc.author) do%>
+       <tr><td class="name"><%= author %></td></tr>
+<%end%>
+</table>
+</p>
+<%end%>
+<%if module_doc.copyright then%>
+<p>Copyright&copy; <%=module_doc.copyright%></p>
+<%end%>
+<%if module_doc.release then%>
+<p><small><b>Release:</b> <%=module_doc.release%></small></p>
+<%end%>
+
+<%if #module_doc.functions > 0 then%>
+<h2>Functions</h2>
+<table class="function_list">
+<%for _, func_name in ipairs(module_doc.functions) do
+  local func_data = module_doc.functions[func_name]%>
+       <tr>
+       <td class="name" nowrap><%=func_data.private and "local " or ""%><a href="#<%=func_name%>"><%=func_name%></a>&nbsp;(<%=table.concat(module_doc.functions[func_name].param, ", ")%>)</td>
+       <td class="summary"><%=module_doc.functions[func_name].summary%></td>
+       </tr>
+<%end%>
+</table>
+<%end%>
+
+
+<%if #module_doc.tables > 0 then%>
+<h2>Tables</h2>
+<table class="table_list">
+<%for _, tab_name in ipairs(module_doc.tables) do%>
+       <tr>
+       <td class="name" nowrap><a href="#<%=tab_name%>"><%=tab_name%></a></td>
+       <td class="summary"><%=module_doc.tables[tab_name].summary%></td>
+       </tr>
+<%end%>
+</table>
+<%end%>
+
+
+<br/>
+<br/>
+
+
+<%if #module_doc.functions > 0 then%>
+<h2><a name="functions"></a>Functions</h2>
+<dl class="function">
+<%for _, func_name in ipairs(module_doc.functions) do%>
+<%=luadoc.doclet.html.include("function.lp", { doc=doc, module_doc=module_doc, func=module_doc.functions[func_name] })%>
+<%end%>
+</dl>
+<%end%>
+
+
+<%if #module_doc.tables > 0 then%>
+<h2><a name="tables"></a>Tables</h2>
+<dl class="table">
+<%for _, tab_name in ipairs(module_doc.tables) do%>
+<%=luadoc.doclet.html.include("table.lp", { doc=doc, module_doc=module_doc, tab=module_doc.tables[tab_name] })%>
+<%end%>
+</dl>
+<%end%>
+
+
+</div> <!-- id="content" -->
+
+</div> <!-- id="main" -->
+
+<div id="about">
+       <p><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.w3.org/Icons/valid-xhtml10" alt="Valid XHTML 1.0!" height="31" width="88" /></a></p>
+</div> <!-- id="about" -->
+
+</div> <!-- id="container" --> 
+</body>
+</html>
diff --git a/contrib/luadoc/lua/luadoc/doclet/html/table.lp b/contrib/luadoc/lua/luadoc/doclet/html/table.lp
new file mode 100644 (file)
index 0000000..5cd0239
--- /dev/null
@@ -0,0 +1,15 @@
+<dt><a name="<%=tab.name%>"></a><strong><%=tab.name%></strong></dt>
+<dd><%=tab.description%>
+
+<%if type(tab.field) == "table" and #tab.field > 0 then%>
+<em>Fields</em>
+<ul>
+       <%for p = 1, #tab.field do%>
+       <li>
+         <%=tab.field[p]%>: <%=tab.field[tab.field[p]] or ""%>
+       </li>
+       <%end%>
+</ul>
+<%end%>
+
+</dd>
diff --git a/contrib/luadoc/lua/luadoc/doclet/raw.lua b/contrib/luadoc/lua/luadoc/doclet/raw.lua
new file mode 100644 (file)
index 0000000..1e880b8
--- /dev/null
@@ -0,0 +1,12 @@
+-----------------------------------------------------------------
+-- @release $Id: raw.lua,v 1.5 2007/04/18 14:28:39 tomas Exp $
+-----------------------------------------------------------------
+
+module "luadoc.doclet.raw"
+
+-----------------------------------------------------------------
+-- Generate the output.
+-- @param doc Table with the structured documentation.
+
+function start (doc)
+end
diff --git a/contrib/luadoc/lua/luadoc/init.lua b/contrib/luadoc/lua/luadoc/init.lua
new file mode 100644 (file)
index 0000000..649515d
--- /dev/null
@@ -0,0 +1,50 @@
+-------------------------------------------------------------------------------
+-- LuaDoc main function.
+-- @release $Id: init.lua,v 1.4 2008/02/17 06:42:51 jasonsantos Exp $
+-------------------------------------------------------------------------------
+
+local require = require
+
+local util = require "luadoc.util"
+
+logger = {}
+
+module ("luadoc")
+
+-------------------------------------------------------------------------------
+-- LuaDoc version number.
+
+_COPYRIGHT = "Copyright (c) 2003-2007 The Kepler Project"
+_DESCRIPTION = "Documentation Generator Tool for the Lua language"
+_VERSION = "LuaDoc 3.0.1"
+
+-------------------------------------------------------------------------------
+-- Main function
+-- @see luadoc.doclet.html, luadoc.doclet.formatter, luadoc.doclet.raw
+-- @see luadoc.taglet.standard
+
+function main (files, options)
+       logger = util.loadlogengine(options)
+
+       -- load config file
+       if options.config ~= nil then
+               -- load specified config file
+               dofile(options.config)
+       else
+               -- load default config file
+               require("luadoc.config")
+       end
+       
+       local taglet = require(options.taglet)
+       local doclet = require(options.doclet)
+
+       -- analyze input
+       taglet.options = options
+       taglet.logger = logger
+       local doc = taglet.start(files)
+
+       -- generate output
+       doclet.options = options
+       doclet.logger = logger
+       doclet.start(doc)
+end
diff --git a/contrib/luadoc/lua/luadoc/lp.lua b/contrib/luadoc/lua/luadoc/lp.lua
new file mode 100644 (file)
index 0000000..adf84f9
--- /dev/null
@@ -0,0 +1,130 @@
+----------------------------------------------------------------------------
+-- Lua Pages Template Preprocessor.
+--
+-- @release $Id: lp.lua,v 1.7 2007/04/18 14:28:39 tomas Exp $
+----------------------------------------------------------------------------
+
+local assert, error, getfenv, loadstring, setfenv = assert, error, getfenv, loadstring, setfenv
+local find, format, gsub, strsub = string.find, string.format, string.gsub, string.sub
+local concat, tinsert = table.concat, table.insert
+local open = io.open
+
+module (...)
+
+----------------------------------------------------------------------------
+-- function to do output
+local outfunc = "io.write"
+-- accepts the old expression field: `$| <Lua expression> |$'
+local compatmode = true
+
+--
+-- Builds a piece of Lua code which outputs the (part of the) given string.
+-- @param s String.
+-- @param i Number with the initial position in the string.
+-- @param f Number with the final position in the string (default == -1).
+-- @return String with the correspondent Lua code which outputs the part of the string.
+--
+local function out (s, i, f)
+       s = strsub(s, i, f or -1)
+       if s == "" then return s end
+       -- we could use `%q' here, but this way we have better control
+       s = gsub(s, "([\\\n\'])", "\\%1")
+       -- substitute '\r' by '\'+'r' and let `loadstring' reconstruct it
+       s = gsub(s, "\r", "\\r")
+       return format(" %s('%s'); ", outfunc, s)
+end
+
+
+----------------------------------------------------------------------------
+-- Translate the template to Lua code.
+-- @param s String to translate.
+-- @return String with translated code.
+----------------------------------------------------------------------------
+function translate (s)
+       if compatmode then
+               s = gsub(s, "$|(.-)|%$", "<?lua = %1 ?>")
+               s = gsub(s, "<!%-%-$$(.-)$$%-%->", "<?lua %1 ?>")
+       end
+       s = gsub(s, "<%%(.-)%%>", "<?lua %1 ?>")
+       local res = {}
+       local start = 1   -- start of untranslated part in `s'
+       while true do
+               local ip, fp, target, exp, code = find(s, "<%?(%w*)[ \t]*(=?)(.-)%?>", start)
+               if not ip then break end
+               tinsert(res, out(s, start, ip-1))
+               if target ~= "" and target ~= "lua" then
+                       -- not for Lua; pass whole instruction to the output
+                       tinsert(res, out(s, ip, fp))
+               else
+                       if exp == "=" then   -- expression?
+                               tinsert(res, format(" %s(%s);", outfunc, code))
+                       else  -- command
+                               tinsert(res, format(" %s ", code))
+                       end
+               end
+               start = fp + 1
+       end
+       tinsert(res, out(s, start))
+       return concat(res)
+end
+
+
+----------------------------------------------------------------------------
+-- Defines the name of the output function.
+-- @param f String with the name of the function which produces output.
+
+function setoutfunc (f)
+       outfunc = f
+end
+
+----------------------------------------------------------------------------
+-- Turns on or off the compatibility with old CGILua 3.X behavior.
+-- @param c Boolean indicating if the compatibility mode should be used.
+
+function setcompatmode (c)
+       compatmode = c
+end
+
+----------------------------------------------------------------------------
+-- Internal compilation cache.
+
+local cache = {}
+
+----------------------------------------------------------------------------
+-- Translates a template into a Lua function.
+-- Does NOT execute the resulting function.
+-- Uses a cache of templates.
+-- @param string String with the template to be translated.
+-- @param chunkname String with the name of the chunk, for debugging purposes.
+-- @return Function with the resulting translation.
+
+function compile (string, chunkname)
+       local f, err = cache[string]
+       if f then return f end
+       f, err = loadstring (translate (string), chunkname)
+       if not f then error (err, 3) end
+       cache[string] = f
+       return f
+end
+
+----------------------------------------------------------------------------
+-- Translates and executes a template in a given file.
+-- The translation creates a Lua function which will be executed in an
+-- optionally given environment.
+-- @param filename String with the name of the file containing the template.
+-- @param env Table with the environment to run the resulting function.
+
+function include (filename, env)
+       -- read the whole contents of the file
+       local fh = assert (open (filename))
+       local src = fh:read("*a")
+       fh:close()
+       -- translates the file into a function
+       local prog = compile (src, '@'..filename)
+       local _env
+       if env then
+               _env = getfenv (prog)
+               setfenv (prog, env)
+       end
+       prog ()
+end
diff --git a/contrib/luadoc/lua/luadoc/taglet/standard.lua b/contrib/luadoc/lua/luadoc/taglet/standard.lua
new file mode 100644 (file)
index 0000000..144755e
--- /dev/null
@@ -0,0 +1,493 @@
+-------------------------------------------------------------------------------
+-- @release $Id: standard.lua,v 1.39 2007/12/21 17:50:48 tomas Exp $
+-------------------------------------------------------------------------------
+
+local assert, pairs, tostring, type = assert, pairs, tostring, type
+local io = require "io"
+local lfs = require "lfs"
+local luadoc = require "luadoc"
+local util = require "luadoc.util"
+local tags = require "luadoc.taglet.standard.tags"
+local string = require "string"
+local table = require "table"
+
+module 'luadoc.taglet.standard'
+
+-------------------------------------------------------------------------------
+-- Creates an iterator for an array base on a class type.
+-- @param t array to iterate over
+-- @param class name of the class to iterate over
+
+function class_iterator (t, class)
+       return function ()
+               local i = 1
+               return function ()
+                       while t[i] and t[i].class ~= class do
+                               i = i + 1
+                       end
+                       local v = t[i]
+                       i = i + 1
+                       return v
+               end
+       end
+end
+
+-- Patterns for function recognition
+local identifiers_list_pattern = "%s*(.-)%s*"
+local identifier_pattern = "[^%(%s]+"
+local function_patterns = {
+       "^()%s*function%s*("..identifier_pattern..")%s*%("..identifiers_list_pattern.."%)",
+       "^%s*(local%s)%s*function%s*("..identifier_pattern..")%s*%("..identifiers_list_pattern.."%)",
+       "^()%s*("..identifier_pattern..")%s*%=%s*function%s*%("..identifiers_list_pattern.."%)",
+}
+
+-------------------------------------------------------------------------------
+-- Checks if the line contains a function definition
+-- @param line string with line text
+-- @return function information or nil if no function definition found
+
+local function check_function (line)
+       line = util.trim(line)
+
+       local info = table.foreachi(function_patterns, function (_, pattern)
+               local r, _, l, id, param = string.find(line, pattern)
+               if r ~= nil then
+                       return {
+                               name = id,
+                               private = (l == "local"),
+                               param = util.split("%s*,%s*", param),
+                       }
+               end
+       end)
+
+       -- TODO: remove these assert's?
+       if info ~= nil then
+               assert(info.name, "function name undefined")
+               assert(info.param, string.format("undefined parameter list for function `%s'", info.name))
+       end
+
+       return info
+end
+
+-------------------------------------------------------------------------------
+-- Checks if the line contains a module definition.
+-- @param line string with line text
+-- @param currentmodule module already found, if any
+-- @return the name of the defined module, or nil if there is no module 
+-- definition
+
+local function check_module (line, currentmodule)
+       line = util.trim(line)
+       
+       -- module"x.y"
+       -- module'x.y'
+       -- module[[x.y]]
+       -- module("x.y")
+       -- module('x.y')
+       -- module([[x.y]])
+       -- module(...)
+
+       local r, _, modulename = string.find(line, "^module%s*[%s\"'(%[]+([^,\"')%]]+)")
+       if r then
+               -- found module definition
+               logger:debug(string.format("found module `%s'", modulename))
+               return modulename
+       end
+       return currentmodule
+end
+
+-------------------------------------------------------------------------------
+-- Extracts summary information from a description. The first sentence of each 
+-- doc comment should be a summary sentence, containing a concise but complete 
+-- description of the item. It is important to write crisp and informative 
+-- initial sentences that can stand on their own
+-- @param description text with item description
+-- @return summary string or nil if description is nil
+
+local function parse_summary (description)
+       -- summary is never nil...
+       description = description or ""
+       
+       -- append an " " at the end to make the pattern work in all cases
+       description = description.." "
+
+       -- read until the first period followed by a space or tab       
+       local summary = string.match(description, "(.-%.)[%s\t]")
+       
+       -- if pattern did not find the first sentence, summary is the whole description
+       summary = summary or description
+       
+       return summary
+end
+
+-------------------------------------------------------------------------------
+-- @param f file handle
+-- @param line current line being parsed
+-- @param modulename module already found, if any
+-- @return current line
+-- @return code block
+-- @return modulename if found
+
+local function parse_code (f, line, modulename)
+       local code = {}
+       while line ~= nil do
+               if string.find(line, "^[\t ]*%-%-%-") then
+                       -- reached another luadoc block, end this parsing
+                       return line, code, modulename
+               else
+                       -- look for a module definition
+                       modulename = check_module(line, modulename)
+
+                       table.insert(code, line)
+                       line = f:read()
+               end
+       end
+       -- reached end of file
+       return line, code, modulename
+end
+
+-------------------------------------------------------------------------------
+-- Parses the information inside a block comment
+-- @param block block with comment field
+-- @return block parameter
+
+local function parse_comment (block, first_line)
+
+       -- get the first non-empty line of code
+       local code = table.foreachi(block.code, function(_, line)
+               if not util.line_empty(line) then
+                       -- `local' declarations are ignored in two cases:
+                       -- when the `nolocals' option is turned on; and
+                       -- when the first block of a file is parsed (this is
+                       --      necessary to avoid confusion between the top
+                       --      local declarations and the `module' definition.
+                       if (options.nolocals or first_line) and line:find"^%s*local" then
+                               return
+                       end
+                       return line
+               end
+       end)
+       
+       -- parse first line of code
+       if code ~= nil then
+               local func_info = check_function(code)
+               local module_name = check_module(code)
+               if func_info then
+                       block.class = "function"
+                       block.name = func_info.name
+                       block.param = func_info.param
+                       block.private = func_info.private
+               elseif module_name then
+                       block.class = "module"
+                       block.name = module_name
+                       block.param = {}
+               else
+                       block.param = {}
+               end
+       else
+               -- TODO: comment without any code. Does this means we are dealing
+               -- with a file comment?
+       end
+
+       -- parse @ tags
+       local currenttag = "description"
+       local currenttext
+       
+       table.foreachi(block.comment, function (_, line)
+               line = util.trim_comment(line)
+               
+               local r, _, tag, text = string.find(line, "@([_%w%.]+)%s+(.*)")
+               if r ~= nil then
+                       -- found new tag, add previous one, and start a new one
+                       -- TODO: what to do with invalid tags? issue an error? or log a warning?
+                       tags.handle(currenttag, block, currenttext)
+                       
+                       currenttag = tag
+                       currenttext = text
+               else
+                       currenttext = util.concat(currenttext, line)
+                       assert(string.sub(currenttext, 1, 1) ~= " ", string.format("`%s', `%s'", currenttext, line))
+               end
+       end)
+       tags.handle(currenttag, block, currenttext)
+
+       -- extracts summary information from the description
+       block.summary = parse_summary(block.description)
+       assert(string.sub(block.description, 1, 1) ~= " ", string.format("`%s'", block.description))
+       
+       return block
+end
+
+-------------------------------------------------------------------------------
+-- Parses a block of comment, started with ---. Read until the next block of
+-- comment.
+-- @param f file handle
+-- @param line being parsed
+-- @param modulename module already found, if any
+-- @return line
+-- @return block parsed
+-- @return modulename if found
+
+local function parse_block (f, line, modulename, first)
+       local block = {
+               comment = {},
+               code = {},
+       }
+
+       while line ~= nil do
+               if string.find(line, "^[\t ]*%-%-") == nil then
+                       -- reached end of comment, read the code below it
+                       -- TODO: allow empty lines
+                       line, block.code, modulename = parse_code(f, line, modulename)
+                       
+                       -- parse information in block comment
+                       block = parse_comment(block, first)
+
+                       return line, block, modulename
+               else
+                       table.insert(block.comment, line)
+                       line = f:read()
+               end
+       end
+       -- reached end of file
+       
+       -- parse information in block comment
+       block = parse_comment(block, first)
+       
+       return line, block, modulename
+end
+
+-------------------------------------------------------------------------------
+-- Parses a file documented following luadoc format.
+-- @param filepath full path of file to parse
+-- @param doc table with documentation
+-- @return table with documentation
+
+function parse_file (filepath, doc)
+       local blocks = {}
+       local modulename = nil
+       
+       -- read each line
+       local f = io.open(filepath, "r")
+       local i = 1
+       local line = f:read()
+       local first = true
+       while line ~= nil do
+               if string.find(line, "^[\t ]*%-%-%-") then
+                       -- reached a luadoc block
+                       local block
+                       line, block, modulename = parse_block(f, line, modulename, first)
+                       table.insert(blocks, block)
+               else
+                       -- look for a module definition
+                       modulename = check_module(line, modulename)
+                       
+                       -- TODO: keep beginning of file somewhere
+                       
+                       line = f:read()
+               end
+               first = false
+               i = i + 1
+       end
+       f:close()
+       -- store blocks in file hierarchy
+       assert(doc.files[filepath] == nil, string.format("doc for file `%s' already defined", filepath))
+       table.insert(doc.files, filepath)
+       doc.files[filepath] = {
+               type = "file",
+               name = filepath,
+               doc = blocks,
+--             functions = class_iterator(blocks, "function"),
+--             tables = class_iterator(blocks, "table"),
+       }
+--
+       local first = doc.files[filepath].doc[1]
+       if first and modulename then
+               doc.files[filepath].author = first.author
+               doc.files[filepath].copyright = first.copyright
+               doc.files[filepath].description = first.description
+               doc.files[filepath].release = first.release
+               doc.files[filepath].summary = first.summary
+       end
+
+       -- if module definition is found, store in module hierarchy
+       if modulename ~= nil then
+               if modulename == "..." then
+                               modulename = string.gsub (filepath, "%.lua$", "")
+                               modulename = string.gsub (modulename, "/", ".")
+               end
+               if doc.modules[modulename] ~= nil then
+                       -- module is already defined, just add the blocks
+                       table.foreachi(blocks, function (_, v)
+                               table.insert(doc.modules[modulename].doc, v)
+                       end)
+               else
+                       -- TODO: put this in a different module
+                       table.insert(doc.modules, modulename)
+                       doc.modules[modulename] = {
+                               type = "module",
+                               name = modulename,
+                               doc = blocks,
+--                             functions = class_iterator(blocks, "function"),
+--                             tables = class_iterator(blocks, "table"),
+                               author = first and first.author,
+                               copyright = first and first.copyright,
+                               description = "",
+                               release = first and first.release,
+                               summary = "",
+                       }
+                       
+                       -- find module description
+                       for m in class_iterator(blocks, "module")() do
+                               doc.modules[modulename].description = util.concat(
+                                       doc.modules[modulename].description, 
+                                       m.description)
+                               doc.modules[modulename].summary = util.concat(
+                                       doc.modules[modulename].summary, 
+                                       m.summary)
+                               if m.author then
+                                       doc.modules[modulename].author = m.author
+                               end
+                               if m.copyright then
+                                       doc.modules[modulename].copyright = m.copyright
+                               end
+                               if m.release then
+                                       doc.modules[modulename].release = m.release
+                               end
+                               if m.name then
+                                       doc.modules[modulename].name = m.name
+                               end
+                       end
+                       doc.modules[modulename].description = doc.modules[modulename].description or (first and first.description) or ""
+                       doc.modules[modulename].summary = doc.modules[modulename].summary or (first and first.summary) or ""
+               end
+               
+               -- make functions table
+               doc.modules[modulename].functions = {}
+               for f in class_iterator(blocks, "function")() do
+                       if f and f.name then
+                               table.insert(doc.modules[modulename].functions, f.name)
+                               doc.modules[modulename].functions[f.name] = f
+                       end
+               end
+               
+               -- make tables table
+               doc.modules[modulename].tables = {}
+               for t in class_iterator(blocks, "table")() do
+                       if t and t.name then
+                               table.insert(doc.modules[modulename].tables, t.name)
+                               doc.modules[modulename].tables[t.name] = t
+                       end
+               end
+       end
+       
+       -- make functions table
+       doc.files[filepath].functions = {}
+       for f in class_iterator(blocks, "function")() do
+               if f and f.name then
+                       table.insert(doc.files[filepath].functions, f.name)
+                       doc.files[filepath].functions[f.name] = f
+               end
+       end
+       
+       -- make tables table
+       doc.files[filepath].tables = {}
+       for t in class_iterator(blocks, "table")() do
+               if t and t.name then
+                       table.insert(doc.files[filepath].tables, t.name)
+                       doc.files[filepath].tables[t.name] = t
+               end
+       end
+       
+       return doc
+end
+
+-------------------------------------------------------------------------------
+-- Checks if the file is terminated by ".lua" or ".luadoc" and calls the 
+-- function that does the actual parsing
+-- @param filepath full path of the file to parse
+-- @param doc table with documentation
+-- @return table with documentation
+-- @see parse_file
+
+function file (filepath, doc)
+       local patterns = { "%.lua$", "%.luadoc$" }
+       local valid = table.foreachi(patterns, function (_, pattern)
+               if string.find(filepath, pattern) ~= nil then
+                       return true
+               end
+       end)
+       
+       if valid then
+               logger:info(string.format("processing file `%s'", filepath))
+               doc = parse_file(filepath, doc)
+       end
+       
+       return doc
+end
+
+-------------------------------------------------------------------------------
+-- Recursively iterates through a directory, parsing each file
+-- @param path directory to search
+-- @param doc table with documentation
+-- @return table with documentation
+
+function directory (path, doc)
+       for f in lfs.dir(path) do
+               local fullpath = path .. "/" .. f
+               local attr = lfs.attributes(fullpath)
+               assert(attr, string.format("error stating file `%s'", fullpath))
+               
+               if attr.mode == "file" then
+                       doc = file(fullpath, doc)
+               elseif attr.mode == "directory" and f ~= "." and f ~= ".." then
+                       doc = directory(fullpath, doc)
+               end
+       end
+       return doc
+end
+
+-- Recursively sorts the documentation table
+local function recsort (tab)
+       table.sort (tab)
+       -- sort list of functions by name alphabetically
+       for f, doc in pairs(tab) do
+               if doc.functions then
+                       table.sort(doc.functions)
+               end
+               if doc.tables then
+                       table.sort(doc.tables)
+               end
+       end
+end
+
+-------------------------------------------------------------------------------
+
+function start (files, doc)
+       assert(files, "file list not specified")
+       
+       -- Create an empty document, or use the given one
+       doc = doc or {
+               files = {},
+               modules = {},
+       }
+       assert(doc.files, "undefined `files' field")
+       assert(doc.modules, "undefined `modules' field")
+       
+       table.foreachi(files, function (_, path)
+               local attr = lfs.attributes(path)
+               assert(attr, string.format("error stating path `%s'", path))
+               
+               if attr.mode == "file" then
+                       doc = file(path, doc)
+               elseif attr.mode == "directory" then
+                       doc = directory(path, doc)
+               end
+       end)
+       
+       -- order arrays alphabetically
+       recsort(doc.files)
+       recsort(doc.modules)
+
+       return doc
+end
diff --git a/contrib/luadoc/lua/luadoc/taglet/standard/tags.lua b/contrib/luadoc/lua/luadoc/taglet/standard/tags.lua
new file mode 100644 (file)
index 0000000..980514c
--- /dev/null
@@ -0,0 +1,171 @@
+-------------------------------------------------------------------------------
+-- Handlers for several tags
+-- @release $Id: tags.lua,v 1.8 2007/09/05 12:39:09 tomas Exp $
+-------------------------------------------------------------------------------
+
+local luadoc = require "luadoc"
+local util = require "luadoc.util"
+local string = require "string"
+local table = require "table"
+local assert, type, tostring = assert, type, tostring
+
+module "luadoc.taglet.standard.tags"
+
+-------------------------------------------------------------------------------
+
+local function author (tag, block, text)
+       block[tag] = block[tag] or {}
+       if not text then
+               luadoc.logger:warn("author `name' not defined [["..text.."]]: skipping")
+               return
+       end
+       table.insert (block[tag], text)
+end
+
+-------------------------------------------------------------------------------
+-- Set the class of a comment block. Classes can be "module", "function", 
+-- "table". The first two classes are automatic, extracted from the source code
+
+local function class (tag, block, text)
+       block[tag] = text
+end
+
+-------------------------------------------------------------------------------
+
+local function copyright (tag, block, text)
+       block[tag] = text
+end
+
+-------------------------------------------------------------------------------
+
+local function description (tag, block, text)
+       block[tag] = text
+end
+
+-------------------------------------------------------------------------------
+
+local function field (tag, block, text)
+       if block["class"] ~= "table" then
+               luadoc.logger:warn("documenting `field' for block that is not a `table'")
+       end
+       block[tag] = block[tag] or {}
+
+       local _, _, name, desc = string.find(text, "^([_%w%.]+)%s+(.*)")
+       assert(name, "field name not defined")
+       
+       table.insert(block[tag], name)
+       block[tag][name] = desc
+end
+
+-------------------------------------------------------------------------------
+-- Set the name of the comment block. If the block already has a name, issue
+-- an error and do not change the previous value
+
+local function name (tag, block, text)
+       if block[tag] and block[tag] ~= text then
+               luadoc.logger:error(string.format("block name conflict: `%s' -> `%s'", block[tag], text))
+       end
+       
+       block[tag] = text
+end
+
+-------------------------------------------------------------------------------
+-- Processes a parameter documentation.
+-- @param tag String with the name of the tag (it must be "param" always).
+-- @param block Table with previous information about the block.
+-- @param text String with the current line beeing processed.
+
+local function param (tag, block, text)
+       block[tag] = block[tag] or {}
+       -- TODO: make this pattern more flexible, accepting empty descriptions
+       local _, _, name, desc = string.find(text, "^([_%w%.]+)%s+(.*)")
+       if not name then
+               luadoc.logger:warn("parameter `name' not defined [["..text.."]]: skipping")
+               return
+       end
+       local i = table.foreachi(block[tag], function (i, v)
+               if v == name then
+                       return i
+               end
+       end)
+       if i == nil then
+               luadoc.logger:warn(string.format("documenting undefined parameter `%s'", name))
+               table.insert(block[tag], name)
+       end
+       block[tag][name] = desc
+end
+
+-------------------------------------------------------------------------------
+
+local function release (tag, block, text)
+       block[tag] = text
+end
+
+-------------------------------------------------------------------------------
+
+local function ret (tag, block, text)
+       tag = "ret"
+       if type(block[tag]) == "string" then
+               block[tag] = { block[tag], text }
+       elseif type(block[tag]) == "table" then
+               table.insert(block[tag], text)
+       else
+               block[tag] = text
+       end
+end
+
+-------------------------------------------------------------------------------
+-- @see ret
+
+local function see (tag, block, text)
+       -- see is always an array
+       block[tag] = block[tag] or {}
+       
+       -- remove trailing "."
+       text = string.gsub(text, "(.*)%.$", "%1")
+       
+       local s = util.split("%s*,%s*", text)                   
+       
+       table.foreachi(s, function (_, v)
+               table.insert(block[tag], v)
+       end)
+end
+
+-------------------------------------------------------------------------------
+-- @see ret
+
+local function usage (tag, block, text)
+       if type(block[tag]) == "string" then
+               block[tag] = { block[tag], text }
+       elseif type(block[tag]) == "table" then
+               table.insert(block[tag], text)
+       else
+               block[tag] = text
+       end
+end
+
+-------------------------------------------------------------------------------
+
+local handlers = {}
+handlers["author"] = author
+handlers["class"] = class
+handlers["copyright"] = copyright
+handlers["description"] = description
+handlers["field"] = field
+handlers["name"] = name
+handlers["param"] = param
+handlers["release"] = release
+handlers["return"] = ret
+handlers["see"] = see
+handlers["usage"] = usage
+
+-------------------------------------------------------------------------------
+
+function handle (tag, block, text)
+       if not handlers[tag] then
+               luadoc.logger:error(string.format("undefined handler for tag `%s'", tag))
+               return
+       end
+--     assert(handlers[tag], string.format("undefined handler for tag `%s'", tag))
+       return handlers[tag](tag, block, text)
+end
diff --git a/contrib/luadoc/lua/luadoc/util.lua b/contrib/luadoc/lua/luadoc/util.lua
new file mode 100644 (file)
index 0000000..e51e2e5
--- /dev/null
@@ -0,0 +1,201 @@
+-------------------------------------------------------------------------------
+-- General utilities.
+-- @release $Id: util.lua,v 1.16 2008/02/17 06:42:51 jasonsantos Exp $
+-------------------------------------------------------------------------------
+
+local lfs = require "lfs"
+local type, table, string, io, assert, tostring, setmetatable, pcall = type, table, string, io, assert, tostring, setmetatable, pcall
+
+-------------------------------------------------------------------------------
+-- Module with several utilities that could not fit in a specific module
+
+module "luadoc.util"
+
+-------------------------------------------------------------------------------
+-- Removes spaces from the begining and end of a given string
+-- @param s string to be trimmed
+-- @return trimmed string
+
+function trim (s)
+       return (string.gsub(s, "^%s*(.-)%s*$", "%1"))
+end
+
+-------------------------------------------------------------------------------
+-- Removes spaces from the begining and end of a given string, considering the
+-- string is inside a lua comment.
+-- @param s string to be trimmed
+-- @return trimmed string
+-- @see trim
+-- @see string.gsub
+
+function trim_comment (s)
+       s = string.gsub(s, "%-%-+(.*)$", "%1")
+       return trim(s)
+end
+
+-------------------------------------------------------------------------------
+-- Checks if a given line is empty
+-- @param line string with a line
+-- @return true if line is empty, false otherwise
+
+function line_empty (line)
+       return (string.len(trim(line)) == 0)
+end
+
+-------------------------------------------------------------------------------
+-- Appends two string, but if the first one is nil, use to second one
+-- @param str1 first string, can be nil
+-- @param str2 second string
+-- @return str1 .. " " .. str2, or str2 if str1 is nil
+
+function concat (str1, str2)
+       if str1 == nil or string.len(str1) == 0 then
+               return str2
+       else
+               return str1 .. " " .. str2
+       end
+end
+
+-------------------------------------------------------------------------------
+-- Split text into a list consisting of the strings in text,
+-- separated by strings matching delim (which may be a pattern). 
+-- @param delim if delim is "" then action is the same as %s+ except that 
+-- field 1 may be preceeded by leading whitespace
+-- @usage split(",%s*", "Anna, Bob, Charlie,Dolores")
+-- @usage split(""," x y") gives {"x","y"}
+-- @usage split("%s+"," x y") gives {"", "x","y"}
+-- @return array with strings
+-- @see table.concat
+
+function split(delim, text)
+       local list = {}
+       if string.len(text) > 0 then
+               delim = delim or ""
+               local pos = 1
+               -- if delim matches empty string then it would give an endless loop
+               if string.find("", delim, 1) and delim ~= "" then 
+                       error("delim matches empty string!")
+               end
+               local first, last
+               while 1 do
+                       if delim ~= "" then 
+                               first, last = string.find(text, delim, pos)
+                       else
+                               first, last = string.find(text, "%s+", pos)
+                               if first == 1 then
+                                       pos = last+1
+                                       first, last = string.find(text, "%s+", pos)
+                               end
+                       end
+                       if first then -- found?
+                               table.insert(list, string.sub(text, pos, first-1))
+                               pos = last+1
+                       else
+                               table.insert(list, string.sub(text, pos))
+                               break
+                       end
+               end
+       end
+       return list
+end
+
+-------------------------------------------------------------------------------
+-- Comments a paragraph.
+-- @param text text to comment with "--", may contain several lines
+-- @return commented text
+
+function comment (text)
+       text = string.gsub(text, "\n", "\n-- ")
+       return "-- " .. text
+end
+
+-------------------------------------------------------------------------------
+-- Wrap a string into a paragraph.
+-- @param s string to wrap
+-- @param w width to wrap to [80]
+-- @param i1 indent of first line [0]
+-- @param i2 indent of subsequent lines [0]
+-- @return wrapped paragraph
+
+function wrap(s, w, i1, i2)
+       w = w or 80
+       i1 = i1 or 0
+       i2 = i2 or 0
+       assert(i1 < w and i2 < w, "the indents must be less than the line width")
+       s = string.rep(" ", i1) .. s
+       local lstart, len = 1, string.len(s)
+       while len - lstart > w do
+               local i = lstart + w
+               while i > lstart and string.sub(s, i, i) ~= " " do i = i - 1 end
+               local j = i
+               while j > lstart and string.sub(s, j, j) == " " do j = j - 1 end
+               s = string.sub(s, 1, j) .. "\n" .. string.rep(" ", i2) ..
+                       string.sub(s, i + 1, -1)
+               local change = i2 + 1 - (i - j)
+               lstart = j + change
+               len = len + change
+       end
+       return s
+end
+
+-------------------------------------------------------------------------------
+-- Opens a file, creating the directories if necessary
+-- @param filename full path of the file to open (or create)
+-- @param mode mode of opening
+-- @return file handle
+
+function lfs.open (filename, mode)
+       local f = io.open(filename, mode)
+       if f == nil then
+               filename = string.gsub(filename, "\\", "/")
+               local dir = ""
+               for d in string.gfind(filename, ".-/") do
+                       dir = dir .. d
+                       lfs.mkdir(dir)
+               end
+               f = io.open(filename, mode)
+       end
+       return f
+end
+
+
+----------------------------------------------------------------------------------
+-- Creates a Logger with LuaLogging, if present. Otherwise, creates a mock logger.
+-- @param options a table with options for the logging mechanism
+-- @return logger object that will implement log methods
+
+function loadlogengine(options)
+       local logenabled = pcall(function()
+               require "logging"
+               require "logging.console"
+       end)
+       
+       local logging = logenabled and logging
+       
+       if logenabled then
+               if options.filelog then
+                       logger = logging.file("luadoc.log") -- use this to get a file log
+               else
+                       logger = logging.console("[%level] %message\n")
+               end
+       
+               if options.verbose then
+                       logger:setLevel(logging.INFO)
+               else
+                       logger:setLevel(logging.WARN)
+               end
+               
+       else
+               noop = {__index=function(...)
+                       return function(...)
+                               -- noop
+                       end
+               end}
+               
+               logger = {} 
+               setmetatable(logger, noop)
+       end
+       
+       return logger
+end
+