--- /dev/null
+LDoc License\r
+-----------\r
+Copyright (C) 2011 Steve Donovan.\r
+\r
+Permission is hereby granted, free of charge, to any person obtaining a copy\r
+of this software and associated documentation files (the "Software"), to deal\r
+in the Software without restriction, including without limitation the rights\r
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+copies of the Software, and to permit persons to whom the Software is\r
+furnished to do so, subject to the following conditions:\r
+\r
+The above copyright notice and this permission notice shall be included in\r
+all copies or substantial portions of the Software.\r
+\r
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
+THE SOFTWARE.\r
+\r
--- /dev/null
+project='LDoc'\r
+title='LDoc documentation'\r
+description='A Lua documentation tool'\r
+format='discount'\r
+file='ldoc.lua'\r
+dir='out'\r
+readme='docs/doc.md'\r
--- /dev/null
+# LDoc, a Lua Documentation Tool
+
+## Introduction
+
+LDoc is a second-generation documentation tool that can be used as a replacement for [LuaDoc](http://keplerproject.github.com/luadoc/). It arose out of my need to document my own projects and only depends on the [Penlight](https://github.com/stevedonovan/Penlight) libraries.
+
+It is mostly compatible with LuaDoc, except that certain workarounds are no longer needed. For instance, it is not so married to the idea that Lua modules should be defined using the `module` function; this is not only a matter of taste since this has been deprecated in Lua 5.2.
+
+Otherwise, the output is very similar, which is no accident since the HTML templates are based directly on LuaDoc. You can ship your own customized templates and style sheets with your [own project](http://nilnor.github.com/textui/docs/), however. You have an option to use Markdown to process the documentation, which means no ugly HTML is needed in doc comments. C/C++ extension modules may be documented in a similar way, although function names cannot be inferred from the code itself.
+
+LDoc can provide integrated documentation, with traditional function comments, any documents in Markdown format, and specified source examples. Lua source in examples and the documents will be prettified.
+
+Although there are a fair number of command-line options, the preferred route is to write a `config.ld` configuration file in Lua format. By convention, if LDoc is simply invoked as `ldoc .` it will read this file first. In this way, the aim is to make it very easy for end-users to build your documentation using this simple command.
+
+## Commenting Conventions
+
+LDoc follows the conventions established by Javadoc and later by LuaDoc.
+
+Only 'doc comments' are parsed; these can be started with at least 3 hyphens, or by a empty comment line with at least 3 hypens:
+
+ --- summary.
+ -- Description; this can extend over
+ -- several lines
+
+ -----------------
+ -- This will also do.
+
+You can also use Lua block comments:
+
+ --[[--
+ Summary. A description
+ ...;
+ ]]
+
+Any module or script must start with a doc comment; any other files are ignored and a warning issued. The only exception is if the module starts with an explicit `module` statement.
+
+All doc comments start with a summary sentence, that ends with a period or a question mark. An optional description may follow. Normally the summary sentence will appear in the module contents.
+
+After this descriptive text, there will typically be _tags_. These follow the convention established by Javadoc and widely used in tools for other languages.
+
+ --- foo explodes text.
+ -- It is a specialized splitting operation on a string.
+ -- @param text the string
+ -- @return a table of substrings
+ function foo (text)
+ ....
+ end
+
+There are also 'tparam' and 'treturn' which let you [specify a type](#Tag_Modifiers):
+
+ -- @tparam string text the string
+ -- @treturn {string,...} a table of substrings
+
+There may be multiple 'param' tags, which should document each formal parameter of the function. For Lua, there can also be multiple 'return' tags
+
+ --- solvers for common equations.
+ module("solvers", package.seeall)
+
+ --- solve a quadratic equation.
+ -- @param a first coeff
+ -- @param b second coeff
+ -- @param c third coeff
+ -- @return first root, or nil
+ -- @return second root, or imaginary root error
+ function solve (a,b,c)
+ local disc = b^2 - 4*a*c
+ if disc < 0 then
+ return nil,"imaginary roots"
+ else
+ disc = math.sqrt(disc)
+ return (-b + disc)/2*a,
+ (-b - disc)/2*a
+ end
+ end
+
+ ...
+
+This is the common module style used in Lua 5.1, but it's increasingly common to see less 'magic' ways of creating modules in Lua. Since `module` is deprecated in Lua 5.2, any future-proof documentation tool needs to handle these styles gracefully:
+
+ --- a test module
+ -- @module test
+
+ local test = {}
+
+ --- first test.
+ function test.one()
+ ...
+ end
+
+ ...
+
+ return test
+
+Here the name of the module is explicitly given using the 'module' tag. If you leave this out, then LDoc will infer the name of the module from the name of the file and its relative location in the filesystem; this logic is also used for the `module(...)` idiom. (How this works and when you need to provide extra information is discussed later.)
+
+It is common to use a local name for a module when declaring its contents. In this case the 'alias' tag can tell LDoc that these functions do belong to the module:
+
+ --- another test.
+ -- @module test2
+ -- @alias M
+
+ local M = {}
+
+ -- first test.
+ function M.one()
+ ..
+ end
+
+ return M
+
+`M` and `_M` are used commonly enough that LDoc will recognize them as aliases automatically, but 'alias' allows you to use any identifier.
+
+LDoc tries to deduce the function name and the formal parameter names from examining the code after the doc comment. It also recognizes the 'unsugared' way of defining functions as explicit assignment to a variable:
+
+ --- second test.
+ M.two = function(...) ... end
+
+Apart from exported functions, a module usually contains local functions. By default, LDoc does not include these in the documentation, but they can be enabled using the `--all` flag. They can be documented just like 'public' functions:
+
+ --- it's clear that boo is local from context.
+ local function boo(...) .. end
+
+ local foo
+
+ --- we need to give a hint here for foo
+ -- @local here
+ function foo(...) .. end
+
+Modules can of course export tables and other values. The classic way to document a table looks like this:
+
+ --- a useful table of constants
+ -- @field alpha first correction
+ -- @field beta second correction
+ -- @field gamma fudge factor
+ -- @table constants
+
+Here the kind of item is made explicit by the 'table' tag; tables have 'fields' in the same way as functions have parameters.
+
+This can get tedious, so LDoc will attempt to extract table documentation from code:
+
+ --- a useful table of constants
+ M.constants = {
+ alpha = 0.23, -- first correction
+ beta = 0.443, -- second correction
+ gamma = 0.01 -- fudge factor
+ }
+
+The rule followed here is `NAME = <table-constructor>`. If LDoc can't work out the name and type from the following code, then a warning will be issued, pointing to the file and location.
+
+Another kind of module-level type is 'field', such as follows:
+
+ --- module version.
+ M._VERSION = '0.5'
+
+That is, a module may contain exported functions, local functions, tables and fields.
+
+When the code analysis would lead to the wrong type, you can always be explicit.
+
+ --- module contents.
+ -- @field _CONTENTS
+ M._CONTENTS = {constants=true,one=true,...}
+
+The order of tags is not important, but as always, consistency is useful. Tags like 'param' and 'return' can be specified multiple times, whereas a type tag like 'function' can only occur once in a comment. The basic rule is that a single doc comment can only document one entity.
+
+By default, LDoc will process any file ending in '.lua' or '.luadoc' in a specified directory; you may point it to a single file as well. A 'project' usually consists of many modules in one or more _packages_. The generated `index.html` will point to the generated documentation for each of these modules.
+
+If only one module or script is documented for a project, then the `index.html` generated contains the documentation for that module, since an index pointing to one module would be redundant.
+
+(If you want to document a script, there is a project-level type 'script' for that.)
+
+## See References
+
+The tag 'see' is used to reference other parts of the documentation, and 'usage' can provide examples of use:
+
+ ---------
+ -- split a string in two.
+ -- @param s the string
+ -- @param delim the delimiter (default space)
+ -- @return first part
+ -- @return second part
+ -- @usage local hello,world = split2("hello world")
+ -- @see split
+ funtion split2(s,delim) .. end
+
+Here it's assumed that 'split' is a function defined in the same module. If you wish to link to a function in another module, then the reference has to be qualified.
+
+References to methods use a colon: `myclass:method`; this is for instance how you would refer to members of a `@type` section.
+
+The example at `tests/complex` shows how @see references are interpreted:
+
+ complex.util.parse
+ complex.convert.basic
+ complex.util
+ complex.display
+ complex
+
+You may of course use the full name of a module or function, but can omit the top-level namespace - e.g. can refer to the module `util` and the function `display.display_that` directly. Within a module, you can directly use a function name, e.g. in `display` you can say `display_this`.
+
+What applies to functions also applies to any module-level item like tables. New module-level items can be defined and they will work according to these rules.
+
+If a reference is not found within the project, LDoc checks to see if it is a reference to a Lua standard function or table, and links to the online Lua manual. So references like 'table.concat' are handled sensibly.
+
+References may be made inline using the @\{ref} syntax. This may appear anywhere in the text, and is more flexible than @see. In particular, it provides one way to document the type of a parameter or return value when that type has a particular structure:
+
+ ------
+ -- extract standard variables.
+ -- @param s the string
+ -- @return @\{stdvars}
+ function extract_std(s) ... end
+
+ ------
+ -- standard variables.
+ -- Use @\{extract_std} to parse a string containing variables,
+ -- and @\{pack_std} to make such a string.
+ -- @field length
+ -- @field duration
+ -- @field viscosity
+ -- @table stdvars
+
+@\{ref} is very useful for referencing your API from code samples and readme text. (I've had to throw in a spurious backspace to stop expansion in this example.)
+
+The link text can be changed from the default by the extended syntax @\{ref|text}.
+
+You can also put references in backticks, like `\`stdvars\``. This is commonly used in Markdown to indicate code, so it comes naturally when writing documents. It is controlled by the configuration variable `backtick_references`; the default is `true` if you use Markdown in your project, but can be specified explicitly in your `config.ld`.
+
+## Sections
+
+LDoc supports _explicit_ sections. By default, the sections correspond to the pre-existing types in a module: 'Functions', 'Tables' and 'Fields' (There is another default section 'Local Functions' which only appears if LDoc is invoked with the `--all` flag.) But new sections can be added; the first mechanism is when you define a new type (say 'macro') a new section ('Macros') is created to contain these types. There is also a way to declare ad-hoc sections using the `@section` tag.
+
+The need occurs when a module has a lot of functions that need to be put into logical sections.
+
+ --- File functions.
+ -- Useful utilities for opening foobar format files.
+ -- @section file
+
+ --- open a file
+ ...
+
+ --- read a file
+ ...
+
+ --- Encoding operations.
+ -- Encoding foobar output in different ways.
+ -- @section encoding
+
+ ...
+
+A section doc-comment has the same structure as a normal doc-comment; the summary is used as the new section title, and the description will be output at the start of the function details for that section.
+
+In any case, sections appear under 'Contents' on the left-hand side. See the [winapi](http://stevedonovan.github.com/winapi/api.html) documentation for an example of how this looks.
+
+Arguably a module writer should not write such very long modules, but it is not the job of the documentation tool to limit the programmer!
+
+A specialized kind of section is `type`: it is used for documenting classes. The functions (or fields) within a type section are considered to be the methods of that class.
+
+ --- A File class.
+ -- @type File
+
+ ....
+ --- get the modification time.
+ -- @return standard time since epoch
+ function File:mtime()
+ ...
+ end
+
+(In an ideal world, we would use the word 'class' instead of 'type', but this would conflict with the LuaDoc usage.)
+
+A section continues until the next section is found, `@section end`, or end of file.
+
+
+
+## Differences from LuaDoc
+
+LDoc only does 'module' documentation, so the idea of 'files' is redundant.
+
+One added convenience is that it is easier to name entities:
+
+ ------------
+ -- a simple module.
+ -- (LuaDoc)
+ -- @class module
+ -- @name simple
+
+ ------------
+ -- a simple module.
+ -- (LDoc)
+ -- @module simple
+
+This is because type names (like 'function', 'module', 'table', etc) can function as tags. LDoc also provides a means to add new types (e.g. 'macro') using a configuration file which can be shipped with the source. If you become bored with typing 'param' repeatedly then you can define an alias for it, such as 'p'. This can also be specified in the configuration file.
+
+LDoc will also work with C/C++ files, since extension writers clearly have the same documentation needs as Lua module writers.
+
+LDoc gives the documenter the option to use Markdown to parse the contents of comments.
+
+## Adding new Tags
+
+LDoc tries to be faithful to LuaDoc, but provides some extensions. Aliases for tags can be defined, and new types declared.
+
+ --- zero function. Two new ldoc features here; item types
+ -- can be used directly as tags, and aliases for tags
+ -- can be defined in config.ld.
+ -- @function zero_fun
+ -- @p k1 first
+ -- @p k2 second
+
+Here an alias for 'param' has been defined. If a file `config.ld` is found in the source, then it will be loaded as Lua data. For example, the configuration for the above module provides a title and defines an alias for 'param':
+
+ title = "testmod docs"
+ project = "testmod"
+ alias("p","param")
+
+Extra tag _types_ can be defined:
+
+ new_type("macro","Macros")
+
+And then used as any other type:
+
+ -----
+ -- A useful macro. This is an example of a custom type.
+ -- @macro first_macro
+ -- @see second_function
+
+This will also create a new module section called 'Macros'.
+
+## Inferring more from Code
+
+The qualified name of a function will be inferred from any `function` keyword following the doc comment. LDoc goes further with this kind of code analysis, however.
+
+Instead of:
+
+ --- first table.
+ -- @table one
+ -- @field A alpha
+ -- @field B beta
+ M.one = {
+ A = 1,
+ B = 2;
+ }
+
+you can write:
+
+ --- first table
+ -- @table one
+ M.one = {
+ A = 1, -- alpha
+ B = 2; -- beta
+ }
+
+Simularly, function parameter comments can be directly used:
+
+ ------------
+ -- third function. Can also provide parameter comments inline,
+ -- provided they follow this pattern.
+ function mod1.third_function(
+ alpha, -- correction A
+ beta, -- correction B
+ gamma -- factor C
+ )
+ ...
+ end
+
+As always, explicit tags can override this behaviour if it is inappropriate.
+
+## Extension modules written in C
+
+LDoc can process C/C++ files:
+
+ @plain
+ /***
+ Create a table with given array and hash slots.
+ @function createtable
+ @param narr initial array slots, default 0
+ @param nrec initial hash slots, default 0
+ @return the new table
+ */
+ static int l_createtable (lua_State *L) {
+ ....
+
+Both `/**` and `///` are recognized as starting a comment block. Otherwise, the tags are processed in exactly the same way. It is necessary to specify that this is a function with a given name, since this cannot be reliably be inferred from code. Such a file will need a module comment, which is treated exactly as in Lua.
+
+An unknown extension can be associated with a language using a call like `add_language_extension('lc','c')` in `config.ld`. (Currently the language can only be 'c' or 'lua'.)
+
+See 'tests/examples/mylib.c' for the full example.
+
+## Basic Usage
+
+For example, to process all files in the 'lua' directory:
+
+ $ ldoc lua
+ output written to docs/
+
+Thereafter the `docs` directory will contain `index.html` which points to individual modules in the `modules` subdirectory. The `--dir` flag can specify where the output is generated, and will ensure that the directory exists. The output structure is like LuaDoc: there is an `index.html` and the individual modules are in the `modules` subdirectory. This applies to all project-level types, so that you can also get `scripts`, `examples` and `topics` directories.
+
+If your modules use `module(...)` then the module name has to be deduced. If `ldoc` is run from the root of the package, then this deduction does not need any help - e.g. if your package was `foo` then `ldoc foo` will work as expected. If we were actually in the `foo` directory then `ldoc -b .. .` will correctly deduce the module names. An example would be generating documentation for LuaDoc itself:
+
+ $ ldoc -b .. /path/to/luadoc
+
+Without the `-b` setting the base of the package to the _parent_ of the directory, implicit modules like `luadoc.config` will be incorrectly placed in the global namespace.
+
+For new-style modules, that don't use `module()`, it is recommended that the module comment has an explicit `@module PACKAGE.NAME`. If it does not, then `ldoc` will still attempt to deduce the module name, but may need help with `--package/-b` as above.
+
+`format = 'markdown'` can be used in your `config.ld` and will be used to process summaries and descriptions. This requires [markdown.lua](http://www.frykholm.se/files/markdown.lua) by Niklas Frykholm to be installed (this can be most easily done with `luarocks install markdown`.) A much faster alternative is [lua-discount](http://asbradbury.org/projects/lua-discount/) which you can use by setting `format` to 'discount' after installing using `luarocks install lua-discount`) The [discount](http://www.pell.portland.or.us/~orc/Code/discount/) Markdown processor additionally has more features than the pure Lua version, such as PHP-Extra style tables. As a special case, LDoc will fall back to using `markdown.lua` if it cannot find `discount`.
+
+A special case is if you simply say 'ldoc .'. Then there _must_ be a `config.ld` file available in the directory, and it can specify the file:
+
+ file = "mymod.lua"
+ title = "mymod documentation"
+ description = "mymod does some simple but useful things"
+
+`file` can of course point to a directory, just as with the `--file` option. This mode makes it particularly easy for the user to build the documentation, by allowing you to specify everything explicitly in the configuration.
+
+In `config.ld`, `file` may be a Lua table, containing file names or directories; if it has an `exclude` field then that will be used to exclude files from the list, for example `{'examples', exclude = {'examples/slow.lua'}}`.
+
+
+## Processing Single Modules
+
+`--output` can be used to give the output file a different name. This is useful for the special case when a single module file is specified. Here an index would be redundant, so the single HTML file generated contains the module documentation.
+
+ $ ldoc mylib.lua --> results in docs/index.html
+ $ ldoc --output mylib mylib.lua --> results in docs/mylib.html
+ $ ldoc --output mylib --dir html mylib.lua --> results in html/mylib.html
+
+The default sections used by LDoc are 'Functions', 'Tables' and 'Fields', corresponding to the built-in types 'function', 'table' and 'field'. If `config.ld` contains something like `new_type("macro","Macros")` then this adds a new section 'Macros' which contains items of 'macro' type - 'macro' is registered as a new valid tag name. The default template then presents items under their corresponding section titles, in order of definition.
+
+## Getting Help about a Module
+
+There is an option to simply dump the results of parsing modules. Consider the C example `tests/example/mylib.c':
+
+ @plain
+ $ ldoc --dump mylib.c
+ ----
+ module: mylib A sample C extension.
+ Demonstrates using ldoc's C/C++ support. Can either use /// or /*** */ etc.
+
+ function createtable(narr, nrec)
+ Create a table with given array and hash slots.
+ narr initial array slots, default 0
+ nrec initial hash slots, default 0
+
+ function solve(a, b, c)
+ Solve a quadratic equation.
+ a coefficient of x^2
+ b coefficient of x
+ c constant
+ return {"first root","second root"}
+
+This is useful to quickly check for problems; here we see that `createable` did not have a return tag.
+
+LDoc takes this idea of data dumping one step further. If used with the `-m` flag it will look up an installed Lua module and parse it. If it has been marked up in LuaDoc-style then you will get a handy summary of the contents:
+
+ @plain
+ $ ldoc -m pl.pretty
+ ----
+ module: pl.pretty Pretty-printing Lua tables.
+ * read(s) - read a string representation of a Lua table.
+ * write(tbl, space, not_clever) - Create a string representation of a Lua table.
+
+ * dump(t, ...) - Dump a Lua table out to a file or stdout.
+
+You can specify a fully qualified function to get more information:
+
+ @plain
+ $ ldoc -m pl.pretty.write
+
+ function write(tbl, space, not_clever)
+ create a string representation of a Lua table.
+ tbl {table} Table to serialize to a string.
+ space {string} (optional) The indent to use.
+ Defaults to two spaces.
+ not_clever {bool} (optional) Use for plain output, e.g {['key']=1}.
+ Defaults to false.
+
+LDoc knows about the basic Lua libraries, so that it can be used as a handy console reference:
+
+ @plain
+ $> ldoc -m assert
+
+ function assert(v, message)
+ Issues an error when the value of its argument `v` is false (i.e.,
+ nil or false); otherwise, returns all its arguments.
+ `message` is an error
+ message; when absent, it defaults to "assertion failed!"
+ v
+ message
+
+Thanks to mitchell's [TextAdept](http://code.google.com/p/textadept/) project, LDoc has a set of `.luadoc` files for all the standard tables, plus [LuaFileSystem](http://keplerproject.github.com/luafilesystem/) and [LPeg](http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html).
+
+ @plain
+ $> ldoc -m lfs.lock
+
+ function lock(filehandle, mode, start, length)
+ Locks a file or a part of it.
+ This function works on open files; the file
+ handle should be specified as the first argument. The string mode could be
+ either r (for a read/shared lock) or w (for a write/exclusive lock). The
+ optional arguments start and length can be used to specify a starting point
+ and its length; both should be numbers.
+ Returns true if the operation was successful; in case of error, it returns
+ nil plus an error string.
+ filehandle
+ mode
+ start
+ length
+
+## Anatomy of a LDoc-generated Page
+
+[winapi](http://stevedonovan.github.com/winapi/api.html) can be used as a good example of a module that uses extended LDoc features.
+
+The _navigation section_ down the left has several parts:
+
+ - The project name ('project' in the config)
+ - A project description ('description')
+ - ''Contents'' of the current page
+ - ''Modules'' listing all the modules in this project
+
+Note that `description` will be passed through Markdown, if it has been specified for the project. This gives you an opportunity to make lists of links, etc; any '##' headers will be formatted like the other top-level items on the navigation bar.
+
+'Contents' is automatically generated. It will contain any explicit sections, if they have been used. Otherwise you will get the usual categories: 'Functions', 'Tables' and 'Fields'.
+
+'Modules' will appear for any project providing Lua libraries; there may also be a 'Scripts' section if the project contains Lua scripts. For example, [LuaMacro](http://stevedonovan.github.com/LuaMacro/docs/api.html) has a driver script `luam` in this section. The [builtin](http://stevedonovan.github.com/LuaMacro/docs/modules/macro.builtin.html) module only defines macros, which are defined as a _custom tag type_.
+
+The _content section_ on the right shows:
+
+ - The module summary and description
+ - The contents summary, per section as above
+ - The detailed documentation for each item
+
+As before, the description can use Markdown. The summary contains the contents of each section as a table, with links to the details. This is where the difference between an item's summary and an item's description is important; the first will appear in the contents summary. The item details show the item name and its summary again, followed by the description. There are then sections for the following tags: 'param', 'usage', 'return' and 'see' in that order. (For tables, 'Fields' is used instead of 'Parameters' but internally fields of a table are stored as the 'param' tag.)
+
+You can of course customize the default template, but there are some parameters that can control what the template will generate. Setting `one` to `true` in your configuration file will give a _one-column_ layout, which can be easier to use as a programming reference. You can suppress the contents summary with `no_summary`.
+
+## Customizing the Page
+
+Setting `no_return_or_parms` to `true` will suppress the display of 'param' and 'return' tags. This may appeal to programmers who dislike the traditional @tag soup xDoc style and prefer to comment functions just with a description. This is particularly useful when using Markdown in a stylized way to specify arguments:
+
+ ---------
+ -- This extracts the shortest common substring from the strings _s1_ and _s2_
+ function M.common_substring(s1,s2)
+
+Here I've chosen to italicise parameter names; the main thing is to be consistent.
+
+This style is close to the Python [documentation standard](http://docs.python.org/library/array.html#module-array), especially when used with `no_summary`.
+
+It is also very much how the Lua documentation is ordered. For instance, this configuration file formats the built-in documentation for the Lua global functions in a way which is close to the original:
+
+ project = 'Lua'
+ description = 'Lua Standard Libraries'
+ file = {'ldoc/builtin',exclude = {'ldoc/builtin/globals.lua'}}
+ no_summary = true
+ no_return_or_parms = true
+ format = 'discount'
+
+
+Generally, using Markdown gives you the opportunity to structure your documentation in any way you want; particularly if using lua-discount and its [table syntax](http://michelf.com/projects/php-markdown/extra/#table); the desired result can often be achieved then by using a custom style sheet.
+
+## Examples
+
+It has been long known that documentation generated just from the source is not really adequate to explain _how_ to use a library. People like reading narrative documentation, and they like looking at examples. Previously I found myself dealing with source-generated and writer-generated documentation using different tools, and having to match these up.
+
+LDoc allows for source examples to be included in the documentation. For example, see the online documentation for [winapi](http://stevedonovan.github.com/winapi/api.html). The function `utf8_expand` has a `@see` reference to 'testu.lua' and following that link gives you a pretty-printed version of the code.
+
+The line in the `config.ld` that enables this is:
+
+ examples = {'examples', exclude = {'examples/slow.lua'}}
+
+That is, all files in the `examples` folder are to be pretty-printed, except for `slow.lua` which is meant to be called from one of the examples. The see-reference to `testu.lua` resolves to 'examples/testu.lua.html'.
+
+Examples may link back to the API documentation, for instance the example `input.lua` has a @\{spawn_process} inline reference.
+
+## Readme files
+
+Like all good Github projects, Winapi has a `readme.md`:
+
+ readme = "readme.md"
+
+This goes under the 'Topics' global section; the 'Contents' of this document is generated from the second-level (##) headings of the readme.
+
+Readme files are always processed with Markdown, but may also contain @\{} references back to the documentation and to example files. Any symbols within backticks will be expanded as references, if possible. As with doc comments, a link to a standard Lua function like @\{os.execute} will work as well. Any code sections will be pretty-printed as Lua, unless the first indented line is '@plain'. (See the source for this readme to see how it's used.)
+
+Another name for `readme` is `topics`, which is more descriptive. From LDoc 1.2, `readme/topics` can be a list of documents. These act as a top-level table-of-contents for your documentation. Currently, if you want them in a particular order, then use names like `01-introduction.md` etc which sort appropriately.
+
+The first line of a document may be a Markdown `#` title. If so, then LDoc will regard the next level as the subheadings, normally second-level `##`. But if the title is already second-level, then third-level headings will be used `###`, and so forth. The implication is that the first heading must be top-level relative to the headings that follow, and must start at the first line.
+
+A reference like @\{string.upper} is unambiguous, and will refer to the online Lua manual. In a project like Penlight, it can get tedious to have to write out fully qualified names like @\{pl.utils.printf}. The first simplification is to use the `package` field to resolve unknown references, which in this case is 'pl'. (Previously we discussed how `package` is used to tell LDoc where the base package is in cases where the module author wishes to remain vague, but it does double-duty here.) A further level of simplification comes from the @lookup directive in documents, which must start at the first column on its own line. For instance, if I am talking about `pl.utils`, then I can say "@lookup utils" and thereafter references like @\{printf} will resolve correctly.
+
+Remember that the default is for references in backticks to be resolved; unlike @ references, it is not an error if the reference cannot be found.
+
+The _sections_ of a document (the second-level headings) are also references. This particular section can be refered to as @\{doc.md.Resolving_References_in_Documents} - the rule is that any non-alphabetic character is replaced by an underscore.
+
+
+## Tag Modifiers
+
+New with this release is the idea of _tag modifiers_. For instance, you may say @\param[type=number] and this associates the modifier `type` with value `number` with this particular param tag. A shorthand can be introduced for this common case, which is "@tparam <type> <parmname> <comment>"; in the same way @\treturn is defined.
+
+This is useful for larger projects where you want to provide the argument and return value types for your API, in a structured way that can be easily extracted later. There is a useful function for creating new tags that can be used in `config.ld`:
+
+ tparam_alias('string','string')
+
+That is, "@string" will now have the same meaning as "@tparam string".
+
+The exact form of `<type>` is not defined, but here is a suggested scheme:
+
+ number -- a plain type
+ Bonzo -- a known type; a reference link will be generated
+ {string,number} -- a 'list' tuple, built from type expressions
+ {A=string,N=number} -- a 'struct' tuple, ditto
+ {Bonzo,...} -- an array of Bonzo objects
+ {[string]=Bonzo,...} -- a map of Bonzo objects with string keys
+ Array(Bonzo) -- (assuming that Array is a container)
+
+Currently the `type` modifier is the only one known and used by LDoc when generating HTML output. However, any other modifiers are allowed and are available for use with your own templates or for extraction by your own tools.
+
+The `alias` function within configuration files has been extended so that alias tags can be defined as a tag plus a set of modifiers. So `tparam` is defined as:
+
+ alias('tparam',{'param',modifiers={type="$1"}})
+
+## Fields allowed in `config.ld`
+
+These mostly have the same meaning as the corresponding parameters:
+
+ - `file` a file or directory containing sources. In `config.ld` this can also be a table of files and directories.
+ - `project` name of project, used as title in top left
+ - `title` page title, default 'Reference'
+ - `package ` explicit base package name; also used for resolving references in documents
+ - `all` show local functions, etc as well in the docs
+ - `format` markup processor, can be 'plain' (default), 'markdown' or 'discount'
+ - `output` output name (default 'index')
+ - `dir` directory for output files (default 'docs')
+ - `ext` extension for output (default 'html')
+ - `one` use a one-column layout
+ - `style`, `template` together these specify the directories for the style and and the template. In `config.ld` they may also be `true`, meaning use the same directory as the configuration file.
+
+These only appear in `config.ld`:
+
+ - `description` a project description used under the project title
+ - `examples` a directory or file: can be a table
+ - `readme` name of readme file (to be processed with Markdown)
+ - `no_return_or_parms` don't show parameters or return values in output
+ - `backtick_references` whether references in backticks will be resolved
+ - `manual_url` point to an alternative or local location for the Lua manual, e.g. 'file:///D:/dev/lua/projects/lua-5.1.4/doc/manual.html'
+
+
+Available functions are:
+
+ - `alias(a,tag)` provide an alias `a` for the tag `tag`, for instance `p` as short for `param`
+ - `add_language_extension(ext,lang)` here `lang` may be either 'c' or 'lua', and `ext` is an extension to be recognized as this language
+ - `add_section`
+ - `new_type(tag,header,project_level)` used to add new tags, which are put in their own section `header`. They may be 'project level'.
+ - `tparam_alias(name,type)` for instance, you may wish that `string` means `@\tparam string`.
+
+## Annotations and Searching for Tags
+
+Annotations are special tags that can be used to keep track of internal development status. The known annotations are 'todo', 'fixme' and 'warning'. They may occur in regular function/table doc comments, or on their own anywhere in the code.
+
+ --- Testing annotations
+ -- @module annot1
+ ...
+ --- first function.
+ -- @todo check if this works!
+ function annot1.first ()
+ if boo then
+
+ end
+ --- @fixme what about else?
+ end
+
+Although not currently rendered by the template as HTML, they can be extracted by the `--tags` command, which is given a comma-separated list of tags to list.
+
+ @plain
+ D:\dev\lua\LDoc\tests> ldoc --tags todo,fixme annot1.lua
+ d:\dev\lua\ldoc\tests\annot1.lua:14: first: todo check if this works!
+ d:\dev\lua\ldoc\tests\annot1.lua:19: first-fixme1: fixme what about else?
+
+
+## Generating HTML
+
+LDoc, like LuaDoc, generates output HTML using a template, in this case `ldoc_ltp.lua`. This is expanded by the powerful but simple preprocessor devised originally by [Rici Lake](http://lua-users.org/wiki/SlightlyLessSimpleLuaPreprocessor) which is now part of Penlight. There are two rules - any line starting with '#' is Lua code, which can also be embedded with '$(...)'.
+
+ <h2>Contents</h2>
+ <ul>
+ # for kind,items in module.kinds() do
+ <li><a href="#$(no_spaces(kind))">$(kind)</a></li>
+ # end
+ </ul>
+
+This is then styled with `ldoc.css`. Currently the template and stylesheet is very much based on LuaDoc, so the results are mostly equivalent; the main change that the template has been more generalized. The default location (indicated by '!') is the directory of `ldoc.lua`.
+
+You may customize how you generate your documentation by specifying an alternative style sheet and/or template, which can be deployed with your project. The parameters are `--style` and `--template`, which give the directories where `ldoc.css` and `ldoc.ltp` are to be found. If `config.ld` contains these variables, they are interpreted slightly differently; if they are true, then it means 'use the same directory as config.ld'; otherwise they must be a valid directory relative to the ldoc invocation.
+
+An example of fully customized documentation is `tests/example/style`: this is what you could call 'minimal Markdown style' where there is no attempt to tag things (except emphasizing parameter names). The narrative alone _can_ to be sufficient, if it is written appropriately.
+
+Of course, there's no reason why LDoc must always generate HTML. `--ext` defines what output extension to use; this can also be set in the configuration file. So it's possible to write a template that converts LDoc output to LaTex, for instance. The separation of processing and presentation makes this kind of new application possible with LDoc.
+
+## Internal Data Representation
+
+The `--dump` flag gives a rough text output on the console. But there is a more customizeable way to process the output data generated by LDoc, using the `--filter` parameter. This is understood to be a fully qualified function (module + name). For example, try
+
+ $ ldoc --filter pl.pretty.dump mylib.c
+
+to see a raw dump of the data. (Simply using `dump` as the value here would be a shorthand for `pl.pretty.dump`.) This is potentially very powerful, since you may write arbitrary Lua code to extract the information you need from your project.
+
+For instance, a file `custom.lua` like this:
+
+ return {
+ filter = function (t)
+ for _, mod in ipairs(t) do
+ print(mod.type,mod.name,mod.summary)
+ end
+ end
+ }
+
+Can be used like so:
+
+ ~/LDoc/tests/example$ ldoc --filter custom.filter mylib.c
+ module mylib A sample C extension.
+
+The basic data structure is straightforward: it is an array of 'modules' (project-level entities, including scripts) which each contain an `item` array (functions, tables and so forth).
+
+For instance, to find all functions which don't have a @return tag:
+
+ return {
+ filter = function (t)
+ for _, mod in ipairs(t) do
+ for _, item in ipairs(mod.items) do
+ if item.type == 'function' and not item.ret then
+ print(mod.name,item.name,mod.file,item.lineno)
+ end
+ end
+ end
+ end
+ }
+
+The internal naming is not always so consistent; `ret` corresponds to @return, and `params` corresponds to @param. `item.params` is an array of the function parameters, in order; it is also a map from these names to the individual descriptions of the parameters.
+
+`item.modifiers` is a table where the keys are the tags and the values are arrays of modifier tables. The standard tag aliases `tparam` and `treturn` attach a `type` modifier to their tags.
+
+
--- /dev/null
+#!/usr/bin/env lua
+---------------
+-- ## ldoc, a Lua documentation generator.
+--
+-- Compatible with luadoc-style annotations, but providing
+-- easier customization options.
+--
+-- C/C++ support for Lua extensions is provided.
+--
+-- Available from LuaRocks as 'ldoc' and as a [Zip file](http://stevedonovan.github.com/files/ldoc-1.2.0.zip)
+--
+-- [Github Page](https://github.com/stevedonovan/ldoc)
+--
+-- @author Steve Donovan
+-- @copyright 2011
+-- @license MIT/X11
+-- @script ldoc
+
+require 'pl'
+
+local append = table.insert
+
+local lapp = require 'pl.lapp'
+
+-- so we can find our private modules
+app.require_here()
+
+--- @usage
+local usage = [[
+ldoc, a documentation generator for Lua, vs 1.2.0
+ -d,--dir (default docs) output directory
+ -o,--output (default 'index') output name
+ -v,--verbose verbose
+ -a,--all show local functions, etc, in docs
+ -q,--quiet suppress output
+ -m,--module module docs as text
+ -s,--style (default !) directory for style sheet (ldoc.css)
+ -l,--template (default !) directory for template (ldoc.ltp)
+ -1,--one use one-column output layout
+ -p,--project (default ldoc) project name
+ -t,--title (default Reference) page title
+ -f,--format (default plain) formatting - can be markdown, discount or plain
+ -b,--package (default .) top-level package basename (needed for module(...))
+ -x,--ext (default html) output file extension
+ -c,--config (default config.ld) configuration name
+ --dump debug output dump
+ --filter (default none) filter output as Lua data (e.g pl.pretty.dump)
+ --tags (default none) show all references to given tags, comma-separated
+ <file> (string) source file or directory containing source
+
+ `ldoc .` reads options from an `config.ld` file in same directory;
+ `ldoc -c path/to/myconfig.ld .` reads options from `path/to/myconfig.ld`
+]]
+local args = lapp(usage)
+local lfs = require 'lfs'
+local doc = require 'ldoc.doc'
+local lang = require 'ldoc.lang'
+local tools = require 'ldoc.tools'
+local global = require 'ldoc.builtin.globals'
+local markup = require 'ldoc.markup'
+local parse = require 'ldoc.parse'
+local KindMap = tools.KindMap
+local Item,File,Module = doc.Item,doc.File,doc.Module
+local quit = utils.quit
+
+
+class.ModuleMap(KindMap)
+
+function ModuleMap:_init ()
+ self.klass = ModuleMap
+ self.fieldname = 'section'
+end
+
+ModuleMap:add_kind('function','Functions','Parameters')
+ModuleMap:add_kind('table','Tables','Fields')
+ModuleMap:add_kind('field','Fields')
+ModuleMap:add_kind('lfunction','Local Functions','Parameters')
+ModuleMap:add_kind('annotation','Issues')
+
+
+class.ProjectMap(KindMap)
+ProjectMap.project_level = true
+
+function ProjectMap:_init ()
+ self.klass = ProjectMap
+ self.fieldname = 'type'
+end
+
+ProjectMap:add_kind('module','Modules')
+ProjectMap:add_kind('script','Scripts')
+ProjectMap:add_kind('topic','Topics')
+ProjectMap:add_kind('example','Examples')
+
+local lua, cc = lang.lua, lang.cc
+
+local file_types = {
+ ['.lua'] = lua,
+ ['.ldoc'] = lua,
+ ['.luadoc'] = lua,
+ ['.c'] = cc,
+ ['.cpp'] = cc,
+ ['.cxx'] = cc,
+ ['.C'] = cc
+}
+
+------- ldoc external API ------------
+
+-- the ldoc table represents the API available in `config.ld`.
+local ldoc = {}
+local add_language_extension
+
+local function override (field)
+ if ldoc[field] then args[field] = ldoc[field] end
+end
+
+-- aliases to existing tags can be defined. E.g. just 'p' for 'param'
+function ldoc.alias (a,tag)
+ doc.add_alias(a,tag)
+end
+
+-- standard aliases --
+
+ldoc.alias('tparam',{'param',modifiers={type="$1"}})
+ldoc.alias('treturn',{'return',modifiers={type="$1"}})
+ldoc.alias('tfield',{'field',modifiers={type="$1"}})
+
+function ldoc.tparam_alias (name,type)
+ type = type or name
+ ldoc.alias(name,{'param',modifiers={type=type}})
+end
+
+function ldoc.add_language_extension(ext,lang)
+ lang = (lang=='c' and cc) or (lang=='lua' and lua) or quit('unknown language')
+ if ext:sub(1,1) ~= '.' then ext = '.'..ext end
+ file_types[ext] = lang
+end
+
+function ldoc.add_section (name,title,subname)
+ ModuleMap:add_kind(name,title,subname)
+end
+
+-- new tags can be added, which can be on a project level.
+function ldoc.new_type (tag,header,project_level)
+ doc.add_tag(tag,doc.TAG_TYPE,project_level)
+ if project_level then
+ ProjectMap:add_kind(tag,header)
+ else
+ ModuleMap:add_kind(tag,header)
+ end
+end
+
+function ldoc.manual_url (url)
+ global.set_manual_url(url)
+end
+
+local ldoc_contents = {
+ 'alias','add_language_extension','new_type','add_section', 'tparam_alias',
+ 'file','project','title','package','format','output','dir','ext', 'topics',
+ 'one','style','template','description','examples','readme','all','manual_url',
+ 'no_return_or_parms','no_summary','full_description'
+}
+ldoc_contents = tablex.makeset(ldoc_contents)
+
+-- any file called 'config.ld' found in the source tree will be
+-- handled specially. It will be loaded using 'ldoc' as the environment.
+local function read_ldoc_config (fname)
+ local directory = path.dirname(fname)
+ if directory == '' then
+ directory = '.'
+ end
+ local err
+ if args.filter == 'none' then
+ print('reading configuration from '..fname)
+ end
+ local txt,not_found = utils.readfile(fname)
+ if txt then
+ -- Penlight defines loadin for Lua 5.1 as well
+ local chunk
+ if not loadin then -- Penlight 0.9.5
+ if utils.load then load = utils.load end -- Penlight 0.9.7; no more global load() override
+ chunk,err = load(txt,nil,nil,ldoc)
+ else
+ chunk,err = loadin(ldoc,txt)
+ end
+ if chunk then
+ local ok
+ ok,err = pcall(chunk)
+ end
+ end
+ if err then quit('error loading config file '..fname..': '..err) end
+ for k in pairs(ldoc) do
+ if not ldoc_contents[k] then
+ quit("this config file field/function is unrecognized: "..k)
+ end
+ end
+ return directory, not_found
+end
+
+local quote = tools.quote
+--- processing command line and preparing for output ---
+
+local F
+local file_list,module_list = List(),List()
+module_list.by_name = {}
+local config_dir
+
+
+local ldoc_dir = arg[0]:gsub('[^/\\]+$','')
+local doc_path = ldoc_dir..'/ldoc/builtin/?.lua'
+
+
+-- ldoc -m is expecting a Lua package; this converts this to a file path
+if args.module then
+ -- first check if we've been given a global Lua lib function
+ if args.file:match '^%a+$' and global.functions[args.file] then
+ args.file = 'global.'..args.file
+ end
+ local fullpath,mod = tools.lookup_existing_module_or_function (args.file, doc_path)
+ if not fullpath then
+ quit(mod)
+ else
+ args.file = fullpath
+ args.module = mod
+ end
+end
+
+local abspath = tools.abspath
+
+-- a special case: 'ldoc .' can get all its parameters from config.ld
+if args.file == '.' then
+ local err
+ config_dir,err = read_ldoc_config(args.config)
+ if err then quit("no "..quote(args.config).." found") end
+ local config_path = path.dirname(args.config)
+ if config_path ~= '' then
+ print('changing to directory',config_path)
+ lfs.chdir(config_path)
+ end
+ config_is_read = true
+ args.file = ldoc.file or '.'
+ if args.file == '.' then
+ args.file = lfs.currentdir()
+ elseif type(args.file) == 'table' then
+ for i,f in ipairs(args.file) do
+ args.file[i] = abspath(f)
+ print(args.file[i])
+ end
+ else
+ args.file = abspath(args.file)
+ end
+else
+ args.file = abspath(args.file)
+end
+
+local source_dir = args.file
+if type(source_dir) == 'table' then
+ source_dir = source_dir[1]
+end
+if type(source_dir) == 'string' and path.isfile(source_dir) then
+ source_dir = path.splitpath(source_dir)
+end
+
+---------- specifying the package for inferring module names --------
+-- If you use module(...), or forget to explicitly use @module, then
+-- ldoc has to infer the module name. There are three sensible values for
+-- `args.package`:
+--
+-- * '.' the actual source is in an immediate subdir of the path given
+-- * '..' the path given points to the source directory
+-- * 'NAME' explicitly give the base module package name
+--
+
+local function setup_package_base()
+ if ldoc.package then args.package = ldoc.package end
+ if args.package == '.' then
+ args.package = source_dir
+ elseif args.package == '..' then
+ args.package = path.splitpath(source_dir)
+ elseif not args.package:find '[\\/]' then
+ local subdir,dir = path.splitpath(source_dir)
+ if dir == args.package then
+ args.package = subdir
+ elseif path.isdir(path.join(source_dir,args.package)) then
+ args.package = source_dir
+ else
+ quit("args.package is not the name of the source directory")
+ end
+ end
+end
+
+
+--------- processing files ---------------------
+-- ldoc may be given a file, or a directory. `args.file` may also be specified in config.ld
+-- where it is a list of files or directories. If specified on the command-line, we have
+-- to find an optional associated config.ld, if not already loaded.
+
+local function process_file (f, flist)
+ local ext = path.extension(f)
+ local ftype = file_types[ext]
+ if ftype then
+ if args.verbose then print(path.basename(f)) end
+ local F,err = parse.file(f,ftype,args)
+ if err then
+ if F then
+ F:warning("internal LDoc error")
+ end
+ quit(err)
+ end
+ flist:append(F)
+ end
+end
+
+local process_file_list = tools.process_file_list
+
+setup_package_base()
+
+
+if type(args.file) == 'table' then
+ -- this can only be set from config file so we can assume it's already read
+ process_file_list(args.file,'*.*',process_file, file_list)
+ if #file_list == 0 then quit "no source files specified" end
+elseif path.isdir(args.file) then
+ local files = List(dir.getallfiles(args.file,'*.*'))
+ -- use any configuration file we find, if not already specified
+ if not config_dir then
+ local config_files = files:filter(function(f)
+ return path.basename(f) == args.config
+ end)
+ if #config_files > 0 then
+ config_dir = read_ldoc_config(config_files[1])
+ if #config_files > 1 then
+ print('warning: other config files found: '..config_files[2])
+ end
+ end
+ end
+ for f in files:iter() do
+ process_file(f, file_list)
+ end
+ if #file_list == 0 then
+ quit(quote(args.file).." contained no source files")
+ end
+elseif path.isfile(args.file) then
+ -- a single file may be accompanied by a config.ld in the same dir
+ if not config_dir then
+ config_dir = path.dirname(args.file)
+ if config_dir == '' then config_dir = '.' end
+ local config = path.join(config_dir,args.config)
+ if path.isfile(config) then
+ read_ldoc_config(config)
+ end
+ end
+ process_file(args.file, file_list)
+ if #file_list == 0 then quit "unsupported file extension" end
+else
+ quit ("file or directory does not exist: "..quote(args.file))
+end
+
+-- create the function that renders text (descriptions and summaries)
+override 'format'
+ldoc.markup = markup.create(ldoc, args.format)
+
+local multiple_files = #file_list > 1
+local first_module
+
+------ 'Special' Project-level entities ---------------------------------------
+-- Examples and Topics do not contain code to be processed for doc comments.
+-- Instead, they are intended to be rendered nicely as-is, whether as pretty-lua
+-- or as Markdown text. Treating them as 'modules' does stretch the meaning of
+-- of the term, but allows them to be treated much as modules or scripts.
+-- They define an item 'body' field (containing the file's text) and a 'postprocess'
+-- field which is used later to convert them into HTML. They may contain @{ref}s.
+
+local function add_special_project_entity (f,tags,process)
+ local F = File(f)
+ tags.name = path.basename(f)
+ local text = utils.readfile(f)
+ local item = F:new_item(tags,1)
+ if process then
+ text = process(F, text)
+ end
+ F:finish()
+ file_list:append(F)
+ item.body = text
+ return item, F
+end
+
+if type(ldoc.examples) == 'string' then
+ ldoc.examples = {ldoc.examples}
+end
+if type(ldoc.examples) == 'table' then
+ local prettify = require 'ldoc.prettify'
+
+ process_file_list (ldoc.examples, '*.lua', function(f)
+ local item = add_special_project_entity(f,{
+ class = 'example',
+ })
+ -- wrap prettify for this example so it knows which file to blame
+ -- if there's a problem
+ item.postprocess = function(code) return prettify.lua(f,code) end
+ end)
+end
+
+ldoc.readme = ldoc.readme or ldoc.topics
+if type(ldoc.readme) == 'string' then
+ ldoc.readme = {ldoc.readme}
+end
+if type(ldoc.readme) == 'table' then
+ process_file_list(ldoc.readme, '*.md', function(f)
+ local item, F = add_special_project_entity(f,{
+ class = 'topic'
+ }, markup.add_sections)
+ -- add_sections above has created sections corresponding to the 2nd level
+ -- headers in the readme, which are attached to the File. So
+ -- we pass the File to the postprocesser, which will insert the section markers
+ -- and resolve inline @ references.
+ item.postprocess = function(txt) return ldoc.markup(txt,F) end
+ end)
+end
+
+-- extract modules from the file objects, resolve references and sort appropriately ---
+
+local project = ProjectMap()
+
+for F in file_list:iter() do
+ for mod in F.modules:iter() do
+ if not first_module then first_module = mod end
+ module_list:append(mod)
+ module_list.by_name[mod.name] = mod
+ end
+end
+
+for mod in module_list:iter() do
+ if not args.module then -- no point if we're just showing docs on the console
+ mod:resolve_references(module_list)
+ end
+ project:add(mod,module_list)
+end
+
+-- the default is not to show local functions in the documentation.
+if not args.all and not ldoc.all then
+ for mod in module_list:iter() do
+ mod:mask_locals()
+ end
+end
+
+table.sort(module_list,function(m1,m2)
+ return m1.name < m2.name
+end)
+
+-------- three ways to dump the object graph after processing -----
+
+-- ldoc -m will give a quick & dirty dump of the module's documentation;
+-- using -v will make it more verbose
+if args.module then
+ if #module_list == 0 then quit("no modules found") end
+ if args.module == true then
+ file_list[1]:dump(args.verbose)
+ else
+ local fun = module_list[1].items.by_name[args.module]
+ if not fun then quit(quote(args.module).." is not part of "..quote(args.file)) end
+ fun:dump(true)
+ end
+ return
+end
+
+-- ldoc --dump will do the same as -m, except for the currently specified files
+if args.dump then
+ for mod in module_list:iter() do
+ mod:dump(true)
+ end
+ os.exit()
+end
+if args.tags ~= 'none' then
+ local tagset = {}
+ for t in stringx.split(args.tags,','):iter() do
+ tagset[t] = true
+ end
+ for mod in module_list:iter() do
+ mod:dump_tags(tagset)
+ end
+ os.exit()
+end
+
+-- ldoc --filter mod.name will load the module `mod` and pass the object graph
+-- to the function `name`. As a special case --filter dump will use pl.pretty.dump.
+if args.filter ~= 'none' then
+ doc.filter_objects_through_function(args.filter, module_list)
+ os.exit()
+end
+
+ldoc.css, ldoc.templ = 'ldoc.css','ldoc.ltp'
+
+local function style_dir (sname)
+ local style = ldoc[sname]
+ local dir
+ if style then
+ if style == true then
+ dir = config_dir
+ elseif type(style) == 'string' and path.isdir(style) then
+ dir = style
+ else
+ quit(quote(tostring(name)).." is not a directory")
+ end
+ args[sname] = dir
+ end
+end
+
+
+-- the directories for template and stylesheet can be specified
+-- either by command-line '--template','--style' arguments or by 'template and
+-- 'style' fields in config.ld.
+-- The assumption here is that if these variables are simply true then the directory
+-- containing config.ld contains a ldoc.css and a ldoc.ltp respectively. Otherwise
+-- they must be a valid subdirectory.
+
+style_dir 'style'
+style_dir 'template'
+
+-- can specify format, output, dir and ext in config.ld
+override 'output'
+override 'dir'
+override 'ext'
+override 'one'
+
+if not args.ext:find '^%.' then
+ args.ext = '.'..args.ext
+end
+
+if args.one then
+ ldoc.css = 'ldoc_one.css'
+end
+
+if args.style == '!' or args.template == '!' then
+ -- '!' here means 'use built-in templates'
+ local tmpdir = path.join(path.is_windows and os.getenv('TMP') or '/tmp','ldoc')
+ if not path.isdir(tmpdir) then
+ lfs.mkdir(tmpdir)
+ end
+ local function tmpwrite (name)
+ utils.writefile(path.join(tmpdir,name),require('ldoc.html.'..name:gsub('%.','_')))
+ end
+ if args.style == '!' then
+ tmpwrite(ldoc.templ)
+ args.style = tmpdir
+ end
+ if args.template == '!' then
+ tmpwrite(ldoc.css)
+ args.template = tmpdir
+ end
+end
+
+
+ldoc.single = not multiple_files and first_module or nil
+ldoc.log = print
+ldoc.kinds = project
+ldoc.modules = module_list
+ldoc.title = ldoc.title or args.title
+ldoc.project = ldoc.project or args.project
+ldoc.package = args.package:match '%a+' and args.package or nil
+
+local html = require 'ldoc.html'
+
+html.generate_output(ldoc, args, project)
+
+if args.verbose then
+ print 'modules'
+ for k in pairs(module_list.by_name) do print(k) end
+end
+
+
--- /dev/null
+--- creating and controlling coroutines.\r
+\r
+module 'coroutine'\r
+\r
+---\r
+-- Creates a new coroutine, with body `f`. `f` must be a Lua\r
+-- function. Returns this new coroutine, an object with type `"thread"`.\r
+function coroutine.create(f) end\r
+\r
+---\r
+-- Starts or continues the execution of coroutine `co`. The first time\r
+-- you resume a coroutine, it starts running its body. The values\r
+-- ... are passed as the arguments to the body function. If the coroutine\r
+-- has yielded, `resume` restarts it; the values ... are passed\r
+-- as the results from the yield.\r
+-- If the coroutine runs without any errors, `resume` returns true plus any\r
+-- values passed to `yield` (if the coroutine yields) or any values returned\r
+-- by the body function (if the coroutine terminates). If there is any error,\r
+-- `resume` returns false plus the error message.\r
+function coroutine.resume(co , ...) end\r
+\r
+---\r
+-- Returns the running coroutine. Or nil when called by the main thread.\r
+function coroutine.running() end\r
+\r
+---\r
+-- Returns the status of coroutine `co`. Result is a string: `"running"`, if\r
+-- the coroutine is running (that is, it called `status`); `"suspended"`, if\r
+-- the coroutine is suspended in a call to `yield`, or if it has not started\r
+-- running yet; `"normal"` if the coroutine is active but not running (that\r
+-- is, it has resumed another coroutine); and `"dead"` if the coroutine has\r
+-- finished its body function, or if it has stopped with an error.\r
+function coroutine.status(co) end\r
+\r
+---\r
+-- Creates a new coroutine, with body `f`. `f` must be a Lua\r
+-- function. Returns a function that resumes the coroutine each time it is\r
+-- called. Any arguments passed to the function behave as the extra arguments to\r
+-- `resume`. Returns the same values returned by `resume`, except the first\r
+-- boolean. In case of error, propagates the error.\r
+function coroutine.wrap(f) end\r
+\r
+---\r
+-- Suspends the execution of the calling coroutine. The coroutine cannot\r
+-- be running a C function, a metamethod, or an iterator. Any arguments to\r
+-- `yield` are passed as extra results to `resume`.\r
+function coroutine.yield(...) end\r
+\r
--- /dev/null
+--- getting runtime debug information.\r
+\r
+module 'debug'\r
+\r
+---\r
+-- Enters an interactive mode with the user, running each string that\r
+-- the user enters. Using simple commands and other debug facilities,\r
+-- the user can inspect global and local variables, change their values,\r
+-- evaluate expressions, and so on. A line containing only the word `cont`\r
+-- finishes this function, so that the caller continues its execution.\r
+-- Note that commands for `debug.debug` are not lexically nested within any\r
+-- function, and so have no direct access to local variables.\r
+function debug.debug() end\r
+\r
+---\r
+-- Returns the environment of object `o`.\r
+function debug.getfenv(o) end\r
+\r
+---\r
+-- Returns the current hook settings of the thread, as three values: the\r
+-- current hook function, the current hook mask, and the current hook count\r
+-- (as set by the `debug.sethook` function).\r
+function debug.gethook(thread) end\r
+\r
+---\r
+-- Returns a table with information about a function. You can give the\r
+-- function directly, or you can give a number as the value of `function`,\r
+-- which means the function running at level `function` of the call stack\r
+-- of the given thread: level 0 is the current function (`getinfo` itself);\r
+-- level 1 is the function that called `getinfo`; and so on. If `function`\r
+-- is a number larger than the number of active functions, then `getinfo`\r
+-- returns nil.\r
+--\r
+-- `thread` and `what` are optional.\r
+--\r
+-- The returned table can contain all the fields returned by `lua_getinfo`,\r
+-- with the string `what` describing which fields to fill in. The default for\r
+-- `what` is to get all information available, except the table of valid\r
+-- lines. If present, the option '`f`' adds a field named `func` with\r
+-- the function itself. If present, the option '`L`' adds a field named\r
+-- `activelines` with the table of valid lines.\r
+-- For instance, the expression `debug.getinfo(1,"n").name` returns a table\r
+-- with a name for the current function, if a reasonable name can be found,\r
+-- and the expression `debug.getinfo(print)` returns a table with all available\r
+-- information about the `print` function.\r
+function debug.getinfo(thread, function , what) end\r
+\r
+---\r
+-- This function returns the name and the value of the local variable with\r
+-- index `local` of the function at level `level` of the stack. (The first\r
+-- parameter or local variable has index 1, and so on, until the last active\r
+-- local variable.) The function returns nil if there is no local variable\r
+-- with the given index, and raises an error when called with a `level` out\r
+-- of range. (You can call `debug.getinfo` to check whether the level is valid.)\r
+-- Variable names starting with '`(`' (open parentheses) represent internal\r
+-- variables (loop control variables, temporaries, and C function locals).\r
+function debug.getlocal(thread, level, local) end\r
+\r
+---\r
+-- Returns the metatable of the given `object` or nil if it does not have\r
+-- a metatable.\r
+function debug.getmetatable(object) end\r
+\r
+---\r
+-- Returns the registry table (see §3.5).\r
+function debug.getregistry() end\r
+\r
+---\r
+-- This function returns the name and the value of the upvalue with index\r
+-- `up` of the function `func`. The function returns nil if there is no\r
+-- upvalue with the given index.\r
+function debug.getupvalue(func, up) end\r
+\r
+---\r
+-- Sets the environment of the given `object` to the given `table`. Returns\r
+-- `object`.\r
+function debug.setfenv(object, table) end\r
+\r
+---\r
+-- Sets the given function as a hook. The string `mask` and the number\r
+-- `count` describe when the hook will be called. The string mask may have\r
+-- the following characters, with the given meaning:\r
+--\r
+-- * `"c"`: the hook is called every time Lua calls a function;\r
+-- * `"r"`: the hook is called every time Lua returns from a function;\r
+-- * `"l"`: the hook is called every time Lua enters a new line of code.\r
+--\r
+-- With a `count` different from zero, the hook is called after every `count`\r
+-- instructions.\r
+--\r
+-- When called without arguments, `debug.sethook` turns off the hook.\r
+--\r
+-- When the hook is called, its first parameter is a string describing\r
+-- the event that has triggered its call: `"call"`, `"return"` (or `"tail\r
+-- return"`, when simulating a return from a tail call), `"line"`, and\r
+-- `"count"`. For line events, the hook also gets the new line number as its\r
+-- second parameter. Inside a hook, you can call `getinfo` with level 2 to\r
+-- get more information about the running function (level 0 is the `getinfo`\r
+-- function, and level 1 is the hook function), unless the event is `"tail\r
+-- return"`. In this case, Lua is only simulating the return, and a call to\r
+-- `getinfo` will return invalid data.\r
+function debug.sethook(thread, hook, mask , count) end\r
+\r
+---\r
+-- This function assigns the value `value` to the local variable with\r
+-- index `local` of the function at level `level` of the stack. The function\r
+-- returns nil if there is no local variable with the given index, and raises\r
+-- an error when called with a `level` out of range. (You can call `getinfo`\r
+-- to check whether the level is valid.) Otherwise, it returns the name of\r
+-- the local variable.\r
+function debug.setlocal(thread, level, local, value) end\r
+\r
+---\r
+-- Sets the metatable for the given `object` to the given `table` (which\r
+-- can be nil).\r
+function debug.setmetatable(object, table) end\r
+\r
+---\r
+-- This function assigns the value `value` to the upvalue with index `up`\r
+-- of the function `func`. The function returns nil if there is no upvalue\r
+-- with the given index. Otherwise, it returns the name of the upvalue.\r
+function debug.setupvalue(func, up, value) end\r
+\r
--- /dev/null
+--- Lua global functions.\r
+\r
+module 'global'\r
+\r
+---\r
+-- Issues an error when the value of its argument `v` is false (i.e.,\r
+-- nil or false); otherwise, returns all its arguments. `message` is an error\r
+-- message; when absent, it defaults to "assertion failed!"\r
+function assert(v , message) end\r
+\r
+---\r
+-- This function is a generic interface to the garbage collector. It\r
+-- performs different functions according to its first argument, `opt`:\r
+--\r
+-- * "stop": stops the garbage collector.\r
+-- * "restart": restarts the garbage collector.\r
+-- * "collect": performs a full garbage-collection cycle.\r
+-- * "count": returns the total memory in use by Lua (in Kbytes).\r
+-- * "step": performs a garbage-collection step. The step "size" is controlled\r
+-- by `arg` (larger values mean more steps) in a non-specified way. If you\r
+-- want to control the step size you must experimentally tune the value of\r
+-- * `arg`. Returns true if the step finished a collection cycle.\r
+-- * "setpause": sets `arg` as the new value for the *pause* of the collector\r
+-- (see §2.10). Returns the previous value for *pause*.\r
+-- * "setstepmul": sets `arg` as the new value for the *step multiplier*\r
+-- of the collector (see §2.10). Returns the previous value for *step*.\r
+--\r
+function collectgarbage(opt , arg) end\r
+\r
+---\r
+-- Opens the named file and executes its contents as a Lua chunk. When\r
+-- called without arguments,\r
+-- `dofile` executes the contents of the standard input (`stdin`). Returns\r
+-- all values returned by the chunk. In case of errors, `dofile` propagates\r
+-- the error to its caller (that is, `dofile` does not run in protected mode).\r
+function dofile(filename) end\r
+\r
+---\r
+-- Terminates the last protected function called and returns `message`\r
+-- as the error message. Function `error` never returns.\r
+-- Usually, `error` adds some information about the error position at the\r
+-- beginning of the message. The `level` argument specifies how to get the\r
+-- error position. With level 1 (the default), the error position is where the\r
+-- `error` function was called. Level 2 points the error to where the function\r
+-- that called `error` was called; and so on. Passing a level 0 avoids the\r
+-- addition of error position information to the message.\r
+function error(message , level) end\r
+\r
+---\r
+-- A global variable (not a function) that holds the global environment\r
+-- (that is, `_G._G = _G`). Lua itself does not use this variable; changing\r
+-- its value does not affect any environment, nor vice-versa. (Use `setfenv`\r
+-- to change environments.)\r
+-- function _G end\r
+-- * `_G._G`: _G._G\r
+\r
+---\r
+-- Returns the current environment in use by the function.\r
+-- `f` can be a Lua function or a number that specifies the function at that\r
+-- stack level: Level 1 is the function calling `getfenv`. If the given\r
+-- function is not a Lua function, or if `f` is 0, `getfenv` returns the\r
+-- global environment. The default for `f` is 1.\r
+function getfenv(f) end\r
+\r
+---\r
+-- If `object` does not have a metatable, returns nil. Otherwise, if the\r
+-- object's metatable has a `"__metatable"` field, returns the associated\r
+-- value. Otherwise, returns the metatable of the given object.\r
+function getmetatable(object) end\r
+\r
+---\r
+-- Returns three values: an iterator function, the table `t`, and 0,\r
+-- so that the construction\r
+-- for i,v in ipairs(t) do *body* end\r
+-- will iterate over the pairs (`1,t[1]`), (`2,t[2]`), ..., up to the\r
+-- first integer key absent from the table.\r
+function ipairs(t) end\r
+\r
+---\r
+-- Loads a chunk using function `func` to get its pieces. Each call to\r
+-- `func` must return a string that concatenates with previous results. A\r
+-- return of an empty string, nil, or no value signals the end of the chunk.\r
+-- If there are no errors, returns the compiled chunk as a function; otherwise,\r
+-- returns nil plus the error message. The environment of the returned function\r
+-- is the global environment.\r
+-- `chunkname` is used as the chunk name for error messages and debug\r
+-- information. When absent, it defaults to "`=(load)`".\r
+function load(func , chunkname) end\r
+\r
+---\r
+-- Similar to `load`, but gets the chunk from file `filename` or from the\r
+-- standard input, if no file name is given.\r
+function loadfile(filename) end\r
+\r
+---\r
+-- Similar to `load`, but gets the chunk from the given string.\r
+-- To load and run a given string, use the idiom\r
+-- assert(loadstring(s))()\r
+-- When absent, `chunkname` defaults to the given string.\r
+function loadstring(string , chunkname) end\r
+\r
+---\r
+-- Allows a program to traverse all fields of a table. Its first argument is\r
+-- a table and its second argument is an index in this table. `next` returns\r
+-- the next index of the table and its associated value.\r
+--\r
+-- When called with nil\r
+-- as its second argument, `next` returns an initial index and its associated\r
+-- value. When called with the last index, or with nil in an empty table, `next`\r
+-- returns nil.\r
+--\r
+-- If the second argument is absent, then it is interpreted as\r
+-- nil. In particular, you can use `next(t)` to check whether a table is empty.\r
+-- The order in which the indices are enumerated is not specified, *even for\r
+-- numeric indices*. (To traverse a table in numeric order, use a numerical\r
+-- for or the `ipairs` function.)\r
+--\r
+-- The behavior of `next` is *undefined* if, during the traversal, you assign\r
+-- any value to a non-existent field in the table. You may however modify\r
+-- existing fields. In particular, you may clear existing fields.\r
+function next(table , index) end\r
+\r
+---\r
+-- Returns three values: the `next` function, the table `t`, and nil,\r
+-- so that the construction\r
+-- for k,v in pairs(t) do *body* end\r
+-- will iterate over all key–value pairs of table `t`.\r
+-- See function `next` for the caveats of modifying the table during its\r
+-- traversal.\r
+function pairs(t) end\r
+\r
+---\r
+-- Calls function `f` with the given arguments in *protected mode*. This\r
+-- means that any error inside `f` is not propagated; instead, `pcall` catches\r
+-- the error and returns a status code. Its first result is the status code (a\r
+-- boolean), which is true if the call succeeds without errors. In such case,\r
+-- `pcall` also returns all results from the call, after this first result. In\r
+-- case of any error, `pcall` returns false plus the error message.\r
+function pcall(f, arg1, ...) end\r
+\r
+---\r
+-- Receives any number of arguments, and prints their values to `stdout`,\r
+-- using the `tostring` function to convert them to strings. `print` is not\r
+-- intended for formatted output, but only as a quick way to show a value,\r
+-- typically for debugging. For formatted output, use `string.format`.\r
+function print(...) end\r
+\r
+---\r
+-- Checks whether `v1` is equal to `v2`, without invoking any\r
+-- metamethod. Returns a boolean.\r
+function rawequal(v1, v2) end\r
+\r
+---\r
+-- Gets the real value of `table[index]`, without invoking any\r
+-- metamethod. `table` must be a table; `index` may be any value.\r
+function rawget(table, index) end\r
+\r
+---\r
+-- Sets the real value of `table[index]` to `value`, without invoking any\r
+-- metamethod. `table` must be a table, `index` any value different from nil,\r
+-- and `value` any Lua value.\r
+-- This function returns `table`.\r
+function rawset(table, index, value) end\r
+\r
+---\r
+-- If `index` is a number, returns all arguments after argument number\r
+-- `index`. Otherwise, `index` must be the string `"#"`, and `select` returns\r
+-- the total number of extra arguments it received.\r
+function select(index, ...) end\r
+\r
+---\r
+-- Sets the environment to be used by the given function. `f` can be a Lua\r
+-- function or a number that specifies the function at that stack level: Level\r
+-- 1 is the function calling `setfenv`. `setfenv` returns the given function.\r
+-- As a special case, when `f` is 0 `setfenv` changes the environment of the\r
+-- running thread. In this case, `setfenv` returns no values.\r
+function setfenv(f, table) end\r
+\r
+---\r
+-- Sets the metatable for the given table. (You cannot change the metatable\r
+-- of other types from Lua, only from C.) If `metatable` is nil, removes the\r
+-- metatable of the given table. If the original metatable has a `"__metatable"`\r
+-- field, raises an error.\r
+-- This function returns `table`.\r
+function setmetatable(table, metatable) end\r
+\r
+---\r
+-- Tries to convert its argument to a number. If the argument is already\r
+-- a number or a string convertible to a number, then `tonumber` returns this\r
+-- number; otherwise, it returns nil.\r
+-- An optional argument specifies the base to interpret the numeral. The base\r
+-- may be any integer between 2 and 36, inclusive. In bases above 10, the\r
+-- letter '`A`' (in either upper or lower case) represents 10, '`B`' represents\r
+-- 11, and so forth, with '`Z`' representing 35. In base 10 (the default),\r
+-- the number can have a decimal part, as well as an optional exponent part\r
+-- (see §2.1). In other bases, only unsigned integers are accepted.\r
+function tonumber(e , base) end\r
+\r
+---\r
+-- Receives an argument of any type and converts it to a string in a\r
+-- reasonable format. For complete control of how numbers are converted, use\r
+-- `string.format`.\r
+-- If the metatable of `e` has a `"__tostring"` field, then `tostring` calls\r
+-- the corresponding value with `e` as argument, and uses the result of the\r
+-- call as its result.\r
+function tostring(e) end\r
+\r
+---\r
+-- Returns the type of its only argument, coded as a string. The possible\r
+-- results of this function are "\r
+-- `nil`" (a string, not the value nil), "`number`", "`string`", "`boolean`",\r
+-- "`table`", "`function`", "`thread`", and "`userdata`".\r
+function type(v) end\r
+\r
+---\r
+-- Returns the elements from the given table. This function is equivalent to\r
+-- return list[i], list[i+1], ..., list[j]\r
+-- except that the above code can be written only for a fixed number of\r
+-- elements. By default, `i` is 1 and `j` is the length of the list, as\r
+-- defined by the length operator (see §2.5.5).\r
+function unpack(list , i , j) end\r
+\r
+---\r
+-- A global variable (not a function) that holds a string containing the\r
+-- current interpreter version. The current contents of this variable is\r
+-- "`Lua 5.1`".\r
+-- function _VERSION end\r
+-- * `_G._VERSION`: _G._VERSION\r
+\r
+---\r
+-- This function is similar to `pcall`, except that you can set a new\r
+-- error handler.\r
+-- `xpcall` calls function `f` in protected mode, using `err` as the error\r
+-- handler. Any error inside `f` is not propagated; instead, `xpcall` catches\r
+-- the error, calls the `err` function with the original error object, and\r
+-- returns a status code. Its first result is the status code (a boolean),\r
+-- which is true if the call succeeds without errors. In this case, `xpcall`\r
+-- also returns all results from the call, after this first result. In case\r
+-- of any error, `xpcall` returns false plus the result from `err`.\r
+function xpcall(f, err) end\r
+\r
+---\r
+-- Creates a module. If there is a table in `package.loaded[name]`,\r
+-- this table is the module. Otherwise, if there is a global table `t`\r
+-- with the given name, this table is the module. Otherwise creates a new\r
+-- table `t` and sets it as the value of the global `name` and the value of\r
+-- `package.loaded[name]`. This function also initializes `t._NAME` with the\r
+-- given name, `t._M` with the module (`t` itself), and `t._PACKAGE` with the\r
+-- package name (the full module name minus last component; see below). Finally,\r
+-- `module` sets `t` as the new environment of the current function and the\r
+-- new value of `package.loaded[name]`, so that `require` returns `t`.\r
+-- If `name` is a compound name (that is, one with components separated by\r
+-- dots), `module` creates (or reuses, if they already exist) tables for each\r
+-- component. For instance, if `name` is `a.b.c`, then `module` stores the\r
+-- module table in field `c` of field `b` of global `a`.\r
+-- This function can receive optional *options* after the module name, where\r
+-- each option is a function to be applied over the module.\r
+function module(name , ...) end\r
+\r
+---\r
+-- Loads the given module. The function starts by looking into the\r
+-- `package.loaded` table to determine whether `modname` is already\r
+-- loaded. If it is, then `require` returns the value stored at\r
+-- `package.loaded[modname]`. Otherwise, it tries to find a *loader* for\r
+-- the module.\r
+-- To find a loader, `require` is guided by the `package.loaders` array. By\r
+-- changing this array, we can change how `require` looks for a module. The\r
+-- following explanation is based on the default configuration for\r
+-- `package.loaders`.\r
+-- First `require` queries `package.preload[modname]`. If it has a value,\r
+-- this value (which should be a function) is the loader. Otherwise `require`\r
+-- searches for a Lua loader using the path stored in `package.path`. If\r
+-- that also fails, it searches for a C loader using the path stored in\r
+-- `package.cpath`. If that also fails, it tries an *all-in-one* loader (see\r
+-- `package.loaders`).\r
+-- Once a loader is found, `require` calls the loader with a single argument,\r
+-- `modname`. If the loader returns any value, `require` assigns the returned\r
+-- value to `package.loaded[modname]`. If the loader returns no value and\r
+-- has not assigned any value to `package.loaded[modname]`, then `require`\r
+-- assigns true to this entry. In any case, `require` returns the final value\r
+-- of `package.loaded[modname]`.\r
+-- If there is any error loading or running the module, or if it cannot find\r
+-- any loader for the module, then `require` signals an error.\r
+function require(modname) end\r
+\r
--- /dev/null
+-------
+-- global functions and tables
+local tools = require 'ldoc.tools'
+local globals = {}
+local lua52 = _VERSION:match '5.2'
+
+
+globals.functions = {
+ assert = true,
+ collectgarbage = true,
+ dofile = true,
+ getmetatable = true,
+ setmetatable = true,
+ pairs = true,
+ ipairs = true,
+ load = true,
+ loadfile = true,
+ loadstring = true,
+ next = true,
+ pcall = true,
+ print = true,
+ rawequal = true,
+ rawget = true,
+ rawset = true,
+ select = true,
+ tonumber = true,
+ tostring = true,
+ type = true,
+ xpcall = true,
+ module = true,
+ require = true,
+}
+local functions = globals.functions
+
+if not lua52 then
+ functions.setfenv = true
+ functions.getfenv = true
+ functions.unpack = true
+else
+ functions.rawlen = true
+end
+
+local manual, fun_ref
+
+function globals.set_manual_url(url)
+ manual = url .. '#'
+ fun_ref = manual..'pdf-'
+end
+
+if lua52 then
+ globals.tables = {
+ io = '6.8',
+ package = '6.3',
+ math = '6.6',
+ os = '6.9',
+ string = '6.4',
+ table = '6.5',
+ coroutine = '6.2',
+ debug = '6.10'
+ }
+ globals.set_manual_url 'http://www.lua.org/manual/5.2/manual.html'
+else
+ globals.tables = {
+ io = '5.7',
+ package = '5.3',
+ math = '5.6',
+ os = '5.8',
+ string = '5.4',
+ table = '5.5',
+ coroutine = '5.2',
+ debug = '5.9'
+ }
+ globals.set_manual_url 'http://www.lua.org/manual/5.1/manual.html'
+end
+
+local tables = globals.tables
+
+local function function_ref (name)
+ return {href = fun_ref..name, label = name}
+end
+
+local function module_ref (name)
+ return {href = manual..tables[name], label = name}
+end
+
+function globals.lua_manual_ref (name)
+ local tbl,fname = tools.split_dotted_name(name)
+ if not tbl then -- plain symbol
+ if functions[name] then
+ return function_ref(name)
+ end
+ if tables[name] then
+ return module_ref(name)
+ end
+ else
+ if tables[tbl] then
+ return function_ref(name)
+ end
+ end
+ return nil
+end
+
+return globals
--- /dev/null
+--- Reading and Writing Files.\r
+\r
+module 'io'\r
+\r
+---\r
+-- Equivalent to `file:close()`. Without a `file`, closes the default\r
+-- output file.\r
+function io.close(file) end\r
+\r
+---\r
+-- Equivalent to `file:flush` over the default output file.\r
+function io.flush() end\r
+\r
+---\r
+-- When called with a file name, it opens the named file (in text mode),\r
+-- and sets its handle as the default input file. When called with a file\r
+-- handle, it simply sets this file handle as the default input file. When\r
+-- called without parameters, it returns the current default input file.\r
+-- In case of errors this function raises the error, instead of returning an\r
+-- error code.\r
+function io.input(file) end\r
+\r
+---\r
+-- Opens the given file name in read mode and returns an iterator function\r
+-- that, each time it is called, returns a new line from the file. Therefore,\r
+-- the construction\r
+-- for line in io.lines(filename) do *body* end\r
+-- will iterate over all lines of the file. When the iterator function detects\r
+-- the end of file, it returns nil (to finish the loop) and automatically\r
+-- closes the file.\r
+-- The call `io.lines()` (with no file name) is equivalent to\r
+-- `io.input():lines()`; that is, it iterates over the lines of the default\r
+-- input file. In this case it does not close the file when the loop ends.\r
+function io.lines(filename) end\r
+\r
+---\r
+-- This function opens a file, in the mode specified in the string `mode`. It\r
+-- returns a new file handle, or, in case of errors, nil plus an error message.\r
+-- The `mode` string can be any of the following:\r
+-- "r": read mode (the default);\r
+-- "w": write mode;\r
+-- "a": append mode;\r
+-- "r+": update mode, all previous data is preserved;\r
+-- "w+": update mode, all previous data is erased;\r
+-- "a+": append update mode, previous data is preserved, writing is only\r
+-- allowed at the end of file.\r
+-- The `mode` string can also have a '`b`' at the end, which is needed in\r
+-- some systems to open the file in binary mode. This string is exactly what\r
+-- is used in the standard C function `fopen`.\r
+function io.open(filename , mode) end\r
+\r
+---\r
+-- Similar to `io.input`, but operates over the default output file.\r
+function io.output(file) end\r
+\r
+---\r
+-- Starts program `prog` in a separated process and returns a file handle\r
+-- that you can use to read data from this program (if `mode` is `"r"`,\r
+-- the default) or to write data to this program (if `mode` is `"w"`).\r
+-- This function is system dependent and is not available on all platforms.\r
+function io.popen(prog , mode) end\r
+\r
+---\r
+-- Equivalent to `io.input():read`.\r
+function io.read(...) end\r
+\r
+-- * `io.stderr`: Standard error.\r
+-- * `io.stdin`: Standard in.\r
+-- * `io.stdout`: Standard out.\r
+\r
+---\r
+-- Returns a handle for a temporary file. This file is opened in update\r
+-- mode and it is automatically removed when the program ends.\r
+function io.tmpfile() end\r
+\r
+---\r
+-- Checks whether `obj` is a valid file handle. Returns the string `"file"`\r
+-- if `obj` is an open file handle, `"closed file"` if `obj` is a closed file\r
+-- handle, or nil if `obj` is not a file handle.\r
+function io.type(obj) end\r
+\r
+---\r
+-- Equivalent to `io.output():write`.\r
+function io.write(...) end\r
+\r
+---\r
+-- Closes `file`. Note that files are automatically closed when their\r
+-- handles are garbage collected, but that takes an unpredictable amount of\r
+-- time to happen.\r
+function file:close() end\r
+\r
+---\r
+-- Saves any written data to `file`.\r
+function file:flush() end\r
+\r
+---\r
+-- Returns an iterator function that, each time it is called, returns a\r
+-- new line from the file. Therefore, the construction\r
+-- for line in file:lines() do *body* end\r
+-- will iterate over all lines of the file. (Unlike `io.lines`, this function\r
+-- does not close the file when the loop ends.)\r
+function file:lines() end\r
+\r
+---\r
+-- Reads the file `file`, according to the given formats, which specify\r
+-- what to read. For each format, the function returns a string (or a number)\r
+-- with the characters read, or nil if it cannot read data with the specified\r
+-- format. When called without formats, it uses a default format that reads\r
+-- the entire next line (see below).\r
+-- The available formats are\r
+-- "*n": reads a number; this is the only format that returns a number\r
+-- instead of a string.\r
+-- "*a": reads the whole file, starting at the current position. On end of\r
+-- file, it returns the empty string.\r
+-- "*l": reads the next line (skipping the end of line), returning nil on\r
+-- end of file. This is the default format.\r
+-- *number*: reads a string with up to this number of characters, returning\r
+-- nil on end of file. If number is zero, it reads nothing and returns an\r
+-- empty string, or nil on end of file.\r
+function file:read(...) end\r
+\r
+---\r
+-- Sets and gets the file position, measured from the beginning of the\r
+-- file, to the position given by `offset` plus a base specified by the string\r
+-- `whence`, as follows:\r
+-- "set": base is position 0 (beginning of the file);\r
+-- "cur": base is current position;\r
+-- "end": base is end of file;\r
+-- In case of success, function `seek` returns the final file position,\r
+-- measured in bytes from the beginning of the file. If this function fails,\r
+-- it returns nil, plus a string describing the error.\r
+-- The default value for `whence` is `"cur"`, and for `offset` is 0. Therefore,\r
+-- the call `file:seek()` returns the current file position, without changing\r
+-- it; the call `file:seek("set")` sets the position to the beginning of the\r
+-- file (and returns 0); and the call `file:seek("end")` sets the position\r
+-- to the end of the file, and returns its size.\r
+function file:seek(whence , offset) end\r
+\r
+---\r
+-- Sets the buffering mode for an output file. There are three available\r
+-- modes:\r
+--\r
+-- * "no": no buffering; the result of any output operation appears immediately.\r
+-- * "full": full buffering; output operation is performed only when the\r
+-- buffer is full (or when you explicitly `flush` the file (see `io.flush`)).\r
+-- * "line": line buffering; output is buffered until a newline is output or\r
+-- there is any input from some special files (such as a terminal device).\r
+-- For the last two cases, `size` specifies the size of the buffer, in\r
+-- bytes. The default is an appropriate size.\r
+function file:setvbuf(mode , size) end\r
+\r
+---\r
+-- Writes the value of each of its arguments to the `file`. The arguments\r
+-- must be strings or numbers. To write other values, use `tostring` or\r
+-- `string.format` before `write`.\r
+function file:write(...) end\r
+\r
--- /dev/null
+--- File and Directory manipulation\r
+\r
+module 'lfs'\r
+\r
+---\r
+-- Returns a table with the file attributes corresponding to filepath (or nil\r
+-- followed by an error message in case of error). If the second optional\r
+-- argument is given, then only the value of the named attribute is returned\r
+-- (this use is equivalent to lfs.attributes(filepath).aname, but the table is\r
+-- not created and only one attribute is retrieved from the O.S.). The\r
+-- attributes are described as follows; attribute mode is a string, all the\r
+-- others are numbers, and the time related attributes use the same time\r
+-- reference of os.time:\r
+--\r
+-- - dev: on Unix systems, this represents the device that the inode resides on.\r
+-- On Windows systems, represents the drive number of the disk containing\r
+-- the file\r
+-- - ino: on Unix systems, this represents the inode number. On Windows systems\r
+-- this has no meaning\r
+-- - mode: string representing the associated protection mode (the values could\r
+-- be file, directory, link, socket, named pipe, char device, block\r
+-- device or other)\r
+-- - nlink: number of hard links to the file\r
+-- - uid: user-id of owner (Unix only, always 0 on Windows)\r
+-- - gid: group-id of owner (Unix only, always 0 on Windows)\r
+-- - rdev: on Unix systems, represents the device type, for special file inodes.\r
+-- On Windows systems represents the same as dev\r
+-- - access: time of last access\r
+-- - modification: time of last data modification\r
+-- - change: time of last file status change\r
+-- - size: file size, in bytes\r
+-- - blocks: block allocated for file; (Unix only)\r
+-- - blksize: optimal file system I/O blocksize; (Unix only)\r
+-- This function uses stat internally thus if the given filepath is a symbolic\r
+-- link, it is followed (if it points to another link the chain is followed\r
+-- recursively) and the information is about the file it refers to. To obtain\r
+-- information about the link itself, see function lfs.symlinkattributes.\r
+function lfs.attributes(filepath , aname) end\r
+\r
+---\r
+-- Changes the current working directory to the given path.\r
+-- Returns true in case of success or nil plus an error string.\r
+function lfs.chdir(path) end\r
+\r
+---\r
+-- Creates a lockfile (called lockfile.lfs) in path if it does not exist and\r
+-- returns the lock. If the lock already exists checks it it's stale, using the\r
+-- second parameter (default for the second parameter is INT_MAX, which in\r
+-- practice means the lock will never be stale. To free the the lock call\r
+-- lock:free().\r
+-- In case of any errors it returns nil and the error message. In particular,\r
+-- if the lock exists and is not stale it returns the "File exists" message.\r
+function lfs.lock_dir(path, seconds_stale) end\r
+\r
+---\r
+-- Returns a string with the current working directory or nil plus an error\r
+-- string.\r
+function lfs.currentdir() end\r
+\r
+---\r
+-- Lua iterator over the entries of a given directory. Each time the iterator is\r
+-- called with dir_obj it returns a directory entry's name as a string, or nil\r
+-- if there are no more entries. You can also iterate by calling `dir_obj:next()`,\r
+-- and explicitly close the directory before the iteration finished with\r
+-- `dir_obj:close()`. Raises an error if path is not a directory.\r
+function lfs.dir(path) end\r
+\r
+---\r
+-- Locks a file or a part of it. This function works on open files; the file\r
+-- handle should be specified as the first argument. The string mode could be\r
+-- either r (for a read/shared lock) or w (for a write/exclusive lock). The\r
+-- optional arguments start and length can be used to specify a starting point\r
+-- and its length; both should be numbers.\r
+-- Returns true if the operation was successful; in case of error, it returns\r
+-- nil plus an error string.\r
+function lfs.lock(filehandle, mode, start, length)\r
+\r
+---\r
+-- Creates a new directory. The argument is the name of the new directory.\r
+-- Returns true if the operation was successful; in case of error, it returns\r
+-- nil plus an error string.\r
+function lfs.mkdir(dirname) end\r
+\r
+---\r
+-- Removes an existing directory. The argument is the name of the directory.\r
+-- Returns true if the operation was successful; in case of error, it returns\r
+-- nil plus an error string.\r
+function lfs.rmdir(dirname) end\r
+\r
+---\r
+-- Sets the writing mode for a file. The mode string can be either binary or\r
+-- text. Returns the previous mode string for the file. This function is only\r
+-- available in Windows, so you may want to make sure that lfs.setmode exists\r
+-- before using it.\r
+function lfs.setmode(file, mode) end\r
+\r
+---\r
+-- Identical to lfs.attributes except that it obtains information about the link\r
+-- itself (not the file it refers to). This function is not available in Windows\r
+-- so you may want to make sure that lfs.symlinkattributes exists before using\r
+-- it.\r
+function lfs.symlinkattributes(filepath , aname) end\r
+\r
+---\r
+-- Set access and modification times of a file. This function is a bind to utime\r
+-- function. The first argument is the filename, the second argument (atime) is\r
+-- the access time, and the third argument (mtime) is the modification time.\r
+-- Both times are provided in seconds (which should be generated with Lua\r
+-- standard function os.time). If the modification time is omitted, the access\r
+-- time provided is used; if both times are omitted, the current time is used.\r
+-- Returns true if the operation was successful; in case of error, it returns\r
+-- nil plus an error string.\r
+function lfs.touch(filepath , atime , mtime) end\r
+\r
+---\r
+-- Unlocks a file or a part of it. This function works on open files; the file\r
+-- handle should be specified as the first argument. The optional arguments\r
+-- start and length can be used to specify a starting point and its length; both\r
+-- should be numbers.\r
+-- Returns true if the operation was successful; in case of error, it returns\r
+-- nil plus an error string.\r
+function lfs.unlock(filehandle, start, length) end\r
--- /dev/null
+--- LPeg PEG pattern matching.\r
+\r
+module 'lpeg'\r
+\r
+---\r
+-- The matching function. It attempts to match the given pattern against the\r
+-- subject string. If the match succeeds, returns the index in the subject of\r
+-- the first character after the match, or the captured values (if the pattern\r
+-- captured any value).\r
+--\r
+-- An optional numeric argument init makes the match starts at that position in\r
+-- the subject string. As usual in Lua libraries, a negative value counts from\r
+-- the end.\r
+--\r
+-- Unlike typical pattern-matching functions, match works only in anchored mode;\r
+-- that is, it tries to match the pattern with a prefix of the given subject\r
+-- string (at position init), not with an arbitrary substring of the subject.\r
+-- So, if we want to find a pattern anywhere in a string, we must either write a\r
+-- loop in Lua or write a pattern that matches anywhere. This second approach is\r
+-- easy and quite efficient; see examples.\r
+function lpeg.match(pattern, subject , init) end\r
+\r
+---\r
+-- If the given value is a pattern, returns the string "pattern". Otherwise\r
+-- returns nil.\r
+function lpeg.type(value) end\r
+\r
+---\r
+-- Returns a string with the running version of LPeg.\r
+function lpeg.version() end\r
+\r
+---\r
+-- Sets the maximum size for the backtrack stack used by LPeg to track calls and\r
+-- choices. Most well-written patterns need little backtrack levels and\r
+-- therefore you seldom need to change this maximum; but a few useful patterns\r
+-- may need more space. Before changing this maximum you should try to rewrite\r
+-- your pattern to avoid the need for extra space.\r
+function lpeg.setmaxstack(max) end\r
+\r
+---\r
+-- Converts the given value into a proper pattern, according to the following\r
+-- rules:\r
+-- * If the argument is a pattern, it is returned unmodified.\r
+-- * If the argument is a string, it is translated to a pattern that matches\r
+-- literally the string.\r
+-- * If the argument is a non-negative number n, the result is a pattern that\r
+-- matches exactly n characters.\r
+-- * If the argument is a negative number -n, the result is a pattern that\r
+-- succeeds only if the input string does not have n characters: lpeg.P(-n)\r
+-- is equivalent to -lpeg.P(n) (see the unary minus operation).\r
+-- * If the argument is a boolean, the result is a pattern that always\r
+-- succeeds or always fails (according to the boolean value), without\r
+-- consuming any input.\r
+-- * If the argument is a table, it is interpreted as a grammar (see\r
+-- Grammars).\r
+-- * If the argument is a function, returns a pattern equivalent to a\r
+-- match-time capture over the empty string.\r
+function lpeg.P(value) end\r
+\r
+---\r
+-- Returns a pattern that matches any single character belonging to one of the\r
+-- given ranges. Each range is a string xy of length 2, representing all\r
+-- characters with code between the codes of x and y (both inclusive).\r
+-- As an example, the pattern `lpeg.R("09")` matches any digit, and `lpeg.R("az",\r
+-- "AZ")` matches any ASCII letter.\r
+function lpeg.R({range}) end\r
+\r
+---\r
+-- Returns a pattern that matches any single character that appears in the given\r
+-- string. (The S stands for Set.)\r
+-- As an example, the pattern lpeg.S("+-*/") matches any arithmetic operator.\r
+-- Note that, if s is a character (that is, a string of length 1), then\r
+-- lpeg.P(s) is equivalent to lpeg.S(s) which is equivalent to lpeg.R(s..s).\r
+-- Note also that both lpeg.S("") and lpeg.R() are patterns that always fail.\r
+function lpeg.S(string) end\r
+\r
+---\r
+-- This operation creates a non-terminal (a variable) for a grammar. The created\r
+-- non-terminal refers to the rule indexed by v in the enclosing grammar. (See\r
+-- Grammars for details.)\r
+function lpeg.V(v) end\r
+\r
+---\r
+-- Returns a table with patterns for matching some character classes according\r
+-- to the current locale. The table has fields:\r
+--\r
+-- * alnum\r
+-- * alpha\r
+-- * cntrl\r
+-- * digit\r
+-- * graph\r
+-- * lower\r
+-- * print\r
+-- * punct\r
+-- * space\r
+-- * upper\r
+-- * xdigit\r
+--\r
+-- each one containing a\r
+-- correspondent pattern. Each pattern matches any single character that belongs\r
+-- to its class.\r
+--\r
+-- If called with an argument table, then it creates those fields inside the\r
+-- given table and returns that table.\r
+function lpeg.locale(table) end\r
+\r
+---\r
+-- Creates a simple capture, which captures the substring of the subject that\r
+-- matches patt. The captured value is a string. If patt has other captures,\r
+-- their values are returned after this one.\r
+function lpeg.C(patt) end\r
+\r
+---\r
+-- Creates an argument capture. This pattern matches the empty string and\r
+-- produces the value given as the nth extra argument given in the call to\r
+-- lpeg.match.\r
+function lpeg.Carg(n) end\r
+\r
+---\r
+-- Creates a back capture. This pattern matches the empty string and produces\r
+-- the values produced by the most recent group capture named name.\r
+-- Most recent means the last complete outermost group capture with the given\r
+-- name. A Complete capture means that the entire pattern corresponding to the\r
+-- capture has matched. An Outermost capture means that the capture is not\r
+-- inside another complete capture.\r
+function lpeg.Cb(name) end\r
+\r
+---\r
+-- Creates a constant capture. This pattern matches the empty string and\r
+-- produces all given values as its captured values.\r
+function lpeg.Cc(...) end\r
+\r
+---\r
+-- Creates a fold capture. If patt produces a list of captures C1 C2 ... Cn,\r
+-- this capture will produce the value func(...func(func(C1, C2), C3)..., Cn),\r
+-- that is, it will fold (or accumulate, or reduce) the captures from patt using\r
+-- function func.\r
+--\r
+-- This capture assumes that patt should produce at least one capture with at\r
+-- least one value (of any type), which becomes the initial value of an\r
+-- accumulator. (If you need a specific initial value, you may prefix a constant\r
+-- capture to patt.) For each subsequent capture LPeg calls func with this\r
+-- accumulator as the first argument and all values produced by the capture as\r
+-- extra arguments; the value returned by this call becomes the new value for\r
+-- the accumulator. The final value of the accumulator becomes the captured\r
+-- value.\r
+--\r
+-- As an example, the following pattern matches a list of numbers separated by\r
+-- commas and returns their addition:\r
+--\r
+-- -- matches a numeral and captures its value\r
+-- number = lpeg.R"09"^1 / tonumber\r
+-- -- matches a list of numbers, captures their values\r
+-- list = number * ("," * number)^0\r
+-- -- auxiliary function to add two numbers\r
+-- function add (acc, newvalue) return acc + newvalue end\r
+-- -- folds the list of numbers adding them\r
+-- sum = lpeg.Cf(list, add)\r
+-- -- example of use\r
+-- print(sum:match("10,30,43")) --> 83\r
+--\r
+function lpeg.Cf(patt, func) end\r
+\r
+---\r
+-- Creates a group capture. It groups all values returned by patt into a single\r
+-- capture. The group may be anonymous (if no name is given) or named with the\r
+-- given name.\r
+-- An anonymous group serves to join values from several captures into a single\r
+-- capture. A named group has a different behavior. In most situations, a named\r
+-- group returns no values at all. Its values are only relevant for a following\r
+-- back capture or when used inside a table capture.\r
+function lpeg.Cg(patt , name) end\r
+\r
+---\r
+-- Creates a position capture. It matches the empty string and captures the\r
+-- position in the subject where the match occurs. The captured value is a\r
+-- number.\r
+function lpeg.Cp() end\r
+\r
+---\r
+-- Creates a substitution capture, which captures the substring of the subject\r
+-- that matches patt, with substitutions. For any capture inside patt with a\r
+-- value, the substring that matched the capture is replaced by the capture\r
+-- value (which should be a string). The final captured value is the string\r
+-- resulting from all replacements.\r
+function lpeg.Cs(patt) end\r
+\r
+---\r
+-- Creates a table capture. This capture creates a table and puts all values\r
+-- from all anonymous captures made by patt inside this table in successive\r
+-- integer keys, starting at 1. Moreover, for each named capture group created\r
+-- by patt, the first value of the group is put into the table with the group\r
+-- name as its key. The captured value is only the table.\r
+function lpeg.Ct(patt) end\r
+\r
+---\r
+-- Creates a match-time capture. Unlike all other captures, this one is\r
+-- evaluated immediately when a match occurs. It forces the immediate evaluation\r
+-- of all its nested captures and then calls function.\r
+-- The given function gets as arguments the entire subject, the current position\r
+-- (after the match of patt), plus any capture values produced by patt.\r
+-- The first value returned by function defines how the match happens. If the\r
+-- call returns a number, the match succeeds and the returned number becomes the\r
+-- new current position. (Assuming a subject s and current position i, the\r
+-- returned number must be in the range [i, len(s) + 1].) If the call returns\r
+-- true, the match succeeds without consuming any input. (So, to return true is\r
+-- equivalent to return i.) If the call returns false, nil, or no value, the\r
+-- match fails.\r
+-- Any extra values returned by the function become the values produced by the\r
+-- capture.\r
+function lpeg.Cmt(patt, function) end\r
--- /dev/null
+--- standard mathematical functions.\r
+\r
+module 'math'\r
+\r
+---\r
+-- Returns the absolute value of `x`.\r
+function math.abs(x) end\r
+\r
+---\r
+-- Returns the arc cosine of `x` (in radians).\r
+function math.acos(x) end\r
+\r
+---\r
+-- Returns the arc sine of `x` (in radians).\r
+function math.asin(x) end\r
+\r
+---\r
+-- Returns the arc tangent of `x` (in radians).\r
+function math.atan(x) end\r
+\r
+---\r
+-- Returns the arc tangent of `y/x` (in radians), but uses the signs\r
+-- of both parameters to find the quadrant of the result. (It also handles\r
+-- correctly the case of `x` being zero.)\r
+function math.atan2(y, x) end\r
+\r
+---\r
+-- Returns the smallest integer larger than or equal to `x`.\r
+function math.ceil(x) end\r
+\r
+---\r
+-- Returns the cosine of `x` (assumed to be in radians).\r
+function math.cos(x) end\r
+\r
+---\r
+-- Returns the hyperbolic cosine of `x`.\r
+function math.cosh(x) end\r
+\r
+---\r
+-- Returns the angle `x` (given in radians) in degrees.\r
+function math.deg(x) end\r
+\r
+---\r
+-- Returns the value *e^x*.\r
+function math.exp(x) end\r
+\r
+---\r
+-- Returns the largest integer smaller than or equal to `x`.\r
+function math.floor(x) end\r
+\r
+---\r
+-- Returns the remainder of the division of `x` by `y` that rounds the\r
+-- quotient towards zero.\r
+function math.fmod(x, y) end\r
+\r
+---\r
+-- Returns `m` and `e` such that *x = m2^e*, `e` is an integer and the\r
+-- absolute value of `m` is in the range *[0.5, 1)* (or zero when `x` is zero).\r
+function math.frexp(x) end\r
+\r
+---\r
+-- The value `HUGE_VAL`, a value larger than or equal to any other\r
+-- numerical value.\r
+-- function math.huge end\r
+-- * `math.HUGE_VAL`: math.HUGE_VAL\r
+\r
+---\r
+-- Returns *m2^e* (`e` should be an integer).\r
+function math.ldexp(m, e) end\r
+\r
+---\r
+-- Returns the natural logarithm of `x`.\r
+function math.log(x) end\r
+\r
+---\r
+-- Returns the base-10 logarithm of `x`.\r
+function math.log10(x) end\r
+\r
+---\r
+-- Returns the maximum value among its arguments.\r
+function math.max(x, ...) end\r
+\r
+---\r
+-- Returns the minimum value among its arguments.\r
+function math.min(x, ...) end\r
+\r
+---\r
+-- Returns two numbers, the integral part of `x` and the fractional part of\r
+-- `x`.\r
+function math.modf(x) end\r
+\r
+---\r
+-- The value of *pi*.\r
+-- function math.pi end\r
+-- * `math.pi`: math.pi\r
+\r
+---\r
+-- Returns *x^y*. (You can also use the expression `x^y` to compute this\r
+-- value.)\r
+function math.pow(x, y) end\r
+\r
+---\r
+-- Returns the angle `x` (given in degrees) in radians.\r
+function math.rad(x) end\r
+\r
+---\r
+-- This function is an interface to the simple pseudo-random generator\r
+-- function `rand` provided by ANSI C. (No guarantees can be given for its\r
+-- statistical properties.)\r
+-- When called without arguments, returns a uniform pseudo-random real\r
+-- number in the range *[0,1)*. When called with an integer number `m`,\r
+-- `math.random` returns a uniform pseudo-random integer in the range *[1,\r
+-- m]*. When called with two integer numbers `m` and `n`, `math.random`\r
+-- returns a uniform pseudo-random integer in the range *[m, n]*.\r
+function math.random(m , n) end\r
+\r
+---\r
+-- Sets `x` as the "seed" for the pseudo-random generator: equal seeds\r
+-- produce equal sequences of numbers.\r
+function math.randomseed(x) end\r
+\r
+---\r
+-- Returns the sine of `x` (assumed to be in radians).\r
+function math.sin(x) end\r
+\r
+---\r
+-- Returns the hyperbolic sine of `x`.\r
+function math.sinh(x) end\r
+\r
+---\r
+-- Returns the square root of `x`. (You can also use the expression `x^0.5`\r
+-- to compute this value.)\r
+function math.sqrt(x) end\r
+\r
+---\r
+-- Returns the tangent of `x` (assumed to be in radians).\r
+function math.tan(x) end\r
+\r
+---\r
+-- Returns the hyperbolic tangent of `x`.\r
+function math.tanh(x) end\r
+\r
--- /dev/null
+--- Operating System facilities like date, time and program execution.\r
+\r
+module 'os'\r
+\r
+---\r
+-- Returns an approximation of the amount in seconds of CPU time used by\r
+-- the program.\r
+function os.clock() end\r
+\r
+---\r
+-- Returns a string or a table containing date and time, formatted according\r
+-- to the given string `format`.\r
+--\r
+-- If the `time` argument is present, this is the time to be formatted\r
+-- (see the `os.time` function for a description of this value). Otherwise,\r
+-- `date` formats the current time.\r
+--\r
+-- If `format` starts with '`!`', then the date is formatted in Coordinated\r
+-- Universal Time. After this optional character, if `format` is the string\r
+-- "`*t`", then `date` returns a table with the following fields:\r
+--\r
+-- * `year` (four digits)\r
+-- * `month` (1--12)\r
+-- * `day` (1--31)\r
+-- * `hour` (0--23)\r
+-- * `min` (0--59)\r
+-- * `sec` (0--61)\r
+-- * `wday` (weekday, Sunday is 1)\r
+-- * `yday` (day of the year)\r
+-- * `isdst` (daylight saving flag, a boolean).\r
+--\r
+-- If `format` is not "`*t`", then `date` returns the date as a string,\r
+-- formatted according to the same rules as the C function `strftime`.\r
+-- When called without arguments, `date` returns a reasonable date and time\r
+-- representation that depends on the host system and on the current locale\r
+-- (that is, `os.date()` is equivalent to `os.date("%c")`).\r
+function os.date(format , time) end\r
+\r
+---\r
+-- Returns the number of seconds from time `t1` to time `t2`. In POSIX,\r
+-- Windows, and some other systems, this value is exactly `t2`*-*`t1`.\r
+function os.difftime(t2, t1) end\r
+\r
+---\r
+-- This function is equivalent to the C function `system`. It passes\r
+-- `command` to be executed by an operating system shell. It returns a status\r
+-- code, which is system-dependent. If `command` is absent, then it returns\r
+-- nonzero if a shell is available and zero otherwise.\r
+function os.execute(command) end\r
+\r
+---\r
+-- Calls the C function `exit`, with an optional `code`, to terminate the\r
+-- host program. The default value for `code` is the success code.\r
+function os.exit(code) end\r
+\r
+---\r
+-- Returns the value of the process environment variable `varname`, or\r
+-- nil if the variable is not defined.\r
+function os.getenv(varname) end\r
+\r
+---\r
+-- Deletes the file or directory with the given name. Directories must be\r
+-- empty to be removed. If this function fails, it returns nil, plus a string\r
+-- describing the error.\r
+function os.remove(filename) end\r
+\r
+---\r
+-- Renames file or directory named `oldname` to `newname`. If this function\r
+-- fails, it returns nil, plus a string describing the error.\r
+function os.rename(oldname, newname) end\r
+\r
+---\r
+-- Sets the current locale of the program. `locale` is a string specifying\r
+-- a locale; `category` is an optional string describing which category to\r
+-- change: `"all"`, `"collate"`, `"ctype"`, `"monetary"`, `"numeric"`, or\r
+-- `"time"`; the default category is `"all"`. The function returns the name\r
+-- of the new locale, or nil if the request cannot be honored.\r
+-- If `locale` is the empty string, the current locale is set to an\r
+-- implementation-defined native locale. If `locale` is the string "`C`",\r
+-- the current locale is set to the standard C locale.\r
+-- When called with nil as the first argument, this function only returns\r
+-- the name of the current locale for the given category.\r
+function os.setlocale(locale , category) end\r
+\r
+---\r
+-- Returns the current time when called without arguments, or a time\r
+-- representing the date and time specified by the given table. This table\r
+-- must have fields `year`, `month`, and `day`, and may have fields `hour`,\r
+-- `min`, `sec`, and `isdst` (for a description of these fields, see the\r
+-- `os.date` function).\r
+-- The returned value is a number, whose meaning depends on your system. In\r
+-- POSIX, Windows, and some other systems, this number counts the number\r
+-- of seconds since some given start time (the "epoch"). In other systems,\r
+-- the meaning is not specified, and the number returned by `time` can be\r
+-- used only as an argument to `date` and `difftime`.\r
+function os.time(table) end\r
+\r
+---\r
+-- Returns a string with a file name that can be used for a temporary\r
+-- file. The file must be explicitly opened before its use and explicitly\r
+-- removed when no longer needed.\r
+-- On some systems (POSIX), this function also creates a file with that\r
+-- name, to avoid security risks. (Someone else might create the file with\r
+-- wrong permissions in the time between getting the name and creating the\r
+-- file.) You still have to open the file to use it and to remove it (even\r
+-- if you do not use it).\r
+-- When possible, you may prefer to use `io.tmpfile`, which automatically\r
+-- removes the file when the program ends.\r
+function os.tmpname() end\r
+\r
--- /dev/null
+--- controlling how `require` finds packages.\r
+\r
+module 'package'\r
+\r
+---\r
+-- The path used by `require` to search for a C loader.\r
+-- Lua initializes the C path `package.cpath` in the same way it initializes\r
+-- the Lua path `package.path`, using the environment variable `LUA_CPATH`\r
+-- or a default path defined in `luaconf.h`.\r
+-- function package.cpath end\r
+-- * `package.cpath`: package.cpath\r
+\r
+---\r
+-- A table used by `require` to control which modules are already\r
+-- loaded. When you require a module `modname` and `package.loaded[modname]`\r
+-- is not false, `require` simply returns the value stored there.\r
+-- function package.loaded end\r
+-- * `package.loaded`: package.loaded\r
+\r
+---\r
+-- A table used by `require` to control how to load modules.\r
+-- Each entry in this table is a *searcher function*. When looking for a module,\r
+-- `require` calls each of these searchers in ascending order, with the module\r
+-- name (the argument given to `require`) as its sole parameter. The function\r
+-- can return another function (the module *loader*) or a string explaining\r
+-- why it did not find that module (or nil if it has nothing to say). Lua\r
+-- initializes this table with four functions.\r
+-- The first searcher simply looks for a loader in the `package.preload` table.\r
+-- The second searcher looks for a loader as a Lua library, using the path\r
+-- stored at `package.path`. A path is a sequence of *templates* separated by\r
+-- semicolons. For each template, the searcher will change each interrogation\r
+-- mark in the template by `filename`, which is the module name with each dot\r
+-- replaced by a "directory separator" (such as "`/`" in Unix); then it will\r
+-- try to open the resulting file name. So, for instance, if the Lua path is\r
+-- the string\r
+-- "./?.lua;./?.lc;/usr/local/?/init.lua"\r
+-- the search for a Lua file for module `foo` will try to open the files\r
+-- `./foo.lua`, `./foo.lc`, and `/usr/local/foo/init.lua`, in that order.\r
+-- The third searcher looks for a loader as a C library, using the path given\r
+-- by the variable `package.cpath`. For instance, if the C path is the string\r
+-- "./?.so;./?.dll;/usr/local/?/init.so"\r
+-- the searcher for module `foo` will try to open the files `./foo.so`,\r
+-- `./foo.dll`, and `/usr/local/foo/init.so`, in that order. Once it finds\r
+-- a C library, this searcher first uses a dynamic link facility to link the\r
+-- application with the library. Then it tries to find a C function inside the\r
+-- library to be used as the loader. The name of this C function is the string\r
+-- "`luaopen_`" concatenated with a copy of the module name where each dot\r
+-- is replaced by an underscore. Moreover, if the module name has a hyphen,\r
+-- its prefix up to (and including) the first hyphen is removed. For instance,\r
+-- if the module name is `a.v1-b.c`, the function name will be `luaopen_b_c`.\r
+-- The fourth searcher tries an *all-in-one loader*. It searches the C\r
+-- path for a library for the root name of the given module. For instance,\r
+-- when requiring `a.b.c`, it will search for a C library for `a`. If found,\r
+-- it looks into it for an open function for the submodule; in our example,\r
+-- that would be `luaopen_a_b_c`. With this facility, a package can pack\r
+-- several C submodules into one single library, with each submodule keeping\r
+-- its original open function.\r
+-- function package.loaders end\r
+-- * `package.loaders`: package.loaders\r
+\r
+---\r
+-- Dynamically links the host program with the C library `libname`. Inside\r
+-- this library, looks for a function `funcname` and returns this function as a\r
+-- C function. (So, `funcname` must follow the protocol (see `lua_CFunction`)).\r
+-- This is a low-level function. It completely bypasses the package and module\r
+-- system. Unlike `require`, it does not perform any path searching and does\r
+-- not automatically adds extensions. `libname` must be the complete file name\r
+-- of the C library, including if necessary a path and extension. `funcname`\r
+-- must be the exact name exported by the C library (which may depend on the\r
+-- C compiler and linker used).\r
+-- This function is not supported by ANSI C. As such, it is only available\r
+-- on some platforms (Windows, Linux, Mac OS X, Solaris, BSD, plus other Unix\r
+-- systems that support the `dlfcn` standard).\r
+function package.loadlib(libname, funcname) end\r
+\r
+---\r
+-- The path used by `require` to search for a Lua loader.\r
+-- At start-up, Lua initializes this variable with the value of the environment\r
+-- variable `LUA_PATH` or with a default path defined in `luaconf.h`, if\r
+-- the environment variable is not defined. Any "`;;`" in the value of the\r
+-- environment variable is replaced by the default path.\r
+-- function package.path end\r
+-- * `package.path`: package.path\r
+\r
+---\r
+-- A table to store loaders for specific modules (see `require`).\r
+-- function package.preload end\r
+-- * `package.preload`: package.preload\r
+\r
+---\r
+-- Sets a metatable for `module` with its `__index` field referring to the\r
+-- global environment, so that this module inherits values from the global\r
+-- environment. To be used as an option to function `module`.\r
+function package.seeall(module) end\r
+\r
--- /dev/null
+--- string operations like searching and matching.\r
+\r
+module 'string'\r
+\r
+---\r
+-- Returns the internal numerical codes of the characters `s[i]`, `s[i+1]`,\r
+-- ..., `s[j]`. The default value for `i` is 1; the default value for `j`\r
+-- is `i`.\r
+-- Note that numerical codes are not necessarily portable across platforms.\r
+function string.byte(s , i , j) end\r
+\r
+---\r
+-- Receives zero or more integers. Returns a string with length equal to\r
+-- the number of arguments, in which each character has the internal numerical\r
+-- code equal to its corresponding argument.\r
+-- Note that numerical codes are not necessarily portable across platforms.\r
+function string.char(...) end\r
+\r
+---\r
+-- Returns a string containing a binary representation of the given\r
+-- function, so that a later `loadstring` on this string returns a copy of\r
+-- the function. `function` must be a Lua function without upvalues.\r
+function string.dump(function) end\r
+\r
+---\r
+-- Looks for the first match of `pattern` in the string `s`. If it finds a\r
+-- match, then `find` returns the indices of `s` where this occurrence starts\r
+-- and ends; otherwise, it returns nil. A third, optional numerical argument\r
+-- `init` specifies where to start the search; its default value is 1 and\r
+-- can be negative. A value of true as a fourth, optional argument `plain`\r
+-- turns off the pattern matching facilities, so the function does a plain\r
+-- "find substring" operation, with no characters in `pattern` being considered\r
+-- "magic". Note that if `plain` is given, then `init` must be given as well.\r
+-- If the pattern has captures, then in a successful match the captured values\r
+-- are also returned, after the two indices.\r
+function string.find(s, pattern , init , plain) end\r
+\r
+---\r
+-- Returns a formatted version of its variable number of arguments following\r
+-- the description given in its first argument (which must be a string). The\r
+-- format string follows the same rules as the `printf` family of standard C\r
+-- functions. The only differences are that the options/modifiers `*`, `l`,\r
+-- `L`, `n`, `p`, and `h` are not supported and that there is an extra option,\r
+-- `q`. The `q` option formats a string in a form suitable to be safely read\r
+-- back by the Lua interpreter: the string is written between double quotes,\r
+-- and all double quotes, newlines, embedded zeros, and backslashes in the\r
+-- string are correctly escaped when written. For instance, the call\r
+--\r
+-- string.format('%q', 'a string with "quotes" and \n new line')\r
+--\r
+-- will produce the string:\r
+--\r
+-- "a string with \"quotes\" and \\r
+-- new line"\r
+--\r
+-- The options `c`, `d`, `E`, `e`, `f`, `g`, `G`, `i`, `o`, `u`, `X`, and\r
+-- `x` all expect a number as argument, whereas `q` and `s` expect a string.\r
+-- This function does not accept string values containing embedded zeros,\r
+-- except as arguments to the `q` option.\r
+function string.format(formatstring, ...) end\r
+\r
+---\r
+-- Returns an iterator function that, each time it is called, returns the\r
+-- next captures from `pattern` over string `s`. If `pattern` specifies no\r
+-- captures, then the whole match is produced in each call.\r
+-- As an example, the following loop\r
+--\r
+-- s = "hello world from Lua"\r
+-- for w in string.gmatch(s, "%a+") do\r
+-- print(w)\r
+-- end\r
+--\r
+-- will iterate over all the words from string `s`, printing one per line. The\r
+-- next example collects all pairs `key=value` from the given string into\r
+-- a table:\r
+--\r
+-- t = {}\r
+-- s = "from=world, to=Lua"\r
+-- for k, v in string.gmatch(s, "(%w+)=(%w+)") do\r
+-- t[k] = v\r
+-- end\r
+--\r
+-- For this function, a '`^`' at the start of a pattern does not work as an\r
+-- anchor, as this would prevent the iteration.\r
+function string.gmatch(s, pattern) end\r
+\r
+---\r
+-- Returns a copy of `s` in which all (or the first `n`, if given)\r
+-- occurrences of the `pattern` have been replaced by a replacement string\r
+-- specified by `repl`, which can be a string, a table, or a function. `gsub`\r
+-- also returns, as its second value, the total number of matches that occurred.\r
+--\r
+-- If `repl` is a string, then its value is used for replacement. The character\r
+-- `%` works as an escape character: any sequence in `repl` of the form `%n`,\r
+-- with *n* between 1 and 9, stands for the value of the *n*-th captured\r
+-- substring (see below). The sequence `%0` stands for the whole match. The\r
+-- sequence `%%` stands for a single `%`.\r
+--\r
+-- If `repl` is a table, then the table is queried for every match, using\r
+-- the first capture as the key; if the pattern specifies no captures, then\r
+-- the whole match is used as the key.\r
+--\r
+-- If `repl` is a function, then this function is called every time a match\r
+-- occurs, with all captured substrings passed as arguments, in order; if\r
+-- the pattern specifies no captures, then the whole match is passed as a\r
+-- sole argument.\r
+--\r
+-- If the value returned by the table query or by the function call is a\r
+-- string or a number, then it is used as the replacement string; otherwise,\r
+-- if it is false or nil, then there is no replacement (that is, the original\r
+-- match is kept in the string).\r
+--\r
+-- Here are some examples:\r
+-- x = string.gsub("hello world", "(%w+)", "%1 %1")\r
+-- --> x="hello hello world world"\r
+-- x = string.gsub("hello world", "%w+", "%0 %0", 1)\r
+-- --> x="hello hello world"\r
+-- x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")\r
+-- --> x="world hello Lua from"\r
+-- x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)\r
+-- --> x="home = /home/roberto, user = roberto"\r
+-- x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)\r
+-- return loadstring(s)()\r
+-- end)\r
+-- --> x="4+5 = 9"\r
+-- local t = {name="lua", version="5.1"}\r
+-- x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)\r
+-- --> x="lua-5.1.tar.gz"\r
+function string.gsub(s, pattern, repl , n) end\r
+\r
+---\r
+-- Receives a string and returns its length. The empty string `""` has\r
+-- length 0. Embedded zeros are counted, so `"a\000bc\000"` has length 5.\r
+function string.len(s) end\r
+\r
+---\r
+-- Receives a string and returns a copy of this string with all uppercase\r
+-- letters changed to lowercase. All other characters are left unchanged. The\r
+-- definition of what an uppercase letter is depends on the current locale.\r
+function string.lower(s) end\r
+\r
+---\r
+-- Looks for the first *match* of `pattern` in the string `s`. If it\r
+-- finds one, then `match` returns the captures from the pattern; otherwise\r
+-- it returns nil. If `pattern` specifies no captures, then the whole match\r
+-- is returned. A third, optional numerical argument `init` specifies where\r
+-- to start the search; its default value is 1 and can be negative.\r
+function string.match(s, pattern , init) end\r
+\r
+---\r
+-- Returns a string that is the concatenation of `n` copies of the string\r
+-- `s`.\r
+function string.rep(s, n) end\r
+\r
+---\r
+-- Returns a string that is the string `s` reversed.\r
+function string.reverse(s) end\r
+\r
+---\r
+-- Returns the substring of `s` that starts at `i` and continues until\r
+-- `j`; `i` and `j` can be negative. If `j` is absent, then it is assumed to\r
+-- be equal to -1 (which is the same as the string length). In particular,\r
+-- the call `string.sub(s,1,j)` returns a prefix of `s` with length `j`, and\r
+-- `string.sub(s, -i)` returns a suffix of `s` with length `i`.\r
+function string.sub(s, i , j) end\r
+\r
+---\r
+-- Receives a string and returns a copy of this string with all lowercase\r
+-- letters changed to uppercase. All other characters are left unchanged. The\r
+-- definition of what a lowercase letter is depends on the current locale.\r
+function string.upper(s) end\r
+\r
--- /dev/null
+--- manipulating Lua tables.\r
+\r
+module 'table'\r
+\r
+---\r
+-- Given an array where all elements are strings or numbers, returns\r
+-- `table[i]..sep..table[i+1] ... sep..table[j]`. The default value for\r
+-- `sep` is the empty string, the default for `i` is 1, and the default for\r
+-- `j` is the length of the table. If `i` is greater than `j`, returns the\r
+-- empty string.\r
+function table.concat(table , sep , i , j) end\r
+\r
+---\r
+-- Inserts element `value` at position `pos` in `table`, shifting up\r
+-- other elements to open space, if necessary. The default value for `pos` is\r
+-- `n+1`, where `n` is the length of the table (see §2.5.5), so that a call\r
+-- `table.insert(t,x)` inserts `x` at the end of table `t`.\r
+function table.insert(table, pos, value) end\r
+\r
+---\r
+-- Returns the largest positive numerical index of the given table, or\r
+-- zero if the table has no positive numerical indices. (To do its job this\r
+-- function does a linear traversal of the whole table.)\r
+function table.maxn(table) end\r
+\r
+---\r
+-- Removes from `table` the element at position `pos`, shifting down other\r
+-- elements to close the space, if necessary. Returns the value of the removed\r
+-- element. The default value for `pos` is `n`, where `n` is the length of the\r
+-- table, so that a call `table.remove(t)` removes the last element of table\r
+-- `t`.\r
+function table.remove(table , pos) end\r
+\r
+---\r
+-- Sorts table elements in a given order,\r
+-- *in-place*, from `table[1]` to `table[n]`, where `n` is the length of the\r
+-- table. If `comp` is given, then it must be a function that receives two\r
+-- table elements, and returns true when the first is less than the second\r
+-- (so that `not comp(a[i+1],a[i])` will be true after the sort). If `comp`\r
+-- is not given, then the '<' operator will be used.\r
+function table.sort(table , comp) end\r
--- /dev/null
+------
+-- Defining the ldoc document model.
+
+
+require 'pl'
+
+local doc = {}
+local global = require 'ldoc.builtin.globals'
+local tools = require 'ldoc.tools'
+local split_dotted_name = tools.split_dotted_name
+
+-- these are the basic tags known to ldoc. They come in several varieties:
+-- - tags with multiple values like 'param' (TAG_MULTI)
+-- - tags which are identifiers, like 'name' (TAG_ID)
+-- - tags with a single value, like 'release' (TAG_SINGLE)
+-- - tags which represent a type, like 'function' (TAG_TYPE)
+local known_tags = {
+ param = 'M', see = 'M', usage = 'M', ['return'] = 'M', field = 'M', author='M';
+ class = 'id', name = 'id', pragma = 'id', alias = 'id';
+ copyright = 'S', summary = 'S', description = 'S', release = 'S', license = 'S',
+ fixme = 'S', todo = 'S', warning = 'S', raise = 'S';
+ module = 'T', script = 'T', example = 'T', topic = 'T', -- project-level
+ ['function'] = 'T', lfunction = 'T', table = 'T', section = 'T', type = 'T',
+ annotation = 'T'; -- module-level
+ ['local'] = 'N', export = 'N';
+}
+known_tags._alias = {}
+known_tags._project_level = {
+ module = true,
+ script = true,
+ example = true,
+ topic = true
+}
+
+local TAG_MULTI,TAG_ID,TAG_SINGLE,TAG_TYPE,TAG_FLAG = 'M','id','S','T','N'
+doc.TAG_MULTI,doc.TAG_ID,doc.TAG_SINGLE,doc.TAG_TYPE,doc.TAG_FLAG =
+ TAG_MULTI,TAG_ID,TAG_SINGLE,TAG_TYPE,TAG_FLAG
+
+-- add a new tag.
+function doc.add_tag(tag,type,project_level)
+ if not known_tags[tag] then
+ known_tags[tag] = type
+ known_tags._project_level[tag] = project_level
+ end
+end
+
+-- add an alias to an existing tag (exposed through ldoc API)
+function doc.add_alias (a,tag)
+ known_tags._alias[a] = tag
+end
+
+-- get the tag alias value, if it exists.
+function doc.get_alias(tag)
+ return known_tags._alias[tag]
+end
+
+-- is it a'project level' tag, such as 'module' or 'script'?
+function doc.project_level(tag)
+ return known_tags._project_level[tag]
+end
+
+-- is it a section tag, like 'type' (class) or 'section'?
+function doc.section_tag (tag)
+ return tag == 'section' or tag == 'type'
+end
+
+
+-- annotation tags can appear anywhere in the code and may contain of these tags:
+known_tags._annotation_tags = {
+ fixme = true, todo = true, warning = true
+}
+
+local acount = 1
+
+function doc.expand_annotation_item (tags, last_item)
+ if tags.summary ~= '' then return false end
+ for tag, value in pairs(tags) do
+ if known_tags._annotation_tags[tag] then
+ tags.class = 'annotation'
+ tags.summary = value
+ local item_name = last_item and last_item.tags.name or '?'
+ tags.name = item_name..'-'..tag..acount
+ acount = acount + 1
+ return true
+ end
+ end
+end
+
+-- we process each file, resulting in a File object, which has a list of Item objects.
+-- Items can be modules, scripts ('project level') or functions, tables, etc.
+-- (In the code 'module' refers to any project level tag.)
+-- When the File object is finalized, we specialize some items as modules which
+-- are 'container' types containing functions and tables, etc.
+
+local File = class()
+local Item = class()
+local Module = class(Item) -- a specialized kind of Item
+
+doc.File = File
+doc.Item = Item
+doc.Module = Module
+
+function File:_init(filename)
+ self.filename = filename
+ self.items = List()
+ self.modules = List()
+ self.sections = List()
+end
+
+function File:new_item(tags,line)
+ local item = Item(tags,self,line or 1)
+ self.items:append(item)
+ return item
+end
+
+function File:export_item (name)
+ for item in self.items:iter() do
+ local tags = item.tags
+ if tags.name == name then
+ if tags['local'] then
+ tags['local'] = false
+ end
+ end
+ end
+end
+
+function File:finish()
+ local this_mod
+ local items = self.items
+ for item in items:iter() do
+ item:finish()
+ if doc.project_level(item.type) then
+ this_mod = item
+ local package,mname
+ if item.type == 'module' then
+ -- if name is 'package.mod', then mod_name is 'mod'
+ package,mname = split_dotted_name(this_mod.name)
+ end
+ if not package then
+ mname = this_mod.name
+ package = ''
+ end
+ self.modules:append(this_mod)
+ this_mod.package = package
+ this_mod.mod_name = mname
+ this_mod.kinds = ModuleMap() -- the iterator over the module contents
+ elseif doc.section_tag(item.type) then
+ local display_name = item.name
+ if display_name == 'end' then
+ this_mod.section = nil
+ else
+ local summary = item.summary:gsub('%.$','')
+ if item.type == 'type' then
+ display_name = 'Class '..item.name
+ item.module = this_mod
+ this_mod.items.by_name[item.name] = item
+ else
+ display_name = summary
+ end
+ item.display_name = display_name
+ this_mod.section = item
+ this_mod.kinds:add_kind(display_name,display_name)
+ this_mod.sections:append(item)
+ this_mod.sections.by_name[display_name:gsub('%A','_')] = item
+ end
+ else
+ -- add the item to the module's item list
+ if this_mod then
+ -- new-style modules will have qualified names like 'mod.foo'
+ local mod,fname = split_dotted_name(item.name)
+ -- warning for inferred unqualified names in new style modules
+ -- (retired until we handle methods like Set:unset() properly)
+ if not mod and not this_mod.old_style and item.inferred then
+ --item:warning(item.name .. ' is declared in global scope')
+ end
+ -- the function may be qualified with a module alias...
+ local alias = this_mod.tags.alias
+ if (alias and mod == alias) or mod == 'M' or mod == '_M' then
+ mod = this_mod.mod_name
+ end
+ -- if that's the mod_name, then we want to only use 'foo'
+ if mod == this_mod.mod_name and this_mod.tags.pragma ~= 'nostrip' then
+ item.name = fname
+ end
+
+ -- right, this item was within a section or a 'class'
+ local section_description
+ if this_mod.section then
+ item.section = this_mod.section.display_name
+ -- if it was a class, then the name should be 'Class:foo'
+ if this_mod.section.type == 'type' then
+ local prefix = this_mod.section.name .. ':'
+ local i1,i2 = item.name:find(prefix)
+ if not (i1 == 1 and i2 == #prefix) then
+ item.name = prefix .. item.name
+ end
+ end
+ section_description = this_mod.section.description
+ else -- otherwise, just goes into the default sections (Functions,Tables,etc)
+ item.section = item.type
+ end
+
+ item.module = this_mod
+ local these_items = this_mod.items
+ these_items.by_name[item.name] = item
+ these_items:append(item)
+
+ this_mod.kinds:add(item,these_items,section_description)
+
+ else
+ -- must be a free-standing function (sometimes a problem...)
+ end
+ end
+ end
+end
+
+-- some serious hackery. We force sections into this 'module',
+-- and ensure that there is a dummy item so that the section
+-- is not empty.
+
+function File:add_document_section(title)
+ local section = title:gsub('%A','_')
+ self:new_item {
+ name = section,
+ class = 'section',
+ summary = title
+ }
+ self:new_item {
+ name = 'dumbo',
+ class = 'function',
+ }
+ return section
+end
+
+function Item:_init(tags,file,line)
+ self.file = file
+ self.lineno = line
+ self.summary = tags.summary
+ self.description = tags.description
+ tags.summary = nil
+ tags.description = nil
+ self.tags = {}
+ self.formal_args = tags.formal_args
+ tags.formal_args = nil
+ for tag,value in pairs(tags) do
+ self:set_tag(tag,value)
+ end
+end
+
+function Item:set_tag (tag,value)
+ local ttype = known_tags[tag]
+ if ttype == TAG_MULTI then
+ if getmetatable(value) ~= List then
+ value = List{value}
+ end
+ self.tags[tag] = value
+ elseif ttype == TAG_ID then
+ if type(value) ~= 'string' then
+ -- such tags are _not_ multiple, e.g. name
+ self:error("'"..tag.."' cannot have multiple values")
+ else
+ self.tags[tag] = tools.extract_identifier(value)
+ end
+ elseif ttype == TAG_SINGLE then
+ self.tags[tag] = value
+ elseif ttype == TAG_FLAG then
+ self.tags[tag] = true
+ else
+ Item.warning(self,"unknown tag: '"..tag.."' "..tostring(ttype))
+ end
+end
+
+-- preliminary processing of tags. We check for any aliases, and for tags
+-- which represent types. This implements the shortcut notation.
+function Item.check_tag(tags,tag, value, modifiers)
+ local alias = doc.get_alias(tag)
+ if alias then
+ if type(alias) == 'string' then
+ tag = alias
+ else
+ local avalue,amod
+ tag, avalue, amod = alias[1],alias.value,alias.modifiers
+ if avalue then value = avalue..' '..value end
+ if amod then
+ modifiers = modifiers or {}
+ for m,v in pairs(amod) do
+ local idx = v:match('^%$(%d+)')
+ if idx then
+ v, value = value:match('(%S+)(.*)')
+ end
+ modifiers[m] = v
+ end
+ end
+ end
+ end
+ local ttype = known_tags[tag]
+ if ttype == TAG_TYPE then
+ tags.class = tag
+ tag = 'name'
+ end
+ return tag, value, modifiers
+end
+
+-- any tag (except name and classs) may have associated modifiers,
+-- in the form @tag[m1,...] where m1 is either name1=value1 or name1.
+-- At this stage, these are encoded
+-- in the tag value table and need to be extracted.
+
+local function extract_value_modifier (p)
+ if type(p)=='string' then
+ return p, { }
+ elseif type(p) == 'table' then
+ return p[1], p.modifiers or { }
+ else
+ return 'que?',{}
+ end
+end
+
+local function extract_tag_modifiers (tags)
+ local modifiers = {}
+ for tag, value in pairs(tags) do
+ if type(value)=='table' and value.append then
+ local tmods = {}
+ for i, v in ipairs(value) do
+ v, mods = extract_value_modifier(v)
+ tmods[i] = mods
+ value[i] = v
+ end
+ modifiers[tag] = tmods
+ else
+ value, mods = extract_value_modifier(value)
+ modifiers[tag] = mods
+ tags[tag] = value
+ end
+ end
+ return modifiers
+end
+
+local function read_del (tags,name)
+ local ret = tags[name]
+ tags[name] = nil
+ return ret
+end
+
+
+function Item:finish()
+ local tags = self.tags
+ local quote = tools.quote
+ self.name = read_del(tags,'name')
+ self.type = read_del(tags,'class')
+ self.modifiers = extract_tag_modifiers(tags)
+ self.usage = read_del(tags,'usage')
+ -- see tags are multiple, but they may also be comma-separated
+ if tags.see then
+ tags.see = tools.expand_comma_list(read_del(tags,'see'))
+ end
+ if doc.project_level(self.type) then
+ -- we are a module, so become one!
+ self.items = List()
+ self.sections = List()
+ self.items.by_name = {}
+ self.sections.by_name = {}
+ setmetatable(self,Module)
+ elseif not doc.section_tag(self.type) then
+ -- params are either a function's arguments, or a table's fields, etc.
+ if self.type == 'function' then
+ self.parameter = 'param'
+ self.ret = read_del(tags,'return')
+ self.raise = read_del(tags,'raise')
+ if tags['local'] then
+ self.type = 'lfunction'
+ end
+ else
+ self.parameter = 'field'
+ end
+ local params = read_del(tags,self.parameter)
+ local names, comments, modifiers = List(), List(), List()
+ if params then
+ for line in params:iter() do
+ local name, comment = line :match('%s*([%w_%.:]+)(.*)')
+ assert(name, "bad param name format")
+ names:append(name)
+ comments:append(comment)
+ end
+ end
+ -- not all arguments may be commented: we use the formal arguments
+ -- if available as the authoritative list, and warn if there's an inconsistency.
+ if self.formal_args then
+ local fargs = self.formal_args
+ if #fargs ~= 1 then
+ local pnames, pcomments = names, comments
+ names, comments = List(),List()
+ local varargs = fargs[#fargs] == '...'
+ for i,name in ipairs(fargs) do
+ if params then -- explicit set of param tags
+ if pnames[i] ~= name and not varargs then
+ if pnames[i] then
+ self:warning("param and formal argument name mismatch: "..quote(name).." "..quote(pnames[i]))
+ else
+ self:warning("undocumented formal argument: "..quote(name))
+ end
+ elseif varargs then
+ name = pnames[i]
+ end
+ end
+ names:append(name)
+ -- ldoc allows comments in the formal arg list to be used
+ comments:append (fargs.comments[name] or pcomments[i] or '')
+ end
+ -- A formal argument of ... may match any number of params, however.
+ if #pnames > #fargs then
+ for i = #fargs+1,#pnames do
+ if not varargs then
+ self:warning("extra param with no formal argument: "..quote(pnames[i]))
+ else
+ names:append(pnames[i])
+ comments:append(pcomments[i] or '')
+ end
+ end
+ end
+ end
+ end
+
+ -- the comments are associated with each parameter by
+ -- adding name-value pairs to the params list (this is
+ -- also done for any associated modifiers)
+ self.params = names
+ local pmods = self.modifiers[self.parameter]
+ for i,name in ipairs(self.params) do
+ self.params[name] = comments[i]
+ if pmods then
+ pmods[name] = pmods[i]
+ end
+ end
+
+ -- build up the string representation of the argument list,
+ -- using any opt and optchain modifiers if present.
+ -- For instance, '(a [, b])' if b is marked as optional
+ -- with @param[opt] b
+ local buffer, npending = { }, 0
+ local function acc(x) table.insert(buffer, x) end
+ for i = 1, #names do
+ local m = pmods and pmods[i]
+ if m then
+ if not m.optchain then
+ acc ((']'):rep(npending))
+ npending=0
+ end
+ if m.opt or m.optchain then acc('['); npending=npending+1 end
+ end
+ if i>1 then acc (', ') end
+ acc(names[i])
+ end
+ acc ((']'):rep(npending))
+ self.args = '('..table.concat(buffer)..')'
+ end
+end
+
+function Item:type_of_param(p)
+ local mods = self.modifiers[self.parameter]
+ if not mods then return '' end
+ local mparam = mods[p]
+ return mparam and mparam.type or ''
+end
+
+function Item:type_of_ret(idx)
+ local rparam = self.modifiers['return'][idx]
+ return rparam and rparam.type or ''
+end
+
+
+
+function Item:warning(msg)
+ local file = self.file and self.file.filename
+ if type(file) == 'table' then pretty.dump(file); file = '?' end
+ file = file or '?'
+ io.stderr:write(file,':',self.lineno or '1',': ',self.name or '?',': ',msg,'\n')
+ return nil
+end
+
+function Item:error(msg)
+ self:warning(msg)
+ os.exit(1)
+end
+
+Module.warning, Module.error = Item.warning, Item.error
+
+function Module:hunt_for_reference (packmod, modules)
+ local mod_ref
+ local package = self.package or ''
+ repeat -- same package?
+ local nmod = package..'.'..packmod
+ mod_ref = modules.by_name[nmod]
+ if mod_ref then break end -- cool
+ package = split_dotted_name(package)
+ until not package
+ return mod_ref
+end
+
+local function reference (s, mod_ref, item_ref)
+ local name = item_ref and item_ref.name or ''
+ -- this is deeply hacky; classes have 'Class ' prepended.
+ if item_ref and item_ref.type == 'type' then
+ name = 'Class_'..name
+ end
+ return {mod = mod_ref, name = name, label=s}
+end
+
+function Module:process_see_reference (s,modules)
+ local mod_ref,fun_ref,name,packmod
+ if not s:match '^[%w_%.%:%-]+$' or not s:match '[%w_]$' then
+ return nil, "malformed see reference: '"..s..'"'
+ end
+ -- is this a fully qualified module name?
+ local mod_ref = modules.by_name[s]
+ if mod_ref then return reference(s, mod_ref,nil) end
+ -- module reference?
+ mod_ref = self:hunt_for_reference(s, modules)
+ if mod_ref then return mod_ref end
+ -- method reference? (These are of form CLASS.NAME)
+ fun_ref = self.items.by_name[s]
+ if fun_ref then return reference(s,self,fun_ref) end
+ -- otherwise, start splitting!
+ local packmod,name = split_dotted_name(s) -- e.g. 'pl.utils','split'
+ if packmod then -- qualified name
+ mod_ref = modules.by_name[packmod] -- fully qualified mod name?
+ if not mod_ref then
+ mod_ref = self:hunt_for_reference(packmod, modules)
+ if not mod_ref then
+ local ref = global.lua_manual_ref(s)
+ if ref then return ref end
+ return nil,"module not found: "..packmod
+ end
+ end
+ fun_ref = mod_ref.items.by_name[name]
+ if fun_ref then
+ return reference(s,mod_ref,fun_ref)
+ else
+ fun_ref = mod_ref.sections.by_name[name]
+ if not fun_ref then
+ return nil,"function or section not found: "..s.." in "..mod_ref.name
+ else
+ return reference(fun_ref.name:gsub('_',' '),mod_ref,fun_ref)
+ end
+ end
+ else -- plain jane name; module in this package, function in this module
+ mod_ref = modules.by_name[self.package..'.'..s]
+ if mod_ref then return reference(s, mod_ref,nil) end
+ fun_ref = self.items.by_name[s]
+ if fun_ref then return reference(s, self,fun_ref)
+ else
+ local ref = global.lua_manual_ref (s)
+ if ref then return ref end
+ return nil, "function not found: "..s.." in this module"
+ end
+ end
+end
+
+-- resolving @see references. A word may be either a function in this module,
+-- or a module in this package. A MOD.NAME reference is within this package.
+-- Otherwise, the full qualified name must be used.
+-- First, check whether it is already a fully qualified module name.
+-- Then split it and see if the module part is a qualified module
+-- and try look up the name part in that module.
+-- If this isn't successful then try prepending the current package to the reference,
+-- and try to to resolve this.
+function Module:resolve_references(modules)
+ local found = List()
+ for item in self.items:iter() do
+ local see = item.tags.see
+ if see then -- this guy has @see references
+ item.see = List()
+ for s in see:iter() do
+ local href, err = self:process_see_reference(s,modules)
+ if href then
+ item.see:append (href)
+ found:append{item,s}
+ elseif err then
+ item:warning(err)
+ end
+ end
+ end
+ end
+ -- mark as found, so we don't waste time re-searching
+ for f in found:iter() do
+ f[1].tags.see:remove_value(f[2])
+ end
+end
+
+-- suppress the display of local functions and annotations.
+-- This is just a placeholder hack until we have a more general scheme
+-- for indicating 'private' content of a module.
+function Module:mask_locals ()
+ self.kinds['Local Functions'] = nil
+ self.kinds['Annotations'] = nil
+end
+
+function Item:dump_tags (taglist)
+ for tag, value in pairs(self.tags) do
+ if not taglist or taglist[tag] then
+ Item.warning(self,tag..' '..tostring(value))
+ end
+ end
+end
+
+function Module:dump_tags (taglist)
+ Item.dump_tags(self,taglist)
+ for item in self.items:iter() do
+ item:dump_tags(taglist)
+ end
+end
+
+--------- dumping out modules and items -------------
+
+function Module:dump(verbose)
+ if self.type ~= 'module' then return end
+ print '----'
+ print(self.type..':',self.name,self.summary)
+ if self.description then print(self.description) end
+ for item in self.items:iter() do
+ item:dump(verbose)
+ end
+end
+
+-- make a text dump of the contents of this File object.
+-- The level of detail is controlled by the 'verbose' parameter.
+-- Primarily intended as a debugging tool.
+function File:dump(verbose)
+ for mod in self.modules:iter() do
+ mod:dump(verbose)
+ end
+end
+
+function Item:dump(verbose)
+ local tags = self.tags
+ local name = self.name
+ if self.type == 'function' then
+ name = name .. self.args
+ end
+ if verbose then
+ print()
+ print(self.type,name)
+ print(self.summary)
+ if self.description and self.description:match '%S' then
+ print 'description:'
+ print(self.description)
+ end
+ if #self.params > 0 then
+ print 'parameters:'
+ for _,p in ipairs(self.params) do
+ print('',p,self.params[p])
+ end
+ end
+ if self.ret and #self.ret > 0 then
+ print 'returns:'
+ for _,r in ipairs(self.ret) do
+ print('',r)
+ end
+ end
+ if next(self.tags) then
+ print 'tags:'
+ for tag, value in pairs(self.tags) do
+ print(tag,value)
+ end
+ end
+ else
+ print('* '..name..' - '..self.summary)
+ end
+end
+
+function doc.filter_objects_through_function(filter, module_list)
+ local quit, quote = utils.quit, tools.quote
+ if filter == 'dump' then filter = 'pl.pretty.dump' end
+ local mod,name = tools.split_dotted_name(filter)
+ local ok,P = pcall(require,mod)
+ if not ok then quit("cannot find module "..quote(mod)) end
+ local ok,f = pcall(function() return P[name] end)
+ if not ok or type(f) ~= 'function' then quit("dump module: no function "..quote(name)) end
+
+ -- clean up some redundant and cyclical references--
+ module_list.by_name = nil
+ for mod in module_list:iter() do
+ mod.kinds = nil
+ mod.file = mod.file.filename
+ for item in mod.items:iter() do
+ item.module = nil
+ item.file = nil
+ item.formal_args = nil
+ item.tags['return'] = nil
+ item.see = nil
+ end
+ mod.items.by_name = nil
+ end
+
+ local ok,err = pcall(f,module_list)
+ if not ok then quit("dump failed: "..err) end
+end
+
+return doc
+
--- /dev/null
+------ generating HTML output ---------
+-- Although this can be generalized for outputting any format, since the template
+-- is language-agnostic, this implementation concentrates on HTML.
+-- This does the actual generation of HTML, and provides support functions in the ldoc
+-- table for the template
+--
+-- A fair amount of the complexity comes from operating in two basic modes; first, where
+-- there is a number of modules (classic LuaDoc) or otherwise, where there is only one
+-- module and the index contains the documentation for that module.
+--
+-- Like LuaDoc, LDoc puts similar kinds of documentation files in their own directories.
+-- So module docs go into 'modules/', scripts go into 'scripts/', and so forth. LDoc
+-- generalizes the idea of these project-level categories and in fact custom categories
+-- can be created (refered to as 'kinds' in the code)
+
+local template = require 'pl.template'
+local tools = require 'ldoc.tools'
+local markup = require 'ldoc.markup'
+local html = {}
+
+
+local quit = utils.quit
+
+function html.generate_output(ldoc, args, project)
+ local check_directory, check_file, writefile = tools.check_directory, tools.check_file, tools.writefile
+
+
+ -- this generates the internal module/function references
+ function ldoc.href(see)
+ if see.href then -- explict reference, e.g. to Lua manual
+ return see.href
+ else
+ return ldoc.ref_to_module(see.mod)..'#'..see.name
+ end
+ end
+
+ -- this is either called from the 'root' (index or single module) or
+ -- from the 'modules' etc directories. If we are in one of those directories,
+ -- then linking to another kind is `../kind/name`; to the same kind is just `name`.
+ -- If we are in the root, then it is `kind/name`.
+ function ldoc.ref_to_module (mod)
+ local base = "" -- default: same directory
+ mod = mod or ldoc.module
+ local kind, module = mod.kind, ldoc.module
+ local name = mod.name -- default: name of module
+ if not ldoc.single then
+ if module then -- we are in kind/
+ if module.type ~= type then -- cross ref to ../kind/
+ base = "../"..kind.."/"
+ end
+ else -- we are in root: index
+ base = kind..'/'
+ end
+ else -- single module
+ if mod == ldoc.single then
+ name = ldoc.output
+ if not ldoc.root then base = '../' end
+ elseif ldoc.root then -- ref to other kinds (like examples)
+ base = kind..'/'
+ else
+ if module.type ~= type then -- cross ref to ../kind/
+ base = "../"..kind.."/"
+ end
+ end
+ end
+ return base..name..'.html'
+ end
+
+ function ldoc.use_li(ls)
+ if #ls > 1 then return '<li>','</li>' else return '','' end
+ end
+
+ function ldoc.display_name(item)
+ local name = item.display_name or item.name
+ if item.type == 'function' then return name..' '..item.args
+ else return name end
+ end
+
+ function ldoc.no_spaces(s) return (s:gsub('%A','_')) end
+
+ function ldoc.titlecase(s)
+ return (s:gsub('(%a)(%a*)',function(f,r)
+ return f:upper()..r
+ end))
+ end
+
+ function ldoc.typename (tp)
+ if not tp then return '' end
+ return (tp:gsub('%a[%w_%.]*',function(name)
+ local ref,err = markup.process_reference(name)
+ if ref then
+ return ('<a href="%s">%s</a> '):format(ldoc.href(ref),name)
+ else
+ return '<strong>'..name..'</strong> '
+ end
+ end))
+ end
+
+ local module_template,err = utils.readfile (path.join(args.template,ldoc.templ))
+ if not module_template then
+ quit("template not found at '"..args.template.."' Use -l to specify directory containing ldoc.ltp")
+ end
+
+ local css = ldoc.css
+ ldoc.output = args.output
+ ldoc.ipairs = ipairs
+
+ -- in single mode there is one module and the 'index' is the
+ -- documentation for that module.
+ ldoc.module = ldoc.single
+ if ldoc.single and args.one then
+ ldoc.kinds_allowed = {module = true, topic = true}
+ end
+ ldoc.root = true
+ local out,err = template.substitute(module_template,{
+ ldoc = ldoc,
+ module = ldoc.module
+ })
+ ldoc.root = false
+ if not out then quit("template failed: "..err) end
+
+ check_directory(args.dir) -- make sure output directory is ok
+
+ args.dir = args.dir .. path.sep
+
+ check_file(args.dir..css, path.join(args.style,css)) -- has CSS been copied?
+
+ -- write out the module index
+ writefile(args.dir..args.output..args.ext,out)
+
+ -- in single mode, we exclude any modules since the module has been done;
+ -- this step is then only for putting out any examples or topics
+ local mods = List()
+ for kind, modules in project() do
+ local lkind = kind:lower()
+ if not ldoc.single or ldoc.single and lkind ~= 'modules' then
+ mods:append {kind, lkind, modules}
+ end
+ end
+
+ -- write out the per-module documentation
+ -- note that we reset the internal ordering of the 'kinds' so that
+ -- e.g. when reading a topic the other Topics will be listed first.
+ ldoc.css = '../'..css
+ for m in mods:iter() do
+ local kind, lkind, modules = unpack(m)
+ check_directory(args.dir..lkind)
+ project:put_kind_first(kind)
+ for m in modules() do
+ ldoc.module = m
+ ldoc.body = m.body
+ if ldoc.body and m.postprocess then
+ ldoc.body = m.postprocess(ldoc.body)
+ end
+ out,err = template.substitute(module_template,{
+ module=m,
+ ldoc = ldoc
+ })
+ if not out then
+ quit('template failed for '..m.name..': '..err)
+ else
+ writefile(args.dir..lkind..'/'..m.name..args.ext,out)
+ end
+ end
+ end
+ if not args.quiet then print('output written to '..tools.abspath(args.dir)) end
+end
+
+return html
+
--- /dev/null
+return [==[
+/* BEGIN RESET
+
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.8.2r1
+*/
+html {
+ color: #000;
+ background: #FFF;
+}
+body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td {
+ margin: 0;
+ padding: 0;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+fieldset,img {
+ border: 0;
+}
+address,caption,cite,code,dfn,em,strong,th,var,optgroup {
+ font-style: inherit;
+ font-weight: inherit;
+}
+del,ins {
+ text-decoration: none;
+}
+li {
+ list-style: bullet;
+ margin-left: 20px;
+}
+caption,th {
+ text-align: left;
+}
+h1,h2,h3,h4,h5,h6 {
+ font-size: 100%;
+ font-weight: bold;
+}
+q:before,q:after {
+ content: '';
+}
+abbr,acronym {
+ border: 0;
+ font-variant: normal;
+}
+sup {
+ vertical-align: baseline;
+}
+sub {
+ vertical-align: baseline;
+}
+legend {
+ color: #000;
+}
+input,button,textarea,select,optgroup,option {
+ font-family: inherit;
+ font-size: inherit;
+ font-style: inherit;
+ font-weight: inherit;
+}
+input,button,textarea,select {*font-size:100%;
+}
+/* END RESET */
+
+body {
+ margin-left: 1em;
+ margin-right: 1em;
+ font-family: arial, helvetica, geneva, sans-serif;
+ background-color: #ffffff; margin: 0px;
+}
+
+code, tt { font-family: monospace; }
+
+body, p, td, th { font-size: .95em; line-height: 1.2em;}
+
+p, ul { margin: 10px 0 0 10px;}
+
+strong { font-weight: bold;}
+
+em { font-style: italic;}
+
+h1 {
+ font-size: 1.5em;
+ margin: 0 0 20px 0;
+}
+h2, h3, h4 { margin: 15px 0 10px 0; }
+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;
+ background: #00007f;
+ height: 1px;
+}
+
+blockquote { margin-left: 3em; }
+
+ul { list-style-type: disc; }
+
+p.name {
+ font-family: "Andale Mono", monospace;
+ padding-top: 1em;
+}
+
+pre.example {
+ background-color: rgb(245, 245, 245);
+ border: 1px solid silver;
+ padding: 10px;
+ margin: 10px 0 10px 0;
+ font-family: "Andale Mono", monospace;
+ font-size: .85em;
+}
+
+pre {
+ background-color: rgb(245, 245, 245);
+ border: 1px solid silver;
+ padding: 10px;
+ margin: 10px 0 10px 0;
+ overflow: auto;
+ font-family: "Andale Mono", monospace;
+}
+
+
+table.index { border: 1px #00007f; }
+table.index td { text-align: left; vertical-align: top; }
+
+#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;
+}
+
+#main {
+ background-color: #f0f0f0;
+ border-left: 2px solid #cccccc;
+}
+
+#navigation {
+ float: left;
+ width: 18em;
+ vertical-align: top;
+ background-color: #f0f0f0;
+ overflow: visible;
+}
+
+#navigation h2 {
+ background-color:#e7e7e7;
+ font-size:1.1em;
+ color:#000000;
+ text-align: left;
+ padding:0.2em;
+ border-top:1px solid #dddddd;
+ border-bottom:1px solid #dddddd;
+}
+
+#navigation ul
+{
+ font-size:1em;
+ list-style-type: none;
+ margin: 1px 1px 10px 1px;
+}
+
+#navigation li {
+ text-indent: -1em;
+ display: block;
+ margin: 3px 0px 0px 22px;
+}
+
+#navigation li li a {
+ margin: 0px 3px 0px -1em;
+}
+
+#content {
+ margin-left: 18em;
+ padding: 1em;
+ width: 700px;
+ border-left: 2px solid #cccccc;
+ border-right: 2px solid #cccccc;
+ background-color: #ffffff;
+}
+
+#about {
+ clear: both;
+ 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 {
+ background-color: #ffffff;
+ border-left: 0px;
+ }
+
+ #container {
+ margin-left: 2%;
+ margin-right: 2%;
+ background-color: #ffffff;
+ }
+
+ #content {
+ padding: 1em;
+ background-color: #ffffff;
+ }
+
+ #navigation {
+ display: none;
+ }
+ pre.example {
+ font-family: "Andale Mono", monospace;
+ font-size: 10pt;
+ page-break-inside: avoid;
+ }
+}
+
+table.module_list {
+ border-width: 1px;
+ border-style: solid;
+ border-color: #cccccc;
+ border-collapse: collapse;
+}
+table.module_list td {
+ border-width: 1px;
+ padding: 3px;
+ border-style: solid;
+ border-color: #cccccc;
+}
+table.module_list td.name { background-color: #f0f0f0; ; min-width: 200px; }
+table.module_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; ; min-width: 200px; }
+table.function_list td.summary { width: 100%; }
+
+dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
+dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
+dl.table h3, dl.function h3 {font-size: .95em;}
+
+/* stop sublists from having initial vertical space */
+ul ul { margin-top: 0px; }
+ol ul { margin-top: 0px; }
+ol ol { margin-top: 0px; }
+ul ol { margin-top: 0px; }
+
+/* styles for prettification of source */
+.keyword {font-weight: bold; color: #6666AA; }
+.number { color: #AA6666; }
+.string { color: #8888AA; }
+.comment { color: #666600; }
+.prepro { color: #006666; }
+.global { color: #800080; }
+]==]
+
+
--- /dev/null
+return [==[
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+<head>
+ <title>$(ldoc.title)</title>
+ <link rel="stylesheet" href="$(ldoc.css)" type="text/css" />
+</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">
+
+# local no_spaces = ldoc.no_spaces
+# local use_li = ldoc.use_li
+# local display_name = ldoc.display_name
+# local iter = ldoc.modules.iter
+# local M = ldoc.markup
+
+<!-- Menu -->
+
+<div id="navigation">
+<br/>
+<h1>$(ldoc.project)</h1>
+
+# if not ldoc.single and module then -- reference back to project index
+<ul>
+ <li><a href="../$(ldoc.output).html">Index</a></li>
+</ul>
+# end
+
+# --------- contents of module -------------
+# if module and not ldoc.no_summary and #module.items > 0 then
+<h2>Contents</h2>
+<ul>
+# for kind,items in module.kinds() do
+<li><a href="#$(no_spaces(kind))">$(kind)</a></li>
+# end
+</ul>
+# end
+
+
+# if ldoc.no_summary and module then -- bang out the functions on the side
+# for kind, items in module.kinds() do
+<h2>$(kind)</h2>
+<ul>
+# for item in items() do
+ <li><a href="#$(item.name)">$(display_name(item))</a></li>
+# end
+</ul>
+# end
+# end
+# -------- contents of project ----------
+# -- if not ldoc.no_summary then
+# local this_mod = module and module.name
+# for kind, mods, type in ldoc.kinds() do
+# if not ldoc.kinds_allowed or ldoc.kinds_allowed[type] then
+<h2>$(kind)</h2>
+<ul>
+# for mod in mods() do
+# if mod.name == this_mod then -- highlight current module, link to others
+ <li><strong>$(mod.name)</strong></li>
+# else
+ <li><a href="$(ldoc.ref_to_module(mod))">$(mod.name)</a></li>
+# end
+# end
+# end
+# -- end
+</ul>
+# end
+
+</div>
+
+<div id="content">
+
+#if module then
+<h1>$(ldoc.titlecase(module.type)) <code>$(module.name)</code></h1>
+# end
+
+# if ldoc.body then -- verbatim HTML as contents; 'non-code' entries
+ $(ldoc.body)
+# elseif module then -- module documentation
+<p>$(M(module.summary,module))</p>
+<p>$(M(module.description,module))</p>
+# if module.usage then
+# local li,il = use_li(module.usage)
+ <h3>Usage:</h3>
+ <ul>
+# for usage in iter(module.usage) do
+ $(li)<pre class="example">$(usage)</pre>$(il)
+# end -- for
+ </ul>
+# end -- if usage
+
+# if not ldoc.no_summary then
+# -- bang out the tables of item types for this module (e.g Functions, Tables, etc)
+# for kind,items in module.kinds() do
+<h2><a href="#$(no_spaces(kind))">$(kind)</a></h2>
+<table class="function_list">
+# for item in items() do
+ <tr>
+ <td class="name" nowrap><a href="#$(item.name)">$(display_name(item))</a></td>
+ <td class="summary">$(M(item.summary,item))</td>
+ </tr>
+# end -- for items
+</table>
+#end -- for kinds
+
+<br/>
+<br/>
+
+#end -- if not no_summary
+
+# --- currently works for both Functions and Tables. The params field either contains
+# --- function parameters or table fields.
+# local show_return = not ldoc.no_return_or_parms
+# local show_parms = show_return
+# for kind, items in module.kinds() do
+ <h2><a name="$(no_spaces(kind))"></a>$(kind)</h2>
+ $(M(module.kinds:get_section_description(kind),nil))
+ <dl class="function">
+# for item in items() do
+ <dt>
+ <a name = "$(item.name)"></a>
+ <strong>$(display_name(item))</strong>
+ </dt>
+ <dd>
+ $(M((item.summary or '?')..' '..(item.description or ''),item))
+
+# if show_parms and item.params and #item.params > 0 then
+ <h3>$(module.kinds:type_of(item).subnames):</h3>
+ <ul>
+# for p in iter(item.params) do
+# local tp = ldoc.typename(item:type_of_param(p))
+ <li><code><em>$(p)</em></code>: $(tp)$(M(item.params[p],item))</li>
+# end -- for
+ </ul>
+# end -- if params
+
+# if item.usage then
+# local li,il = use_li(item.usage)
+ <h3>Usage:</h3>
+ <ul>
+# for usage in iter(item.usage) do
+ $(li)<pre class="example">$(usage)</pre>$(il)
+# end -- for
+ </ul>
+# end -- if usage
+
+# if show_return and item.ret then
+# local li,il = use_li(item.ret)
+ <h3>Returns:</h3>
+ <ol>
+# for i,r in ldoc.ipairs(item.ret) do
+# local tp = ldoc.typename(item:type_of_ret(i))
+ $(li)$(tp)$(M(r,item))$(il)
+# end -- for
+ </ol>
+# end -- if returns
+
+# if show_return and item.raise then
+ <h3>Raises:</h3>
+ $(M(item.raise,item))
+# end
+
+# if item.see then
+# local li,il = use_li(item.see)
+ <h3>see also:</h3>
+ <ul>
+# for see in iter(item.see) do
+ $(li)<a href="$(ldoc.href(see))">$(see.label)</a>$(il)
+# end -- for
+ </ul>
+# end -- if see
+</dd>
+# end -- for items
+</dl>
+# end -- for kinds
+
+# else -- if module; project-level contents
+
+# if ldoc.description then
+ <h2>$(M(ldoc.description,nil))</h2>
+# end
+# if ldoc.full_description then
+ <p>$(M(ldoc.full_description,nil))</p>
+# end
+
+# for kind, mods in ldoc.kinds() do
+<h2>$(kind)</h2>
+# kind = kind:lower()
+<table class="module_list">
+# for m in mods() do
+ <tr>
+ <td class="name" nowrap><a href="$(no_spaces(kind))/$(m.name).html">$(m.name)</a></td>
+ <td class="summary">$(M(m.summary,m))</td>
+ </tr>
+# end -- for modules
+</table>
+# end -- for kinds
+# end -- if module
+
+</div> <!-- id="content" -->
+</div> <!-- id="main" -->
+<div id="about">
+<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.2</a></i>
+</div> <!-- id="about" -->
+</div> <!-- id="container" -->
+</body>
+</html>
+]==]
+
--- /dev/null
+return [==[\r
+/* BEGIN RESET\r
+\r
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.\r
+Code licensed under the BSD License:\r
+http://developer.yahoo.com/yui/license.html\r
+version: 2.8.2r1\r
+*/\r
+html {\r
+ color: #000;\r
+ background: #FFF;\r
+}\r
+body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td {\r
+ margin: 0;\r
+ padding: 0;\r
+}\r
+table {\r
+ border-collapse: collapse;\r
+ border-spacing: 0;\r
+}\r
+fieldset,img {\r
+ border: 0;\r
+}\r
+address,caption,cite,code,dfn,em,strong,th,var,optgroup {\r
+ font-style: inherit;\r
+ font-weight: inherit;\r
+}\r
+del,ins {\r
+ text-decoration: none;\r
+}\r
+li {\r
+ list-style: bullet;\r
+ margin-left: 20px;\r
+}\r
+caption,th {\r
+ text-align: left;\r
+}\r
+h1,h2,h3,h4,h5,h6 {\r
+ font-size: 100%;\r
+ font-weight: bold;\r
+}\r
+q:before,q:after {\r
+ content: '';\r
+}\r
+abbr,acronym {\r
+ border: 0;\r
+ font-variant: normal;\r
+}\r
+sup {\r
+ vertical-align: baseline;\r
+}\r
+sub {\r
+ vertical-align: baseline;\r
+}\r
+legend {\r
+ color: #000;\r
+}\r
+input,button,textarea,select,optgroup,option {\r
+ font-family: inherit;\r
+ font-size: inherit;\r
+ font-style: inherit;\r
+ font-weight: inherit;\r
+}\r
+input,button,textarea,select {*font-size:100%;\r
+}\r
+/* END RESET */\r
+\r
+body {\r
+ margin-left: 1em;\r
+ margin-right: 1em;\r
+ font-family: arial, helvetica, geneva, sans-serif;\r
+ background-color: #ffffff; margin: 0px;\r
+}\r
+\r
+code, tt { font-family: monospace; }\r
+\r
+body, p, td, th { font-size: .95em; line-height: 1.2em;}\r
+\r
+p, ul { margin: 10px 0 0 10px;}\r
+\r
+strong { font-weight: bold;}\r
+\r
+em { font-style: italic;}\r
+\r
+h1 {\r
+ font-size: 1.5em;\r
+ margin: 0 0 20px 0;\r
+}\r
+h2, h3, h4 { margin: 15px 0 10px 0; }\r
+h2 { font-size: 1.25em; }\r
+h3 { font-size: 1.15em; }\r
+h4 { font-size: 1.06em; }\r
+\r
+a:link { font-weight: bold; color: #004080; text-decoration: none; }\r
+a:visited { font-weight: bold; color: #2808FF; text-decoration: none; }\r
+a:link:hover { text-decoration: underline; }\r
+\r
+hr {\r
+ color:#cccccc;\r
+ background: #00007f;\r
+ height: 1px;\r
+}\r
+\r
+blockquote { margin-left: 3em; }\r
+\r
+ul { list-style-type: disc; }\r
+\r
+p.name {\r
+ font-family: "Andale Mono", monospace;\r
+ padding-top: 1em;\r
+}\r
+\r
+pre.example {\r
+ background-color: rgb(245, 245, 245);\r
+ border: 1px solid silver;\r
+ padding: 10px;\r
+ margin: 10px 0 10px 0;\r
+ font-family: "Andale Mono", monospace;\r
+ font-size: .85em;\r
+}\r
+\r
+pre {\r
+ background-color: rgb(245, 245, 245);\r
+ border: 1px solid silver;\r
+ padding: 10px;\r
+ margin: 10px 0 10px 0;\r
+ font-family: "Andale Mono", monospace;\r
+}\r
+\r
+\r
+table.index { border: 1px #00007f; }\r
+table.index td { text-align: left; vertical-align: top; }\r
+\r
+#container {\r
+ margin-left: 1em;\r
+ margin-right: 1em;\r
+ background-color: #f5f5f5;\r
+}\r
+\r
+#product {\r
+ text-align: center;\r
+ border-bottom: 1px solid #cccccc;\r
+ background-color: #ffffff;\r
+}\r
+\r
+#product big {\r
+ font-size: 2em;\r
+}\r
+\r
+#main {\r
+ border-left: 2px solid #cccccc;\r
+ border-right: 2px solid #cccccc;\r
+}\r
+\r
+#navigation {\r
+ float: top;\r
+ vertical-align: top;\r
+ background-color: #f5f5f5;\r
+ overflow: visible;\r
+}\r
+\r
+#navigation h2 {\r
+ background-color:#e7e7e7;\r
+ font-size:1.1em;\r
+ color:#000000;\r
+ text-align: left;\r
+ padding:0.2em;\r
+ border-top:1px solid #dddddd;\r
+ border-bottom:1px solid #dddddd;\r
+}\r
+\r
+#navigation ul\r
+{\r
+ font-size:1em;\r
+ list-style-type: none;\r
+ margin: 1px 1px 10px 1px;\r
+}\r
+\r
+#navigation li {\r
+ text-indent: -1em;\r
+ display: block;\r
+ margin: 3px 0px 0px 22px;\r
+}\r
+\r
+#navigation li li a {\r
+ margin: 0px 3px 0px -1em;\r
+}\r
+\r
+#content {\r
+ padding: 1em;\r
+ background-color: #ffffff;\r
+}\r
+\r
+#about {\r
+ clear: both;\r
+ padding: 5px;\r
+ border-top: 2px solid #cccccc;\r
+ background-color: #ffffff;\r
+}\r
+\r
+@media print {\r
+ body {\r
+ font: 12pt "Times New Roman", "TimeNR", Times, serif;\r
+ }\r
+ a { font-weight: bold; color: #004080; text-decoration: underline; }\r
+\r
+ #main {\r
+ background-color: #ffffff;\r
+ border-left: 0px;\r
+ }\r
+\r
+ #container {\r
+ margin-left: 2%;\r
+ margin-right: 2%;\r
+ background-color: #ffffff;\r
+ }\r
+\r
+ #content {\r
+ padding: 1em;\r
+ background-color: #ffffff;\r
+ }\r
+\r
+ #navigation {\r
+ display: none;\r
+ }\r
+ pre.example {\r
+ font-family: "Andale Mono", monospace;\r
+ font-size: 10pt;\r
+ page-break-inside: avoid;\r
+ }\r
+}\r
+\r
+table.module_list {\r
+ border-width: 1px;\r
+ border-style: solid;\r
+ border-color: #cccccc;\r
+ border-collapse: collapse;\r
+}\r
+table.module_list td {\r
+ border-width: 1px;\r
+ padding: 3px;\r
+ border-style: solid;\r
+ border-color: #cccccc;\r
+}\r
+table.module_list td.name { background-color: #f5f5f5; }\r
+table.module_list td.summary { width: 100%; }\r
+\r
+\r
+table.function_list {\r
+ border-width: 1px;\r
+ border-style: solid;\r
+ border-color: #cccccc;\r
+ border-collapse: collapse;\r
+}\r
+table.function_list td {\r
+ border-width: 1px;\r
+ padding: 3px;\r
+ border-style: solid;\r
+ border-color: #cccccc;\r
+}\r
+table.function_list td.name {\r
+ background-color: #f5f5f5;\r
+ white-space: normal; /* voids the "nowrap" in HTML */\r
+}\r
+table.function_list td.summary { width: 100%; }\r
+\r
+dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}\r
+dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}\r
+dl.table h3, dl.function h3 {font-size: .95em;}\r
+\r
+/* styles for prettification of source */\r
+.keyword {font-weight: bold; color: #6666AA; }\r
+.number { color: #AA6666; }\r
+.string { color: #8888AA; }\r
+.comment { color: #666600; }\r
+.prepro { color: #006666; }\r
+]==]\r
--- /dev/null
+------------
+-- Language-dependent parsing of code.
+-- This encapsulates the different strategies needed for parsing C and Lua
+-- source code.
+
+require 'pl'
+
+local tools = require 'ldoc.tools'
+local lexer = require 'ldoc.lexer'
+
+local tnext = lexer.skipws
+
+
+class.Lang()
+
+function Lang:trim_comment (s)
+ return s:gsub(self.line_comment,'')
+end
+
+function Lang:start_comment (v)
+ local line = v:match (self.start_comment_)
+ local block = v:match(self.block_comment)
+ return line or block, block
+end
+
+function Lang:empty_comment (v)
+ return v:match(self.empty_comment_)
+end
+
+function Lang:grab_block_comment(v,tok)
+ v = v:gsub(self.block_comment,'')
+ return tools.grab_block_comment(v,tok,self.end_comment)
+end
+
+function Lang:find_module(tok,t,v)
+ return '...',t,v
+end
+
+function Lang:item_follows(t,v)
+ return false
+end
+
+function Lang:finalize()
+ self.empty_comment_ = self.start_comment_..'%s*$'
+end
+
+function Lang:search_for_token (tok,type,value,t,v)
+ while t and not (t == type and v == value) do
+ if t == 'comment' and self:start_comment(v) then return nil,t,v end
+ t,v = tnext(tok)
+ end
+ return t ~= nil,t,v
+end
+
+function Lang:parse_extra (tags,tok)
+end
+
+function Lang:is_module_modifier ()
+ return false
+end
+
+function Lang:parse_module_modifier (tags, tok)
+ return nil, "@usage or @exports deduction not implemented for this language"
+end
+
+
+class.Lua(Lang)
+
+function Lua:_init()
+ self.line_comment = '^%-%-+' -- used for stripping
+ self.start_comment_ = '^%-%-%-+' -- used for doc comment line start
+ self.block_comment = '^%-%-%[=*%[%-+' -- used for block doc comments
+ self:finalize()
+end
+
+function Lua.lexer(fname)
+ local f,e = io.open(fname)
+ if not f then quit(e) end
+ return lexer.lua(f,{}),f
+end
+
+function Lua:grab_block_comment(v,tok)
+ local equals = v:match('^%-%-%[(=*)%[')
+ v = v:gsub(self.block_comment,'')
+ return tools.grab_block_comment(v,tok,'%]'..equals..'%]')
+end
+
+
+function Lua:parse_module_call(tok,t,v)
+ t,v = tnext(tok)
+ if t == '(' then t,v = tnext(tok) end
+ if t == 'string' then -- explicit name, cool
+ return v,t,v
+ elseif t == '...' then -- we have to guess!
+ return '...',t,v
+ end
+end
+
+-- If a module name was not provided, then we look for an explicit module()
+-- call. However, we should not try too hard; if we hit a doc comment then
+-- we should go back and process it. Likewise, module(...) also means
+-- that we must infer the module name.
+function Lua:find_module(tok,t,v)
+ local res
+ res,t,v = self:search_for_token(tok,'iden','module',t,v)
+ if not res then return nil,t,v end
+ return self:parse_module_call(tok,t,v)
+end
+
+local function parse_lua_parameters (tags,tok)
+ tags.formal_args = tools.get_parameters(tok)
+ tags.class = 'function'
+end
+
+local function parse_lua_function_header (tags,tok)
+ tags.name = tools.get_fun_name(tok)
+ parse_lua_parameters(tags,tok)
+end
+
+local function parse_lua_table (tags,tok)
+ tags.formal_args = tools.get_parameters(tok,'}',function(s)
+ return s == ',' or s == ';'
+ end)
+end
+
+--------------- function and variable inferrence -----------
+-- After a doc comment, there may be a local followed by:
+-- [1] (l)function: function NAME
+-- [2] (l)function: NAME = function
+-- [3] table: NAME = {
+-- [4] field: NAME = <anything else> (this is a module-level field)
+--
+-- Depending on the case successfully detected, returns a function which
+-- will be called later to fill in inferred item tags
+function Lua:item_follows(t,v,tok)
+ local parser, case
+ local is_local = t == 'keyword' and v == 'local'
+ if is_local then t,v = tnext(tok) end
+ if t == 'keyword' and v == 'function' then -- case [1]
+ case = 1
+ parser = parse_lua_function_header
+ elseif t == 'iden' then
+ local name,t,v = tools.get_fun_name(tok,v)
+ if t ~= '=' then return nil end -- probably invalid code...
+ t,v = tnext(tok)
+ if t == 'keyword' and v == 'function' then -- case [2]
+ tnext(tok) -- skip '('
+ case = 2
+ parser = function(tags,tok)
+ tags.name = name
+ parse_lua_parameters(tags,tok)
+ end
+ elseif t == '{' then -- case [3]
+ case = 3
+ parser = function(tags,tok)
+ tags.class = 'table'
+ tags.name = name
+ parse_lua_table (tags,tok)
+ end
+ else -- case [4]
+ case = 4
+ parser = function(tags)
+ tags.class = 'field'
+ tags.name = name
+ end
+ end
+ elseif t == 'keyword' and v == 'return' then -- case [5]
+ case = 5
+ if tnext(tok) ~= '{' then
+ return nil
+ end
+ parser = function(tags,tok)
+ tags.class = 'table'
+ parse_lua_table(tags,tok)
+ end
+ end
+ return parser, is_local, case
+end
+
+
+-- we only call the function returned by the item_follows above if there
+-- is not already a name and a type.
+-- Otherwise, this is called. Currrently only tries to fill in the fields
+-- of a table from a table definition as identified above
+function Lua:parse_extra (tags,tok,case)
+ if tags.class == 'table' and not tags.field and case == 3 then
+ parse_lua_table(tags,tok)
+ end
+end
+
+-- For Lua, a --- @usage comment means that a long
+-- string containing the usage follows, which we
+-- use to update the module usage tag. Likewise, the @export
+-- tag alone in a doc comment refers to the following returned
+-- Lua table of functions
+
+
+function Lua:is_module_modifier (tags)
+ return tags.summary == '' and (tags.usage or tags.export)
+end
+
+function Lua:parse_module_modifier (tags, tok, F)
+ if tags.usage then
+ if tags.class ~= 'field' then return nil,"cannot deduce @usage" end
+ local t1= tnext(tok)
+ --local t2 = tok()
+ if t1 ~= '[' then return nil, t1..' '..': not a long string' end
+ t, v = tools.grab_block_comment('',tok,'%]%]')
+ return true, v, 'usage'
+ elseif tags.export then
+ if tags.class ~= 'table' then return nil, "cannot deduce @export" end
+ for f in tags.formal_args:iter() do
+ F:export_item(f)
+ end
+ return true
+ end
+end
+
+
+-- note a difference here: we scan C/C++ code in full-text mode, not line by line.
+-- This is because we can't detect multiline comments in line mode
+
+class.CC(Lang)
+
+function CC:_init()
+ self.line_comment = '^//+'
+ self.start_comment_ = '^///+'
+ self.block_comment = '^/%*%*+'
+ self:finalize()
+end
+
+function CC.lexer(f)
+ f,err = utils.readfile(f)
+ if not f then quit(err) end
+ return lexer.cpp(f,{})
+end
+
+function CC:grab_block_comment(v,tok)
+ v = v:gsub(self.block_comment,'')
+ return 'comment',v:sub(1,-3)
+end
+
+return { lua = Lua(), cc = CC() }
--- /dev/null
+--- Lexical scanner for creating a sequence of tokens from text. <br>
+-- <p><code>lexer.scan(s)</code> returns an iterator over all tokens found in the
+-- string <code>s</code>. This iterator returns two values, a token type string
+-- (such as 'string' for quoted string, 'iden' for identifier) and the value of the
+-- token.
+-- <p>
+-- Versions specialized for Lua and C are available; these also handle block comments
+-- and classify keywords as 'keyword' tokens. For example:
+-- <pre class=example>
+-- > s = 'for i=1,n do'
+-- > for t,v in lexer.lua(s) do print(t,v) end
+-- keyword for
+-- iden i
+-- = =
+-- number 1
+-- , ,
+-- iden n
+-- keyword do
+-- </pre>
+-- See the Guide for further <a href="../../index.html#lexer">discussion</a> <br>
+-- @class module
+-- @name pl.lexer
+
+local strfind = string.find
+local strsub = string.sub
+local append = table.insert
+--[[
+module ('pl.lexer',utils._module)
+]]
+
+local function assert_arg(idx,val,tp)
+ if type(val) ~= tp then
+ error("argument "..idx.." must be "..tp, 2)
+ end
+end
+
+local lexer = {}
+
+local NUMBER1 = '^[%+%-]?%d+%.?%d*[eE][%+%-]?%d+'
+local NUMBER2 = '^[%+%-]?%d+%.?%d*'
+local NUMBER3 = '^0x[%da-fA-F]+'
+local NUMBER4 = '^%d+%.?%d*[eE][%+%-]?%d+'
+local NUMBER5 = '^%d+%.?%d*'
+local IDEN = '^[%a_][%w_]*'
+local WSPACE = '^%s+'
+local STRING1 = [[^'.-[^\\]']]
+local STRING2 = [[^".-[^\\]"]]
+local STRING3 = "^((['\"])%2)" -- empty string
+local PREPRO = '^#.-[^\\]\n'
+
+local plain_matches,lua_matches,cpp_matches,lua_keyword,cpp_keyword
+
+local function tdump(tok)
+ return tok,tok
+end
+
+local function ndump(tok,options)
+ if options and options.number then
+ tok = tonumber(tok)
+ end
+ return "number",tok
+end
+
+-- regular strings, single or double quotes; usually we want them
+-- without the quotes
+local function sdump(tok,options)
+ if options and options.string then
+ tok = tok:sub(2,-2)
+ end
+ return "string",tok
+end
+
+-- long Lua strings need extra work to get rid of the quotes
+local function sdump_l(tok,options)
+ if options and options.string then
+ tok = tok:sub(3,-3)
+ end
+ return "string",tok
+end
+
+local function chdump(tok,options)
+ if options and options.string then
+ tok = tok:sub(2,-2)
+ end
+ return "char",tok
+end
+
+local function cdump(tok)
+ return 'comment',tok
+end
+
+local function wsdump (tok)
+ return "space",tok
+end
+
+local function pdump (tok)
+ return 'prepro',tok
+end
+
+local function plain_vdump(tok)
+ return "iden",tok
+end
+
+local function lua_vdump(tok)
+ if lua_keyword[tok] then
+ return "keyword",tok
+ else
+ return "iden",tok
+ end
+end
+
+local function cpp_vdump(tok)
+ if cpp_keyword[tok] then
+ return "keyword",tok
+ else
+ return "iden",tok
+ end
+end
+
+local function count_lines(line, text)
+ local index, limit = 1, #text
+ while index <= limit do
+ local start, stop = text:find('\r\n', index, true)
+ if not start then
+ start, stop = text:find('[\r\n\f]', index)
+ if not start then break end
+ end
+ index = stop + 1
+ line = line + 1
+ end
+ return line
+end
+
+local multiline = { comment = true, space = true }
+
+
+--- create a plain token iterator from a string or file-like object.
+-- @param s the string
+-- @param matches an optional match table (set of pattern-action pairs)
+-- @param filter a table of token types to exclude, by default {space=true}
+-- @param options a table of options; by default, {number=true,string=true},
+-- which means convert numbers and strip string quotes.
+function lexer.scan (s,matches,filter,options)
+ --assert_arg(1,s,'string')
+ local file = type(s) ~= 'string' and s
+ filter = filter or {space=true}
+ options = options or {number=true,string=true}
+ if filter then
+ if filter.space then filter[wsdump] = true end
+ if filter.comments then
+ filter[cdump] = true
+ end
+ end
+ if not matches then
+ if not plain_matches then
+ plain_matches = {
+ {WSPACE,wsdump},
+ {NUMBER3,ndump},
+ {IDEN,plain_vdump},
+ {NUMBER1,ndump},
+ {NUMBER2,ndump},
+ {STRING3,sdump},
+ {STRING1,sdump},
+ {STRING2,sdump},
+ {'^.',tdump}
+ }
+ end
+ matches = plain_matches
+ end
+ local i1,i2,idx,res1,res2,tok,pat,fun,capt
+ local line = 1
+ if file then
+ s = file:read()
+ if not s then return nil end -- empty file
+ s = s ..'\n'
+ end
+ local sz = #s
+ local idx = 1
+ if sz == 0 then return nil end -- empty file
+
+ local res = {}
+ local mt = {}
+ mt.__index = mt
+ setmetatable(res,mt)
+
+ function mt.lineno() return line end
+
+ function mt.getline()
+ if idx < sz then
+ tok = strsub(s,idx,-2)
+ idx = sz + 1
+ line = line + 1
+ return tok
+ else
+ idx = sz + 1
+ line = line + 1
+ return file:read()
+ end
+ end
+
+ function mt.next (tok)
+ local t,v = tok()
+ while t == 'space' do
+ t,v = tok()
+ end
+ return t,v
+ end
+
+ function mt.__call ()
+ if not s then return end
+ while true do
+ for _,m in ipairs(matches) do
+ pat,fun = m[1],m[2]
+ if fun == nil then error("no match for "..pat) end
+ i1,i2 = strfind(s,pat,idx)
+ if i1 then
+ tok = strsub(s,i1,i2)
+ idx = i2 + 1
+ if not (filter and filter[fun]) then
+ lexer.finished = idx > sz
+ local t,v = fun(tok,options)
+ if not file and multiline[t] then
+ line = count_lines(line,v)
+ end
+ return t,v
+ end
+ end
+ end
+ if idx > sz then
+ if file then
+ line = line + 1
+ s = file:read()
+ if not s then return end
+ s = s .. '\n'
+ idx ,sz = 1,#s
+ else
+ return
+ end
+ end
+ end
+ end
+ return res
+end
+
+--- get everything in a stream upto a newline.
+-- @param tok a token stream
+-- @return a string
+function lexer.getline (tok)
+ return tok:getline()
+end
+
+--- get current line number. <br>
+-- Only available if the input source is a file-like object.
+-- @param tok a token stream
+-- @return the line number and current column
+function lexer.lineno (tok)
+ return tok:lineno()
+end
+
+--- get the Lua keywords as a set-like table.
+-- So <code>res["and"]</code> etc would be <code>true</code>.
+-- @return a table
+function lexer.get_keywords ()
+ if not lua_keyword then
+ lua_keyword = {
+ ["and"] = true, ["break"] = true, ["do"] = true,
+ ["else"] = true, ["elseif"] = true, ["end"] = true,
+ ["false"] = true, ["for"] = true, ["function"] = true,
+ ["if"] = true, ["in"] = true, ["local"] = true, ["nil"] = true,
+ ["not"] = true, ["or"] = true, ["repeat"] = true,
+ ["return"] = true, ["then"] = true, ["true"] = true,
+ ["until"] = true, ["while"] = true
+ }
+ end
+ return lua_keyword
+end
+
+
+--- create a Lua token iterator from a string or file-like object.
+-- Will return the token type and value.
+-- @param s the string
+-- @param filter a table of token types to exclude, by default {space=true,comments=true}
+-- @param options a table of options; by default, {number=true,string=true},
+-- which means convert numbers and strip string quotes.
+function lexer.lua(s,filter,options)
+ filter = filter or {space=true,comments=true}
+ lexer.get_keywords()
+ if not lua_matches then
+ lua_matches = {
+ {WSPACE,wsdump},
+ {NUMBER3,ndump},
+ {IDEN,lua_vdump},
+ {NUMBER4,ndump},
+ {NUMBER5,ndump},
+ {STRING3,sdump},
+ {STRING1,sdump},
+ {STRING2,sdump},
+ {'^%-%-%[(=*)%[.-%]%1%]',cdump},
+ {'^%-%-.-\n',cdump},
+ {'^%[(=*)%[.-%]%1%]',sdump_l},
+ {'^==',tdump},
+ {'^~=',tdump},
+ {'^<=',tdump},
+ {'^>=',tdump},
+ {'^%.%.%.',tdump},
+ {'^%.%.',tdump},
+ {'^.',tdump}
+ }
+ end
+ return lexer.scan(s,lua_matches,filter,options)
+end
+
+--- create a C/C++ token iterator from a string or file-like object.
+-- Will return the token type type and value.
+-- @param s the string
+-- @param filter a table of token types to exclude, by default {space=true,comments=true}
+-- @param options a table of options; by default, {number=true,string=true},
+-- which means convert numbers and strip string quotes.
+function lexer.cpp(s,filter,options)
+ filter = filter or {comments=true}
+ if not cpp_keyword then
+ cpp_keyword = {
+ ["class"] = true, ["break"] = true, ["do"] = true, ["sizeof"] = true,
+ ["else"] = true, ["continue"] = true, ["struct"] = true,
+ ["false"] = true, ["for"] = true, ["public"] = true, ["void"] = true,
+ ["private"] = true, ["protected"] = true, ["goto"] = true,
+ ["if"] = true, ["static"] = true, ["const"] = true, ["typedef"] = true,
+ ["enum"] = true, ["char"] = true, ["int"] = true, ["bool"] = true,
+ ["long"] = true, ["float"] = true, ["true"] = true, ["delete"] = true,
+ ["double"] = true, ["while"] = true, ["new"] = true,
+ ["namespace"] = true, ["try"] = true, ["catch"] = true,
+ ["switch"] = true, ["case"] = true, ["extern"] = true,
+ ["return"] = true,["default"] = true,['unsigned'] = true,['signed'] = true,
+ ["union"] = true, ["volatile"] = true, ["register"] = true,["short"] = true,
+ }
+ end
+ if not cpp_matches then
+ cpp_matches = {
+ {WSPACE,wsdump},
+ {PREPRO,pdump},
+ {NUMBER3,ndump},
+ {IDEN,cpp_vdump},
+ {NUMBER4,ndump},
+ {NUMBER5,ndump},
+ {STRING3,sdump},
+ {STRING1,chdump},
+ {STRING2,sdump},
+ {'^//.-\n',cdump},
+ {'^/%*.-%*/',cdump},
+ {'^==',tdump},
+ {'^!=',tdump},
+ {'^<=',tdump},
+ {'^>=',tdump},
+ {'^->',tdump},
+ {'^&&',tdump},
+ {'^||',tdump},
+ {'^%+%+',tdump},
+ {'^%-%-',tdump},
+ {'^%+=',tdump},
+ {'^%-=',tdump},
+ {'^%*=',tdump},
+ {'^/=',tdump},
+ {'^|=',tdump},
+ {'^%^=',tdump},
+ {'^::',tdump},
+ {'^.',tdump}
+ }
+ end
+ return lexer.scan(s,cpp_matches,filter,options)
+end
+
+--- get a list of parameters separated by a delimiter from a stream.
+-- @param tok the token stream
+-- @param endtoken end of list (default ')'). Can be '\n'
+-- @param delim separator (default ',')
+-- @return a list of token lists.
+function lexer.get_separated_list(tok,endtoken,delim)
+ endtoken = endtoken or ')'
+ delim = delim or ','
+ local parm_values = {}
+ local level = 1 -- used to count ( and )
+ local tl = {}
+ local function tappend (tl,t,val)
+ val = val or t
+ append(tl,{t,val})
+ end
+ local is_end
+ if endtoken == '\n' then
+ is_end = function(t,val)
+ return t == 'space' and val:find '\n'
+ end
+ else
+ is_end = function (t)
+ return t == endtoken
+ end
+ end
+ local is_delim
+ if type(delim) == 'function' then
+ is_delim = delim
+ else
+ is_delim = function(t)
+ return t == delim
+ end
+ end
+ local token,value
+ while true do
+ token,value=tok()
+ if not token then return nil,'EOS' end -- end of stream is an error!
+ if is_end(token,value) and level == 1 then
+ append(parm_values,tl)
+ break
+ elseif token == '(' then
+ level = level + 1
+ tappend(tl,'(')
+ elseif token == ')' then
+ level = level - 1
+ if level == 0 then -- finished with parm list
+ append(parm_values,tl)
+ break
+ else
+ tappend(tl,')')
+ end
+ elseif level == 1 and is_delim(token) then
+ append(parm_values,tl) -- a new parm
+ tl = {}
+ else
+ tappend(tl,token,value)
+ end
+ end
+ return parm_values,{token,value}
+end
+
+--- get the next non-space token from the stream.
+-- @param tok the token stream.
+function lexer.skipws (tok)
+ return tok:next()
+end
+
+local skipws = lexer.skipws
+
+--- get the next token, which must be of the expected type.
+-- Throws an error if this type does not match!
+-- @param tok the token stream
+-- @param expected_type the token type
+-- @param no_skip_ws whether we should skip whitespace
+function lexer.expecting (tok,expected_type,no_skip_ws)
+ assert_arg(1,tok,'function')
+ assert_arg(2,expected_type,'string')
+ local t,v
+ if no_skip_ws then
+ t,v = tok()
+ else
+ t,v = skipws(tok)
+ end
+ if t ~= expected_type then error ("expecting "..expected_type,2) end
+ return v
+end
+
+return lexer
--- /dev/null
+--------------
+-- Handling markup transformation.
+-- Currently just does Markdown, but this is intended to
+-- be the general module for managing other formats as well.
+
+require 'pl'
+local doc = require 'ldoc.doc'
+local utils = require 'pl.utils'
+local prettify = require 'ldoc.prettify'
+local quit, concat, lstrip = utils.quit, table.concat, stringx.lstrip
+local markup = {}
+
+local backtick_references
+
+-- inline <references> use same lookup as @see
+local function resolve_inline_references (ldoc, txt, item, plain)
+ local res = (txt:gsub('@{([^}]-)}',function (name)
+ local qname,label = utils.splitv(name,'%s*|')
+ if not qname then
+ qname = name
+ end
+ local ref,err = markup.process_reference(qname)
+ if not ref then
+ err = err .. ' ' .. qname
+ if item then item:warning(err)
+ else
+ io.stderr:write('nofile error: ',err,'\n')
+ end
+ return '???'
+ end
+ if not label then
+ label = ref.label
+ end
+ if not plain and label then -- a nastiness with markdown.lua and underscores
+ label = label:gsub('_','\\_')
+ end
+ local html = ldoc.href(ref) or '#'
+ label = label or '?que'
+ local res = ('<a href="%s">%s</a>'):format(html,label)
+ return res
+ end))
+ if backtick_references then
+ res = res:gsub('`([^`]+)`',function(name)
+ local ref,err = markup.process_reference(name)
+ if ref then
+ return ('<a href="%s">%s</a> '):format(ldoc.href(ref),name)
+ else
+ return '`'..name..'`'
+ end
+ end)
+ end
+ return res
+end
+
+-- for readme text, the idea here is to create module sections at ## so that
+-- they can appear in the contents list as a ToC.
+function markup.add_sections(F, txt)
+ local sections, L, first = {}, 1, true
+ local title_pat_end, title_pat = '[^#]%s*(.+)'
+ for line in stringx.lines(txt) do
+ if first then
+ local level,header = line:match '^(#+)%s*(.+)'
+ if level then
+ level = level .. '#'
+ else
+ level = '##'
+ end
+ title_pat = '^'..level..title_pat_end
+ first = false
+ end
+ local title = line:match (title_pat)
+ if title then
+ -- Markdown does allow this pattern
+ title = title:gsub('%s*#+$','')
+ sections[L] = F:add_document_section(title)
+ end
+ L = L + 1
+ end
+ F.sections = sections
+ return txt
+end
+
+local function indent_line (line)
+ line = line:gsub('\t',' ') -- support for barbarians ;)
+ local indent = #line:match '^%s*'
+ return indent,line
+end
+
+local function non_blank (line)
+ return line:find '%S'
+end
+
+local global_context, local_context
+
+-- before we pass Markdown documents to markdown/discount, we need to do three things:
+-- - resolve any @{refs} and (optionally) `refs`
+-- - any @lookup directives that set local context for ref lookup
+-- - insert any section ids which were generated by add_sections above
+-- - prettify any code blocks
+
+local function process_multiline_markdown(ldoc, txt, F)
+ local res, L, append = {}, 0, table.insert
+ local filename = F.filename
+ local err_item = {
+ warning = function (self,msg)
+ io.stderr:write(filename..':'..L..': '..msg,'\n')
+ end
+ }
+ local get = stringx.lines(txt)
+ local getline = function()
+ L = L + 1
+ return get()
+ end
+ local line = getline()
+ local indent,code,start_indent
+ local_context = nil
+ while line do
+ local name = line:match '^@lookup%s+(%S+)'
+ if name then
+ local_context = name .. '.'
+ line = getline()
+ end
+ indent, line = indent_line(line)
+ if indent >= 4 then -- indented code block
+ code = {}
+ local plain
+ while indent >= 4 or not non_blank(line) do
+ if not start_indent then
+ start_indent = indent
+ if line:match '^%s*@plain%s*$' then
+ plain = true
+ line = getline()
+ end
+ end
+ if not plain then
+ append(code,line:sub(start_indent))
+ else
+ append(res, line)
+ end
+ line = getline()
+ if line == nil then break end
+ indent, line = indent_line(line)
+ end
+ start_indent = nil
+ if #code > 1 then table.remove(code) end
+ code = concat(code,'\n')
+ if code ~= '' then
+ code, err = prettify.lua(filename,code..'\n',L)
+ code = resolve_inline_references(ldoc, code, err_item)
+ append(res, code)
+ append(res,'</pre>')
+ else
+ append(res ,code)
+ end
+ else
+ local section = F.sections[L]
+ if section then
+ append(res,('<a name="%s"></a>'):format(section))
+ end
+ line = resolve_inline_references(ldoc, line, err_item)
+ append(res,line)
+ line = getline()
+ end
+ end
+ res = concat(res,'\n')
+ return res
+end
+
+
+function markup.create (ldoc, format)
+ local processor
+ markup.plain = true
+ backtick_references = ldoc.backtick_references
+ global_context = ldoc.package and ldoc.package .. '.'
+
+ markup.process_reference = function(name)
+ local mod = ldoc.single or ldoc.module or ldoc.modules[1]
+ local ref,err = mod:process_see_reference(name, ldoc.modules)
+ if ref then return ref end
+ if global_context then
+ local qname = global_context .. name
+ ref = mod:process_see_reference(qname, ldoc.modules)
+ if ref then return ref end
+ end
+ if local_context then
+ local qname = local_context .. name
+ ref = mod:process_see_reference(qname, ldoc.modules)
+ if ref then return ref end
+ end
+ -- note that we'll return the original error!
+ return ref,err
+ end
+
+ markup.href = function(ref)
+ return ldoc.href(ref)
+ end
+
+ if format == 'plain' then
+ processor = function(txt, item)
+ if txt == nil then return '' end
+ return resolve_inline_references(ldoc, txt, item, true)
+ end
+ else
+ local ok,formatter = pcall(require,format)
+ if not ok then
+ if format == 'discount' then
+ print('format: discount not found, using markdown')
+ ok,formatter = pcall(require,'markdown')
+ end
+ if not ok then
+ quit("cannot load formatter: "..format)
+ end
+ end
+ if backtick_references == nil then
+ backtick_references = true
+ end
+ markup.plain = false
+ processor = function (txt,item)
+ if txt == nil then return '' end
+ if utils.is_type(item,doc.File) then
+ txt = process_multiline_markdown(ldoc, txt, item)
+ else
+ txt = resolve_inline_references(ldoc, txt, item)
+ end
+ txt = formatter(txt)
+ -- We will add our own paragraph tags, if needed.
+ return (txt:gsub('^%s*<p>(.+)</p>%s*$','%1'))
+ end
+ end
+ markup.resolve_inline_references = function(txt, errfn)
+ return resolve_inline_references(ldoc, txt, errfn, markup.plain)
+ end
+ markup.processor = processor
+ prettify.resolve_inline_references = function(txt, errfn)
+ return resolve_inline_references(ldoc, txt, errfn, true)
+ end
+ return processor
+end
+
+
+return markup
--- /dev/null
+-- parsing code for doc comments
+
+require 'pl'
+local lexer = require 'ldoc.lexer'
+local tools = require 'ldoc.tools'
+local doc = require 'ldoc.doc'
+local Item,File = doc.Item,doc.File
+
+------ Parsing the Source --------------
+-- This uses the lexer from PL, but it should be possible to use Peter Odding's
+-- excellent Lpeg based lexer instead.
+
+local parse = {}
+
+local tnext, append = lexer.skipws, table.insert
+
+-- a pattern particular to LuaDoc tag lines: the line must begin with @TAG,
+-- followed by the value, which may extend over several lines.
+local luadoc_tag = '^%s*@(%a+)'
+local luadoc_tag_value = luadoc_tag..'(.*)'
+local luadoc_tag_mod_and_value = luadoc_tag..'%[(.*)%](.*)'
+
+-- assumes that the doc comment consists of distinct tag lines
+function parse_tags(text)
+ local lines = stringio.lines(text)
+ local preamble, line = tools.grab_while_not(lines,luadoc_tag)
+ local tag_items = {}
+ local follows
+ while line do
+ local tag, mod_string, rest = line :match(luadoc_tag_mod_and_value)
+ if not tag then tag, rest = line :match (luadoc_tag_value) end
+ local modifiers
+ if mod_string then
+ modifiers = { }
+ for x in mod_string :gmatch "[^,]+" do
+ local k, v = x :match "^([^=]+)=(.*)$"
+ if not k then k, v = x, x end
+ modifiers[k] = v
+ end
+ end
+ -- follows: end of current tag
+ -- line: beginning of next tag (for next iteration)
+ follows, line = tools.grab_while_not(lines,luadoc_tag)
+ append(tag_items,{tag, rest .. '\n' .. follows, modifiers})
+ end
+ return preamble,tag_items
+end
+
+-- This takes the collected comment block, and uses the docstyle to
+-- extract tags and values. Assume that the summary ends in a period or a question
+-- mark, and everything else in the preamble is the description.
+-- If a tag appears more than once, then its value becomes a list of strings.
+-- Alias substitution and @TYPE NAME shortcutting is handled by Item.check_tag
+local function extract_tags (s)
+ if s:match '^%s*$' then return {} end
+ local preamble,tag_items = parse_tags(s)
+ local strip = tools.strip
+ local summary, description = preamble:match('^(.-[%.?])(%s.+)')
+ if not summary then
+ -- perhaps the first sentence did not have a . or ? terminating it.
+ -- Then try split at linefeed
+ summary, description = preamble:match('^(.-\n\n)(.+)')
+ if not summary then
+ summary = preamble
+ end
+ end -- and strip(description) ?
+ local tags = {summary=summary and strip(summary) or '',description=description or ''}
+ for _,item in ipairs(tag_items) do
+ local tag, value, modifiers = Item.check_tag(tags,unpack(item))
+ value = strip(value)
+
+ if modifiers then value = { value, modifiers=modifiers } end
+ local old_value = tags[tag]
+
+ if not old_value then -- first element
+ tags[tag] = value
+ elseif type(old_value)=='table' and old_value.append then -- append to existing list
+ old_value :append (value)
+ else -- upgrade string->list
+ tags[tag] = List{old_value, value}
+ end
+ end
+ return Map(tags)
+end
+
+local _xpcall = xpcall
+if true then
+ _xpcall = function(f) return true, f() end
+end
+
+
+
+-- parses a Lua or C file, looking for ldoc comments. These are like LuaDoc comments;
+-- they start with multiple '-'. (Block commments are allowed)
+-- If they don't define a name tag, then by default
+-- it is assumed that a function definition follows. If it is the first comment
+-- encountered, then ldoc looks for a call to module() to find the name of the
+-- module if there isn't an explicit module name specified.
+
+local function parse_file(fname,lang, package)
+ local line,f = 1
+ local F = File(fname)
+ local module_found, first_comment = false,true
+ local current_item, module_item
+
+ local tok,f = lang.lexer(fname)
+ if not tok then return nil end
+
+ function lineno ()
+ return tok:lineno()
+ end
+
+ function filename () return fname end
+
+ function F:warning (msg,kind,line)
+ kind = kind or 'warning'
+ line = line or lineno()
+ io.stderr:write(fname..':'..line..': '..msg,'\n')
+ end
+
+ function F:error (msg)
+ self:warning(msg,'error')
+ io.stderr:write('LDoc error\n')
+ os.exit(1)
+ end
+
+ local function add_module(tags,module_found,old_style)
+ tags.name = module_found
+ tags.class = 'module'
+ local item = F:new_item(tags,lineno())
+ item.old_style = old_style
+ module_item = item
+ end
+
+ local mod
+ local t,v = tnext(tok)
+ if t == '#' then
+ while t and t ~= 'comment' do t,v = tnext(tok) end
+ if t == nil then
+ F:warning('empty file')
+ return nil
+ end
+ end
+ if lang.parse_module_call and t ~= 'comment'then
+ while t and not (t == 'iden' and v == 'module') do
+ t,v = tnext(tok)
+ end
+ if not t then
+ F:warning("no module() call found; no initial doc comment")
+ else
+ mod,t,v = lang:parse_module_call(tok,t,v)
+ if mod ~= '...' then
+ add_module({summary='(no description)'},mod,true)
+ first_comment = false
+ module_found = true
+ end
+ end
+ end
+ local ok, err = xpcall(function()
+ while t do
+ if t == 'comment' then
+ local comment = {}
+ local ldoc_comment,block = lang:start_comment(v)
+
+ if ldoc_comment and block then
+ t,v = lang:grab_block_comment(v,tok)
+ end
+
+ if lang:empty_comment(v) then -- ignore rest of empty start comments
+ t,v = tok()
+ end
+
+ while t and t == 'comment' do
+ v = lang:trim_comment(v)
+ append(comment,v)
+ t,v = tok()
+ if t == 'space' and not v:match '\n' then
+ t,v = tok()
+ end
+ end
+
+ if t == 'space' then t,v = tnext(tok) end
+
+ local item_follows, tags, is_local, case
+ if ldoc_comment or first_comment then
+ comment = table.concat(comment)
+
+ if not ldoc_comment and first_comment then
+ F:warning("first comment must be a doc comment!")
+ break
+ end
+ if first_comment then
+ first_comment = false
+ else
+ item_follows, is_local, case = lang:item_follows(t,v,tok)
+ end
+ if item_follows or comment:find '@'then
+ tags = extract_tags(comment)
+ if doc.project_level(tags.class) then
+ module_found = tags.name
+ end
+ doc.expand_annotation_item(tags,current_item)
+ -- if the item has an explicit name or defined meaning
+ -- then don't continue to do any code analysis!
+ if tags.name then
+ if not tags.class then
+ F:warning("no type specified, assuming function: '"..tags.name.."'")
+ tags.class = 'function'
+ end
+ item_follows, is_local = false, false
+ elseif lang:is_module_modifier (tags) then
+ if not item_follows then
+ F:warning("@usage or @export followed by unknown code")
+ break
+ end
+ item_follows(tags,tok)
+ local res, value, tagname = lang:parse_module_modifier(tags,tok,F)
+ if not res then F:warning(value); break
+ else
+ if tagname then
+ module_item:set_tag(tagname,value)
+ end
+ -- don't continue to make an item!
+ ldoc_comment = false
+ end
+ end
+ end
+ end
+ -- some hackery necessary to find the module() call
+ if not module_found and ldoc_comment then
+ local old_style
+ module_found,t,v = lang:find_module(tok,t,v)
+ -- right, we can add the module object ...
+ old_style = module_found ~= nil
+ if not module_found or module_found == '...' then
+ -- we have to guess the module name
+ module_found = tools.this_module_name(package,fname)
+ end
+ if not tags then tags = extract_tags(comment) end
+ add_module(tags,module_found,old_style)
+ tags = nil
+ if not t then
+ F:warning(fname,' contains no items\n','warning',1)
+ break;
+ end -- run out of file!
+ -- if we did bump into a doc comment, then we can continue parsing it
+ end
+
+ -- end of a block of document comments
+ if ldoc_comment and tags then
+ local line = t ~= nil and lineno()
+ if t ~= nil then
+ if item_follows then -- parse the item definition
+ item_follows(tags,tok)
+ else
+ lang:parse_extra(tags,tok,case)
+ end
+ end
+ if is_local or tags['local'] then
+ tags['local'] = true
+ end
+ if tags.name then
+ current_item = F:new_item(tags,line)
+ current_item.inferred = item_follows ~= nil
+ if doc.project_level(tags.class) then
+ if module_item then
+ F:error("Module already declared!")
+ end
+ module_item = current_item
+ end
+ end
+ if not t then break end
+ end
+ end
+ if t ~= 'comment' then t,v = tok() end
+ end
+ end,debug.traceback)
+ if not ok then return F, err end
+ if f then f:close() end
+ return F
+end
+
+function parse.file(name,lang, args)
+ local F,err = parse_file(name,lang, args.package)
+ if err or not F then return F,err end
+ local ok,err = xpcall(function() F:finish() end,debug.traceback)
+ if not ok then return F,err end
+ return F
+end
+
+return parse
--- /dev/null
+-- Making Lua source code look pretty.
+-- A simple scanner based prettifier, which scans comments for @{ref} and code
+-- for known modules and functions.
+-- A module reference to an example `test-fun.lua` would look like
+-- `@{example:test-fun}`.
+require 'pl'
+local lexer = require 'ldoc.lexer'
+local globals = require 'ldoc.builtin.globals'
+local tnext = lexer.skipws
+local prettify = {}
+
+local escaped_chars = {
+ ['&'] = '&',
+ ['<'] = '<',
+ ['>'] = '>',
+}
+local escape_pat = '[&<>]'
+
+local function escape(str)
+ return (str:gsub(escape_pat,escaped_chars))
+end
+
+local function span(t,val)
+ return ('<span class="%s">%s</span>'):format(t,val)
+end
+
+local spans = {keyword=true,number=true,string=true,comment=true,global=true}
+
+function prettify.lua (fname, code, initial_lineno)
+ local res = List()
+ res:append(header)
+ res:append '<pre>\n'
+ intial_lineno = initial_lineno or 0
+
+ local tok = lexer.lua(code,{},{})
+ local error_reporter = {
+ warning = function (self,msg)
+ io.stderr:write(fname..':'..tok:lineno()+initial_lineno..': '..msg,'\n')
+ end
+ }
+ local t,val = tok()
+ if not t then return nil,"empty file" end
+ while t do
+ val = escape(val)
+ if globals.functions[val] or globals.tables[val] then
+ t = 'global'
+ end
+ if spans[t] then
+ if t == 'comment' then -- may contain @{ref}
+ val = prettify.resolve_inline_references(val,error_reporter)
+ end
+ res:append(span(t,val))
+ else
+ res:append(val)
+ end
+ t,val = tok()
+ end
+ local last = res[#res]
+ if last:match '\n$' then
+ res[#res] = last:gsub('\n+','')
+ end
+ return res:join ()
+end
+
+return prettify
+
--- /dev/null
+---------
+-- General utility functions for ldoc
+-- @module tools
+
+require 'pl'
+local tools = {}
+local M = tools
+local append = table.insert
+local lexer = require 'ldoc.lexer'
+local quit = utils.quit
+
+-- this constructs an iterator over a list of objects which returns only
+-- those objects where a field has a certain value. It's used to iterate
+-- only over functions or tables, etc.
+-- (something rather similar exists in LuaDoc)
+function M.type_iterator (list,field,value)
+ return function()
+ local i = 1
+ return function()
+ local val = list[i]
+ while val and val[field] ~= value do
+ i = i + 1
+ val = list[i]
+ end
+ i = i + 1
+ if val then return val end
+ end
+ end
+end
+
+-- KindMap is used to iterate over a set of categories, called _kinds_,
+-- and the associated iterator over all items in that category.
+-- For instance, a module contains functions, tables, etc and we will
+-- want to iterate over these categories in a specified order:
+--
+-- for kind, items in module.kinds() do
+-- print('kind',kind)
+-- for item in items() do print(item.name) end
+-- end
+--
+-- The kind is typically used as a label or a Title, so for type 'function' the
+-- kind is 'Functions' and so on.
+
+local KindMap = class()
+M.KindMap = KindMap
+
+-- calling a KindMap returns an iterator. This returns the kind, the iterator
+-- over the items of that type, and the actual type tag value.
+function KindMap:__call ()
+ local i = 1
+ local klass = self.klass
+ return function()
+ local kind = klass.kinds[i]
+ if not kind then return nil end -- no more kinds
+ while not self[kind] do
+ i = i + 1
+ kind = klass.kinds[i]
+ if not kind then return nil end
+ end
+ i = i + 1
+ local type = klass.types_by_kind [kind].type
+ return kind, self[kind], type
+ end
+end
+
+function KindMap:put_kind_first (kind)
+ -- find this kind in our kind list
+ local kinds = self.klass.kinds,kind
+ local idx = tablex.find(kinds,kind)
+ -- and swop with the start!
+ if idx then
+ kinds[1],kinds[idx] = kinds[idx],kinds[1]
+ end
+end
+
+function KindMap:type_of (item)
+ local klass = self.klass
+ local kind = klass.types_by_tag[item.type]
+ return klass.types_by_kind [kind]
+end
+
+function KindMap:get_section_description (kind)
+ return self.klass.descriptions[kind]
+end
+
+-- called for each new item. It does not actually create separate lists,
+-- (although that would not break the interface) but creates iterators
+-- for that item type if not already created.
+function KindMap:add (item,items,description)
+ local group = item[self.fieldname] -- which wd be item's type or section
+ local kname = self.klass.types_by_tag[group] -- the kind name
+ if not self[kname] then
+ self[kname] = M.type_iterator (items,self.fieldname,group)
+ self.klass.descriptions[kname] = description
+ end
+ item.kind = kname:lower()
+end
+
+-- KindMap has a 'class constructor' which is used to modify
+-- any new base class.
+function KindMap._class_init (klass)
+ klass.kinds = {} -- list in correct order of kinds
+ klass.types_by_tag = {} -- indexed by tag
+ klass.types_by_kind = {} -- indexed by kind
+ klass.descriptions = {} -- optional description for each kind
+end
+
+
+function KindMap.add_kind (klass,tag,kind,subnames)
+ klass.types_by_tag[tag] = kind
+ klass.types_by_kind[kind] = {type=tag,subnames=subnames}
+ append(klass.kinds,kind)
+end
+
+
+----- some useful utility functions ------
+
+function M.module_basepath()
+ local lpath = List.split(package.path,';')
+ for p in lpath:iter() do
+ local p = path.dirname(p)
+ if path.isabs(p) then
+ return p
+ end
+ end
+end
+
+-- split a qualified name into the module part and the name part,
+-- e.g 'pl.utils.split' becomes 'pl.utils' and 'split'
+function M.split_dotted_name (s)
+ local s1,s2 = path.splitext(s)
+ if s2=='' then return nil
+ else return s1,s2:sub(2)
+ end
+end
+
+-- expand lists of possibly qualified identifiers
+-- given something like {'one , two.2','three.drei.drie)'}
+-- it will output {"one","two.2","three.drei.drie"}
+function M.expand_comma_list (ls)
+ local new_ls = List()
+ for s in ls:iter() do
+ s = s:gsub('[^%.:%-%w_]*$','')
+ if s:find ',' then
+ new_ls:extend(List.split(s,'%s*,%s*'))
+ else
+ new_ls:append(s)
+ end
+ end
+ return new_ls
+end
+
+-- grab lines from a line iterator `iter` until the line matches the pattern.
+-- Returns the joined lines and the line, which may be nil if we run out of
+-- lines.
+function M.grab_while_not(iter,pattern)
+ local line = iter()
+ local res = {}
+ while line and not line:match(pattern) do
+ append(res,line)
+ line = iter()
+ end
+ res = table.concat(res,'\n')
+ return res,line
+end
+
+
+function M.extract_identifier (value)
+ return value:match('([%.:%-_%w]+)')
+end
+
+function M.strip (s)
+ return s:gsub('^%s+',''):gsub('%s+$','')
+end
+
+function M.check_directory(d)
+ if not path.isdir(d) then
+ lfs.mkdir(d)
+ end
+end
+
+function M.check_file (f,original)
+ if not path.exists(f) or path.getmtime(original) > path.getmtime(f) then
+ local text,err = utils.readfile(original)
+ if text then
+ text,err = utils.writefile(f,text)
+ end
+ if err then
+ quit("Could not copy "..original.." to "..f)
+ end
+ end
+end
+
+function M.writefile(name,text)
+ local ok,err = utils.writefile(name,text)
+ if err then quit(err) end
+end
+
+function M.name_of (lpath)
+ lpath,ext = path.splitext(lpath)
+ return lpath
+end
+
+function M.this_module_name (basename,fname)
+ local ext
+ if basename == '' then
+ return M.name_of(fname)
+ end
+ basename = path.abspath(basename)
+ if basename:sub(-1,-1) ~= path.sep then
+ basename = basename..path.sep
+ end
+ local lpath,cnt = fname:gsub('^'..utils.escape(basename),'')
+ --print('deduce',lpath,cnt,basename)
+ if cnt ~= 1 then quit("module(...) name deduction failed: base "..basename.." "..fname) end
+ lpath = lpath:gsub(path.sep,'.')
+ return M.name_of(lpath):gsub('%.init$','')
+end
+
+function M.find_existing_module (name, dname, searchfn)
+ local fullpath,lua = searchfn(name)
+ local mod = true
+ if not fullpath then -- maybe it's a function reference?
+ -- try again with the module part
+ local mpath,fname = M.split_dotted_name(name)
+ if mpath then
+ fullpath,lua = searchfn(mpath)
+ else
+ fullpath = nil
+ end
+ if not fullpath then
+ return nil, "module or function '"..dname.."' not found on module path"
+ else
+ mod = fname
+ end
+ end
+ if not lua then return nil, "module '"..name.."' is a binary extension" end
+ return fullpath, mod
+end
+
+function M.lookup_existing_module_or_function (name, docpath)
+ -- first look up on the Lua module path
+ local fullpath, mod = M.find_existing_module(name,name,path.package_path)
+ -- no go; but see if we can find it on the doc path
+ if not fullpath then
+ fullpath, mod = M.find_existing_module("ldoc.builtin." .. name,name,path.package_path)
+--~ fullpath, mod = M.find_existing_module(name, function(name)
+--~ local fpath = package.searchpath(name,docpath)
+--~ return fpath,true -- result must always be 'lua'!
+--~ end)
+ end
+ return fullpath, mod -- `mod` can be the error message
+end
+
+
+--------- lexer tools -----
+
+local tnext = lexer.skipws
+
+local function type_of (tok) return tok[1] end
+local function value_of (tok) return tok[2] end
+
+-- This parses Lua formal argument lists. It will return a list of argument
+-- names, which also has a comments field, which will contain any commments
+-- following the arguments. ldoc will use these in addition to explicit
+-- param tags.
+
+function M.get_parameters (tok,endtoken,delim)
+ tok = M.space_skip_getter(tok)
+ local args = List()
+ args.comments = {}
+ local ltl = lexer.get_separated_list(tok,endtoken,delim)
+
+ if not ltl or #ltl[1] == 0 then return args end -- no arguments
+
+ local function set_comment (idx,tok)
+ local text = value_of(tok):gsub('%s*$','')
+ args.comments[args[idx]] = text
+ end
+
+ for i = 1,#ltl do
+ --print('check',i,ltl[i],#ltl[i])
+ local tl = ltl[i]
+ if #tl > 0 then
+ if type_of(tl[1]) == 'comment' then
+ if i > 1 then set_comment(i-1,tl[1]) end
+ if #tl > 1 then
+ args:append(value_of(tl[2]))
+ end
+ else
+ args:append(value_of(tl[1]))
+ end
+ if i == #ltl then
+ local last_tok = tl[#tl]
+ if #tl > 1 and type_of(last_tok) == 'comment' then
+ set_comment(i,last_tok)
+ end
+ end
+ end
+ end
+
+ return args
+end
+
+-- parse a Lua identifier - contains names separated by . and :.
+function M.get_fun_name (tok,first)
+ local res = {}
+ local t,name
+ if not first then
+ t,name = tnext(tok)
+ else
+ t,name = 'iden',first
+ end
+ t,sep = tnext(tok)
+ while sep == '.' or sep == ':' do
+ append(res,name)
+ append(res,sep)
+ t,name = tnext(tok)
+ t,sep = tnext(tok)
+ end
+ append(res,name)
+ return table.concat(res),t,sep
+end
+
+-- space-skipping version of token iterator
+function M.space_skip_getter(tok)
+ return function ()
+ local t,v = tok()
+ while t and t == 'space' do
+ t,v = tok()
+ end
+ return t,v
+ end
+end
+
+function M.quote (s)
+ return "'"..s.."'"
+end
+
+-- The PL Lua lexer does not do block comments
+-- when used in line-grabbing mode, so this function grabs each line
+-- until we meet the end of the comment
+function M.grab_block_comment (v,tok,patt)
+ local res = {v}
+ repeat
+ v = lexer.getline(tok)
+ if v:match (patt) then break end
+ append(res,v)
+ append(res,'\n')
+ until false
+ res = table.concat(res)
+ --print(res)
+ return 'comment',res
+end
+
+local prel = path.normcase('/[^/]-/%.%.')
+
+
+function M.abspath (f)
+ local count
+ local res = path.normcase(path.abspath(f))
+ while true do
+ res,count = res:gsub(prel,'')
+ if count == 0 then break end
+ end
+ return res
+end
+
+function M.process_file_list (list, mask, operation, ...)
+ local exclude_list = list.exclude and M.files_from_list(list.exclude, mask)
+ local function process (f,...)
+ f = M.abspath(f)
+ if not exclude_list or exclude_list and exclude_list:index(f) == nil then
+ operation(f, ...)
+ end
+ end
+ for _,f in ipairs(list) do
+ if path.isdir(f) then
+ local files = List(dir.getallfiles(f,mask))
+ for f in files:iter() do
+ process(f,...)
+ end
+ elseif path.isfile(f) then
+ process(f,...)
+ else
+ quit("file or directory does not exist: "..M.quote(f))
+ end
+ end
+end
+
+function M.files_from_list (list, mask)
+ local excl = List()
+ M.process_file_list (list, mask, function(f)
+ excl:append(f)
+ end)
+ return excl
+end
+
+
+
+return tools
--- /dev/null
+# LDoc - A Lua Documentation Tool
+
+Copyright (C) 2011 Steve Donovan.
+
+## Rationale
+
+This project grew out of the documentation needs of [Penlight](https://github.com/stevedonovan/Penlight) (and not always getting satisfaction with LuaDoc) and depends on Penlight itself.(This allowed me to _not_ write a lot of code.)
+
+The [API documentation](http://stevedonovan.github.com/Penlight/api/index.html) of Penlight is an example of a project using plain LuaDoc markup processed using LDoc.
+
+LDoc is intended to be compatible with [LuaDoc](http://luadoc.luaforge.net/manual.htm) and thus follows the pattern set by the various *Doc tools:
+
+ --- Summary ends with a period.
+ -- Some description, can be over several lines.
+ -- @param p1 first parameter
+ -- @param p2 second parameter
+ -- @return a string value
+ -- @see second_fun
+ function mod1.first_fun(p1,p2)
+ end
+
+Tags such as `see` and `usage` are supported, and generally the names of functions and modules can be inferred from the code.
+
+LDoc is designed to give better diagnostics: if a `@see` reference cannot be found, then the line number of the reference is given. LDoc knows about modules which do not use `module()` - this is important since this function has become deprecated in Lua 5.2. And you can avoid having to embed HTML in commments by using Markdown.
+
+LDoc will also work with Lua C extension code, and provides some convenient shortcuts.
+
+An example showing the support for named sections and 'classes' is the [Winapi documentation](http://stevedonovan.github.com/winapi/api.html); this is generated from [winapi.l.c](https://github.com/stevedonovan/winapi/blob/master/winapi.l.c).
+
+## Installation
+
+This is straightforward; the only external dependency is [Penlight](https://github.com/stevedonovan/Penlight), which in turn needs [LuaFileSystem](http://keplerproject.github.com/luafilesystem/). These are already present in Lua for Windows, and Penlight is also available through LuaRocks as `luarocks install penlight`.
+
+Unpack the sources somewhere and make an alias to `ldoc.lua` on your path. That is, either an excutable script called 'ldoc' like so:
+
+ lua /path/to/ldoc/ldoc.lua $*
+
+Or a batch file called 'ldoc.bat':
+
+ @echo off
+ lua \path\to\ldoc\ldoc.lua %*
+
--- /dev/null
+----------------
+-- Testing annotations
+-- @module annot
+
+--- first fun.
+function first()
+ if boo then
+ local bar = do_something()
+ if bar then
+ --- @fixme otherwise do what?
+ end
+ end
+end
+
+--- second try.
+function second()
+ --- @todo also handle foo case
+ if bar then
+
+ end
+end
--- /dev/null
+----------------------\r
+-- a module containing some classes.\r
+-- @module classes\r
+\r
+local _M = {}\r
+\r
+---- a useful class.\r
+-- @type Bonzo\r
+\r
+_M.Bonzo = class()\r
+\r
+--- a method.\r
+-- function one\r
+function Bonzo:one()\r
+\r
+end\r
+\r
+--- a metamethod\r
+-- function __tostring\r
+function Bonzo:__tostring()\r
+\r
+end\r
+\r
+return M\r
+\r
--- /dev/null
+--- oh.
+
+--- convert.
+function M.convert ()
+
+end
--- /dev/null
+--- dee.
+
+--- display this.
+function M.display_this()
+end
+
+--- display that.
+-- @see display_this
+function M.display_that()
+end
--- /dev/null
+--- doo
+
+--- start.
+function M.start()
+end
+
+--- run.
+-- @see complex.display.display_this
+function M.run()
+end
+
+
--- /dev/null
+--- wo.
+
+--- open.
+-- @see convert.basic
+function M.open ()
+
+end
--- /dev/null
+--- wee
+
+--- parse.
+-- @see util.open
+function M.parse ()
+
+end
--- /dev/null
+format='markdown'\r
+file = {'types.lua','classes.lua'}\r
+--file = 'classes.lua'\r
+topics = {'one.md','two.md'}\r
+tparam_alias 'string'\r
+tparam_alias ('int','number')\r
--- /dev/null
+-- ldoc configuration file
+title = "testmod docs"
+project = "testmod"
+
+description = [[
+This description applies to the project as a whole.
+]]
+
+alias("p","param")
+
+file = {'mod1.lua','modtest.lua','mylib.c'}
+
+new_type("macro","Macros")
+
--- /dev/null
+---------------------------
+-- Test module providing bonzo.dog.
+-- Rest is a longer description
+-- @class module
+-- @name mod1
+
+--- zero function. Two new ldoc features here; item types
+-- can be used directly as tags, and aliases for tags
+-- can be defined in config.lp.
+-- @function zero_fun
+-- @p k1 first
+-- @p k2 second
+
+--- first function. Some description
+-- @param p1 first parameter
+-- @param p2 second parameter
+function mod1.first_fun(p1,p2)
+end
+
+-------------------------
+-- second function.
+-- @param ... var args!
+function mod1.second_function(...)
+end
+
+------------
+-- third function. Can also provide parameter comments inline,
+-- provided they follow this pattern.
+function mod1.third_function(
+ alpha, -- correction A
+ beta, -- correction B
+ gamma -- factor C
+ )
+end
+
+-----
+-- A useful macro. This is an example of a custom 'kind'.
+-- @macro first_macro
+-- @see second_function
+
+---- general configuration table
+-- @table config
+-- @field A alpha
+-- @field B beta
+-- @field C gamma
+mod1.config = {
+ A = 1,
+ B = 2,
+ C = 3
+}
+
+--[[--
+Another function. Using a Lua block comment
+@param p a parameter
+]]
+function mod1.zero_function(p)
+end
+
+
+-------
+-- Multiple params may match a varargs function.
+-- Generally, ldoc tries to be strict about matching params and formal arguments,
+-- but this is relaxed for varargs: `function other(p,...)`
+-- @param p
+-- @param q
+-- @param r
+function mod1.other(p,...)
+-- something cunning with select(2,...)
+end
+
+-------
+-- A function with typed arguments.
+-- The tparam tag is followed by the 'type'. There is no standard way
+-- to represent Lua types, but you can adopt a convention. Type names
+-- will be resolved. treturn must include a description after the type.
+-- @tparam string name
+-- @tparam number age
+-- @treturn string modified age
+function mod1.typed(name,age)
+
+end
--- /dev/null
+-------
+-- A script.
+-- Scripts are not containers in the sense that modules are,
+-- (although perhaps the idea of 'commands' could be adopted for some utilities)
+-- It allows any upfront script comments to be included in the
+-- documentation. Any long string marked with the 'usage' tag will also appear
+-- in this area.
+--
+-- @script modtest
+
+--- @usage
+local usage = [[
+modtest NAME
+where NAME is your favourite name!
+
+]]
+
+print ('hello',arg[1])
--- /dev/null
+/// A sample C extension.
+// Demonstrates using ldoc's C/C++ support. Can either use /// or /*** */ etc.
+// @module mylib
+#include <string.h>
+#include <math.h>
+
+// includes for Lua
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+/***
+Create a table with given array and hash slots.
+@function createtable
+@param narr initial array slots, default 0
+@param nrec initial hash slots, default 0
+*/
+static int l_createtable (lua_State *L) {
+ int narr = luaL_optint(L,1,0);
+ int nrec = luaL_optint(L,2,0);
+ lua_createtable(L,narr,nrec);
+ return 1;
+}
+
+/***
+Solve a quadratic equation.
+@function solve
+@tparam num a coefficient of x^2
+@tparam num b coefficient of x
+@tparam num c constant
+@treturn num first root
+@treturn num second root
+*/
+static int l_solve (lua_State *L) {
+ double a = lua_tonumber(L,1); // coeff of x*x
+ double b = lua_tonumber(L,2); // coef of x
+ double c = lua_tonumber(L,3); // constant
+ double abc = b*b - 4*a*c;
+ if (abc < 0.0) {
+ lua_pushnil(L);
+ lua_pushstring(L,"imaginary roots!");
+ return 2;
+ } else {
+ abc = sqrt(abc);
+ a = 2*a;
+ lua_pushnumber(L,(-b + abc)/a);
+ lua_pushnumber(L,(+b - abc)/a);
+ return 2;
+ }
+}
+
+static const luaL_reg mylib[] = {
+ {"createtable",l_createtable},
+ {"solve",l_solve},
+ {NULL,NULL}
+};
+
+int luaopen_mylib(lua_State *L)
+{
+ luaL_register (L, "mylib", mylib);
+ return 1;
+}
--- /dev/null
+file = 'simple.lua'\r
+project = 'simple'\r
+description = [[\r
+a simple project\r
+\r
+##References\r
+\r
+ - [Background]()\r
+ - [Discussion]()\r
+\r
+]]\r
+style = true\r
+template = true\r
+format = 'markdown'\r
--- /dev/null
+/* BEGIN RESET
+
+Copyright (c) 2010, Yahoo! Inc. All rights reserved.
+Code licensed under the BSD License:
+http://developer.yahoo.com/yui/license.html
+version: 2.8.2r1
+*/
+html {
+ color: #000;
+ background: #FFF;
+}
+body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td {
+ margin: 0;
+ padding: 0;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+fieldset,img {
+ border: 0;
+}
+address,caption,cite,code,dfn,em,strong,th,var,optgroup {
+ font-style: inherit;
+ font-weight: inherit;
+}
+del,ins {
+ text-decoration: none;
+}
+li {
+ list-style: bullet;
+ margin-left: 20px;
+}
+caption,th {
+ text-align: left;
+}
+h1,h2,h3,h4,h5,h6 {
+ font-size: 100%;
+ font-weight: bold;
+}
+q:before,q:after {
+ content: '';
+}
+abbr,acronym {
+ border: 0;
+ font-variant: normal;
+}
+sup {
+ vertical-align: baseline;
+}
+sub {
+ vertical-align: baseline;
+}
+legend {
+ color: #000;
+}
+input,button,textarea,select,optgroup,option {
+ font-family: inherit;
+ font-size: inherit;
+ font-style: inherit;
+ font-weight: inherit;
+}
+input,button,textarea,select {*font-size:100%;
+}
+/* END RESET */
+
+body {
+ margin-left: 1em;
+ margin-right: 1em;
+ font-family: arial, helvetica, geneva, sans-serif;
+ background-color: #ffffff; margin: 0px;
+}
+
+code, tt { font-family: monospace; }
+
+body, p, td, th { font-size: .95em; line-height: 1.2em;}
+
+p, ul { margin: 10px 0 0 10px;}
+
+strong { font-weight: bold;}
+
+em { font-style: italic;}
+
+h1 {
+ font-size: 1.5em;
+ margin: 0 0 20px 0;
+}
+h2, h3, h4 { margin: 15px 0 10px 0; }
+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;
+ background: #00007f;
+ height: 1px;
+}
+
+blockquote { margin-left: 3em; }
+
+ul { list-style-type: disc; }
+
+p.name {
+ font-family: "Andale Mono", monospace;
+ padding-top: 1em;
+}
+
+pre.example {
+ background-color: rgb(245, 245, 245);
+ border: 1px solid silver;
+ padding: 10px;
+ margin: 10px 0 10px 0;
+ font-family: "Andale Mono", monospace;
+ font-size: .85em;
+}
+
+pre {
+ background-color: rgb(245, 245, 245);
+ border: 1px solid silver;
+ padding: 10px;
+ margin: 10px 0 10px 0;
+ font-family: "Andale Mono", monospace;
+}
+
+
+table.index { border: 1px #00007f; }
+table.index td { text-align: left; vertical-align: top; }
+
+#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;
+}
+
+#main {
+ background-color: #f0f0ff;
+ border-left: 2px solid #cccccc;
+}
+
+#navigation {
+ float: left;
+ width: 14em;
+ vertical-align: top;
+ background-color: #f0f0f0;
+ overflow: visible;
+}
+
+#navigation h2 {
+ background-color:#e7e7e7;
+ font-size:1.1em;
+ color:#000000;
+ text-align: left;
+ padding:0.2em;
+ border-top:1px solid #dddddd;
+ border-bottom:1px solid #dddddd;
+}
+
+#navigation ul
+{
+ font-size:1em;
+ list-style-type: none;
+ margin: 1px 1px 10px 1px;
+}
+
+#navigation li {
+ text-indent: -1em;
+ display: block;
+ margin: 3px 0px 0px 22px;
+}
+
+#navigation li li a {
+ margin: 0px 3px 0px -1em;
+}
+
+#content {
+ margin-left: 14em;
+ width: 40em;
+ padding: 1em;
+ border-left: 2px solid #cccccc;
+ border-right: 2px solid #cccccc;
+ background-color: #f0ffff;
+}
+
+#about {
+ clear: both;
+ 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 {
+ background-color: #ffffff;
+ border-left: 0px;
+ }
+
+ #container {
+ margin-left: 2%;
+ margin-right: 2%;
+ background-color: #ffffff;
+ }
+
+ #content {
+ padding: 1em;
+ background-color: #ffffff;
+ }
+
+ #navigation {
+ 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: #fff0ff; }
+
+table.file_list td.summary { width: 100%; }
+
+table.function_list {
+ border-width: 1px;
+ border-style: solid;
+ border-color: #aaaaaa;
+ border-collapse: collapse;
+}
+table.function_list td {
+ border-width: 1px;
+ padding: 3px;
+ border-style: solid;
+ border-color: #eeeeee;
+}
+
+table.function_list td.name { background-color: #f0f0ff; }
+
+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.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;}
+dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;}
+dl.table h3, dl.function h3 {font-size: .95em;}
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <title>$(ldoc.title)</title>
+ <link rel="stylesheet" href="$(ldoc.css)" type="text/css" />
+</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">
+
+# local iter = ldoc.modules.iter
+# local M = ldoc.markup
+
+<!-- Menu -->
+
+# local function no_spaces(s) return s:gsub('%s','_') end
+# local function use_li(ls)
+# if #ls > 1 then return '<li>','</li>' else return '','' end
+# end
+# local function display_name(item)
+# if item.type == 'function' then return item.name..' '..item.args
+# else return item.name end
+# end
+
+
+<div id="navigation">
+<h1>$(ldoc.project)</h1>
+# if not ldoc.single then
+<ul>
+ <li><a href="../index.html">Index</a></li>
+</ul>
+# else
+<p>$(M(ldoc.description))</p>
+# end
+
+# if module then
+<hr/>
+<ul>
+# for kind, items in module.kinds() do
+# for item in items() do
+ <li><a href="#$(item.name)">$(display_name(item))</a></li>
+# end
+# end
+</ul>
+# end
+
+</div>
+
+<div id="content">
+
+# if module then
+
+<h1>Module <code>$(module.name)</code></h1>
+
+<p>$(M(module.summary))</p>
+<p>$(M(module.description))</p>
+
+<br/>
+<br/>
+
+# --- currently works for both Functions and Tables. The params field either contains
+# --- function parameters or table fields.
+# for kind, items in module.kinds() do
+ <dl class="function">
+# for item in items() do
+ <dt>
+ <a name = "$(item.name)"></a>
+ <strong>$(display_name(item))</strong>
+ </dt>
+ <dd>
+ $(M(item.summary))
+ $(M(item.description))
+
+# if item.usage then
+# local li,il = use_li(item.usage)
+ <h3>Usage:</h3>
+ <ul>
+# for usage in iter(item.usage) do
+ $(li)<pre class="example">$(usage)</pre>$(il)
+# end -- for
+ </ul>
+# end -- if usage
+
+# if item.see then
+# local li,il = use_li(item.see)
+ <h3>see also:</h3>
+ <ul>
+# for see in iter(item.see) do
+ $(li)<a href="$(see.mod).html#$(see.name)">$(see.label)</a>$(il)
+# end -- for
+ </ul>
+# end -- if see
+</dd>
+# end -- for items
+</dl>
+# end -- for kinds
+
+# else -- if module
+
+# if ldoc.description then
+ <p>$(M(ldoc.description))</p>
+# end
+
+# for kind, mods in ldoc.kinds() do
+<h2>$(kind)</h2>
+# kind = kind:lower()
+# for m in mods() do
+<table class="module_list">
+ <tr>
+ <td class="name"><a href="$(no_spaces(kind))/$(m.name).html">$(m.name)</a></td>
+ <td class="summary">$(M(m.summary))</td>
+ </tr>
+# end -- for modules
+</table>
+# end -- for kinds
+# end -- if module
+
+</div> <!-- id="content" -->
+</div> <!-- id="main" -->
+<div id="about">
+</div> <!-- id="about" -->
+</div> <!-- id="container" -->
+</body>
+</html>
+
--- /dev/null
+---------------------------
+-- Test module providing bonzo.dog.
+-- Rest is a longer description
+-- @class module
+-- @name mod1
+
+--- zero function. Two new ldoc features here; item types
+-- can be used directly as tags, and aliases for tags
+-- can be defined in config.lp.
+-- @function zero_fun
+-- @p k1 first
+-- @p k2 second
+
+--- first function. Some description
+-- @param p1 first parameter
+-- @param p2 second parameter
+function mod1.first_fun(p1,p2)
+end
+
+-------------------------
+-- second function.
+-- @param ... var args!
+function mod1.second_function(...)
+end
+
+------------
+-- third function. Can also provide parameter comments inline,
+-- provided they follow this pattern.
+function mod1.third_function(
+ alpha, -- correction A
+ beta, -- correction B
+ gamma -- factor C
+ )
+end
+
+-----
+-- A useful macro. This is an example of a custom 'kind'.
+-- @macro first_macro
+-- @see second_function
+
+---- general configuration table
+-- @table config
+-- @field A alpha
+-- @field B beta
+-- @field C gamma
+mod1.config = {
+ A = 1,
+ B = 2,
+ C = 3
+}
+
+--[[--
+Another function. Using a Lua block comment
+@param p a parameter
+]]
+function mod1.zero_function(p)
+end
+
+
+
--- /dev/null
+---------------\r
+-- Markdown-flavoured and very simple no-structure style.\r
+--\r
+-- Here the idea is to structure the document entirely with [Markdown]().\r
+--\r
+-- Using the default markdown processor can be a little irritating: you are\r
+-- required to give a blank line before starting lists. The default stylesheet\r
+-- is not quite right, either.\r
+--\r
+module 'mod'\r
+\r
+--- Combine two strings _first_ and _second_ in interesting ways.\r
+function combine(first,second)\r
+end\r
+\r
+--- Combine a whole bunch of strings.\r
+function combine_all(...)\r
+end\r
+\r
+---\r
+-- Creates a constant capture. This pattern matches the empty string and\r
+-- produces all given values as its captured values.\r
+function lpeg.Cc([value, ...]) end\r
+\r
+\r
+--- Split a string _str_. Returns the first part and the second part, so that\r
+-- `combine(first,second)` is equal to _s_.\r
+function split(s)\r
+end\r
+\r
+--- Split a string _text_ into a table.\r
+-- Returns:\r
+--\r
+-- - `name` the name of the text\r
+-- - `pairs` an array of pairs\r
+-- - `key`\r
+-- - `value`\r
+-- - `length`\r
+--\r
+\r
+function split_table (text)\r
+end\r
+\r
+--- A table of useful constants.\r
+--\r
+-- - `alpha` first correction factor\r
+-- - `beta` second correction factor\r
+--\r
+-- @table constants\r
+\r
+\r
--- /dev/null
+project = 'LuaDoc'
+title = "LuaDoc with LDoc"
+package = 'luadoc'
+-- point this to your Luarocks directory
+file = [[c:\lua\lua\luadoc]]
--- /dev/null
+project = 'md-test'
+title = 'Markdown Docs'
+format = 'markdown'
--- /dev/null
+---------------------
+-- Another test module.
+-- This one uses _Markdown_ formating, and
+-- so can include goodies such as `code`
+-- and lists:
+--
+-- - one
+-- - two
+-- - three
+--
+-- @module mod2
+
+--- really basic function. Can contain links such as
+-- [this](http://lua-users.org/wiki/FindPage)
+-- @param first like `gsb(x)`
+-- @param second **bold** maybe? It can continue:
+--
+-- - another point
+-- - finish the damn list
+-- @param third as before
+function mod2.basic(first,second,third)
+
+end
+
+
--- /dev/null
+# Something for all!\r
+\r
+## First Topic\r
+\r
+A first topic\r
+\r
+## Second Topic\r
+\r
+A second topic\r
--- /dev/null
+--- this module has a comment.
+
+local local_two
+
+--- a local function
+local function local_one ()
+end
+
+--- a local function, needing explicit tag.
+-- @local here
+function local_two ()
+
+end
+
+--- A problem function.
+-- @param p a parameter
+function problem.fun(p)
+ return 42
+end
--- /dev/null
+------------
+-- A little old-style module
+local io = io
+-- we'll look for this
+module 'simple'
+
+-- if it were 'module (...)' then the name has to be deduced.
+
+--- return the answer.
+function answer()
+ return 42
+end
--- /dev/null
+------------
+-- A new-style module.
+-- Shows how @alias can be used to tell ldoc that a given name
+-- is a shorthand for the full module name
+-- @alias M
+
+local simple_alias = {}
+local M = simple_alias
+
+--- return the answer. And complete the description
+function M.answer()
+ return 42
+end
+
+return simple_alias
+
--- /dev/null
+------------
+-- A module containing tables.
+-- Shows how Lua table definitions can be conveniently parsed.
+-- @alias M
+
+local tables = {}
+local M = tables
+
+--- first table
+-- @table one
+M.one = {
+ A = 1, -- alpha
+ B = 2; -- beta
+}
+
+return M
+
--- /dev/null
+# And another\r
+\r
+## First\r
+\r
+A `first` topic\r
+\r
+## Second\r
+\r
+A `second` topic\r
--- /dev/null
+-----------------------
+-- Module using tparam for typed parameters.
+--
+-- @module types
+
+--- has typed parameters, `string` and `int`.
+-- And never forget `E = m*c^2`.
+-- Reference to `two.md.First`
+--
+-- A reference to `mydata`.
+-- @string name
+-- @int age
+-- @treturn mydata
+function _M.first (name,age)
+
+end
+
+--- simple function returning something
+-- @return bonzo dog!
+function _M.simple()
+
+end
+
+
+--- a table of this module.
+-- @table mydata
+_M.mydata = {
+ one = 1, -- alpha
+ two = 2, -- beta
+}
+
+
+
+
--- /dev/null
+--[[-----------
+ testing underscores and Markdown pre-formatting.
+
+ A list may immediately follow a line, if it is indented. Necessary lines
+ will also be inserted after resuming current indentation.
+ - a list
+ - sublist (note the needed indentation for Markdown to recognize this)
+ - more
+ * of items
+ more than one line
+ - indeed
+ can keep going
+Inline _see references_: look at @{_ONE_} and @{table.concat}.
+Indented code blocks may also follow immediately.
+ function test(a,b)
+ return three(a,b)
+ end
+ @module underscore
+]]
+
+----------
+-- A function.
+-- Can also use @{_TWO_ |second function}
+function _ONE_() end
+
+-----------
+-- another function
+-- @see string.format
+function _TWO_() end
+
+------
+-- a longer summary.
+-- @param a
+-- @param b
+-- @return one
+-- - one-1
+-- - one-2
+-- @return two
+function three(a,b) end