diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..57493eb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,51 @@ +# +# LuaDist Travis-CI Hook +# + +# We assume C build environments +language: C + +# Try using multiple Lua Implementations +env: + - TOOL="gcc" # Use native compiler (GCC usually) + - TOOL="clang" # Use clang + - TOOL="i686-w64-mingw32" # 32bit MinGW + - TOOL="x86_64-w64-mingw32" # 64bit MinGW + - TOOL="arm-linux-gnueabihf" # ARM hard-float (hf), linux + +# Crosscompile builds may fail +matrix: + allow_failures: + - env: TOOL="i686-w64-mingw32" + - env: TOOL="x86_64-w64-mingw32" + - env: TOOL="arm-linux-gnueabihf" + +# Install dependencies +install: + - git clone git://github.com/LuaDist/Tools.git ~/_tools + - ~/_tools/travis/travis install + +# Bootstap +before_script: + - ~/_tools/travis/travis bootstrap + +# Build the module +script: + - ~/_tools/travis/travis build + +# Execute additional tests or commands +after_script: + - ~/_tools/travis/travis test + +# Only watch the master branch +branches: + only: + - master + +# Notify the LuaDist Dev group if needed +notifications: + recipients: + - luadist-dev@googlegroups.com + email: + on_success: change + on_failure: always diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..005ce80 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright (C) 2007-2013 LuaDist. +# Created by Peter Drahos, Peter Kapec +# Redistribution and use of this file is allowed according to the terms of the MIT license. +# For details see the COPYRIGHT file distributed with LuaDist. +# Please note that the package source code is licensed under its own license. + +project ( asklua NONE ) +cmake_minimum_required ( VERSION 2.8 ) +include ( cmake/dist.cmake ) +include ( lua ) + +install_lua_module ( ask ask.lua ) +install_doc ( doc/ ) +install_example ( example/ ) +install_data ( license.txt readme.txt ) diff --git a/ask.lua b/ask.lua new file mode 100644 index 0000000..2cd7d31 --- /dev/null +++ b/ask.lua @@ -0,0 +1,741 @@ +--[[ + Simple help system for Lua modules + + Author: Julio M. Fernandez-Diaz + Date: Feb, 2010 + (For Lua 5.1) + + It is not intended to be complete, instead to be `enough'. + + Error messages are displayed in stderr +--]] + +-- Change the name of this file for other name if you wish +-- other name for querying help +local NAME = ... + +module(NAME, package.seeall) + +-- saving global names ------------------------------------------- + +local format, rep, gsub, sub = string.format, string.rep, string.gsub, string.sub +local find, char, byte, setlocale = string.find, string.char, string.byte, os.setlocale +local type, io, os, table, ipairs, pairs = type, io, os, table, ipairs, pairs +local _G, setmetatable, require, pcall, error = _G, setmetatable, require, pcall, error +local print = print + +-- default css if not provided +local CSS = [[ +]] + +-- private functions ------------------------------------------- + +-- returns "utf-8" if detected, nil otherwise +local function detectutf8 () + local c = setlocale(nil, "ctype"):lower() + local e = c:find("utf-8", 1, true) or c:find("utf8", 1, true) + return e and "utf-8" +end + +-- convert utf8 to iso +-- http://lua-users.org/lists/lua-l/2003-10/msg00281.html +local function utf8toiso (s) + if s:find("[\224-\255]") then error("non-ISO char") end + s = s:gsub("([\192-\223])(.)", function (c1, c2) + c1 = byte(c1) - 192 + c2 = byte(c2) - 128 + return char(c1 * 64 + c2) + end) + return s +end + +-- convert iso to utf8 +-- the inverse of +-- http://lua-users.org/lists/lua-l/2003-10/msg00281.html +local function isotoutf8 (s) + s = s:gsub("([\128-\255])", function (c) + local b = c:byte() + return b < 192 and "\194"..c or "\195"..char(b-64) + end) + return s +end + +-- convert iso-8859 <-> utf-8 +local function convertcs (s, cs, uft8) + if cs == "utf-8" and not utf8 then -- convert to iso + return utf8toiso(s) + elseif cs ~= "utf-8" and utf8 then -- convert to utf-8 + return isotoutf8(s) + end + return s +end + +-- for formatted writing in a file +local function fprintf (filehandler, fmt, ...) + filehandler:write(format(fmt, ...)) +end + +-- print a line of s with n characters in stderr +local function line (s, n) + n = n or 72 + io.stderr:write(rep(s, n).."\n") +end + +-- Prints the file with name filename in the filehandler +local function printcss (filehandler, filename) + local data + local f = io.open(filename, "rb") + if f == nil then + data = CSS + else + data = f:read("*all") + f:close() + end + filehandler:write(data) +end + +-- extract parts in what, by separating +-- [/][spath]^[info] +local function parts (what) + local absol, spath, info = "" + + if what == nil then return nil end + + what = what:gsub("%s", "") -- delete blanks + what = what:gsub("[%.]+", ".") -- collapse multiple . + + if what:sub(1,1) == "/" then -- search initial / + absol = "/" + what = what:sub(2) + end + + -- split parts + -- if more than one "^" appears only the last part is info + what:gsub("%^", "&") + what:gsub("^([^&]*)^([^&]*)$", function (w1, w2) spath, info = w1, w2 end) + if spath == nil then spath, info = what, "" + else + spath:gsub("&", "%^") + info:gsub("&", "%^") + end + + -- delete possible initial and final . + if spath:sub(1,1) == "." then spath = spath:sub(2) end + if spath:sub(-1) == "." then spath = spath:sub(1,-2) end + + return absol, spath, info +end + +-- split a string in fields separated by "." +local function splitdot (s) + local t = {} + s:gsub('([^%.]+)(%.*)', function (w, d) t[#t+1] = w end) + return t +end + +-- private variables ------------------------------------------- + +local Cases = {"Basic information", "List of functions", "Usage of ", + "More specific information", "See also", "Examples", + "Description of functions", "Version", "Notes"} +local cases = {"basic", "list", "usage", "more", "seealso", "example", + "description", "version", "notes", + } +local order = {b = 1, l = 2, u = 3, m = 4, s = 5, + e = 6, d = 7, v = 8, n = 9} -- order for "all" + +local basis = NAME + + +-- public objects ------------------------------------------- + +_H = { +_CHARSET = "iso-8859-15", -- "utf-8" in other cases +_basic = '`'..NAME..[[` is a system that provides help for modules. + +It searches eight types of information: + + basic list usage more seealso example version notes + +Execute `]]..NAME..'"/'..NAME..'^usage"` and `'..NAME..'"/'..NAME.. +[[^more"` for more explanation. + +Apart from this, documentation in `html` format can +be generated (see `]]..NAME..'.doc`).', +_usage = '`'..NAME..[[(search)` + +@params: + +1. `search` is a string in the form `"what^kind"` (a caret + separates two parts of the argument). + +@returns: nothing. + +@effects: it prints in `io.stderr`. + +`what` is what are looking for; if the first character in it is `/` +an *absolute* path is searched; if not the string defined in `]].. +NAME..[[.base` is used as a basis; + +`kind` is the type of information we want, that can be: + + "basic" | "list" | "usage" | "more" | "seealso" + "b" | "l" | "u" | "m" | "s" + + "example" | "version" | "notes" | "all" + "e" | "v" | "n" | "a" + +(as we see, we can use only the first letter). +If `all` is requested then all information +(basic, list, usage, seealso, example, version, notes) *if exists* is given. +If no kind is provided (in this case the caret is optional) `"basic"` is assumed. + +`]]..NAME..[[(nil)` and `]]..NAME..[[.about(nil)` are equivalent to +`]]..NAME..'"/'..NAME..[[^basic"`, that is, help about this helping system +is given.]], +_more = [[The search of information is controlled by two strings: +`what` and `kind`. Both are typed separated by a caret, `"^"`. + +The first, `what`, is what we are searching. Normally this +is a sequence `name1[.name2[.name3 ...] ]` in which `name1` is a module +name, and `name2`, `name3`, ... indicate functions in the module or +tables with functions in the module. + +If `what` begins with a slash, `/`, then an absolute path is searched. +Otherwise the help system adds as a prefix the string stored in a local +variable assigned with the function `]]..NAME..[[.base`. This improves the +interactivity because the user is not enforced to always type the +complete path of help. + +The second, `kind`, is the type of information we want: + ++ `basic`: the purpose of the module or function inside a module, ++ `list`: the listing of functions in the module, ++ `usage`: the use of a function, describing the arguments and returns, ++ `more`: more specific information, ++ `seealso`: some related information, ++ `example`: an example of use, ++ `version`: information about version and author, ++ `notes`: other information, usually license one, + ++ `all`: show all the previous information. + +For activate the help system the user (interactively) or some module +should invoke + + require"]]..NAME..[[" + +**Note**: the present help system manages a module variable `_H`. +This means that `_H` cannot be used for other purposes in +the module.]], +_example = [[Some examples (with equivalences): + +We assume the *first* invoking after `require"]]..NAME..[["` (by the user +or by some module): + + ]]..NAME..[["" --><-- ]]..NAME..[["/]]..NAME..[[^basic" + ]]..NAME..[["^a" --><-- ]]..NAME..[["/]]..NAME..[[^all" + ]]..NAME..[["/somemodule.fun^u" --><-- ]]..NAME..[["/somemodule.fun^usage" + + ]]..NAME..[[.base"somemodule" -- changes basis for searching + + ]]..NAME..[["^l" --><-- ]]..NAME..[["/somemodule^list" + ]]..NAME..[["fun^u" --><-- ]]..NAME..[["/somemodule.fun^usage" + ]]..NAME..[["/]]..NAME..[[^m" --><-- ]]..NAME..[["/]]..NAME..[[^more"]], +_Name = NAME, +_version = [[by Julio M. Fernández-Díaz, Dept. of Physics, +University of Oviedo, Spain, Version 0.1, February 2010 + +julio a t uniovi d o t es]], +_notes = [[THIS CODE IS HEREBY PLACED IN PUBLIC DOMAIN. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE.]] +} + +_H.about = { +_basic = [[Main function for on line help (synonym of `]]..NAME..[[`)]], +_usage = '`'..NAME..[[.about(search)` + + +`]]..NAME..[[(search)` + +@params: + +1. `search` is a string in the form `"what^kind"` (a caret + separates two parts of the argument). + +@returns: nothing. + +@effects: it prints in `io.stderr`. + +`what` is what are looking for; if the first character in it is `/` +an *absolute* path is searched; if not the string defined through +`]]..NAME..[[.base` is used as a basis; + +`kind` is the type of information we want, that can be: + + "basic" | "list" | "usage" | "more" | "seealso" + "b" | "l" | "u" | "m" | "s" + + "example" | "version" | "notes" | "all" + "e" | "v" | "n" | "a" + +(as we can see we can use only the first letter). +If `all` is requested then all information, +(basic, list, usage, seealso, example, version, notes) *if exists* is given. +If no kind is provided `"basic"` is assumed. + +Spaces typed by the user in the set `what^kind` are deleted before +the search of the help. Also, multiple `"."` are collapsed. + +`]]..NAME..[[(nil)` and `]]..NAME..[[.about(nil)` are equivalent to +`]]..NAME..'"/'..NAME..[[^basic"`, that is, help about this helping system +is given.]], +} + +function about (what) + what = what or "/"..NAME.."^basic" + if what == "" then what = "/"..basis.."^basic" end + + local absol, spath, info = parts(what) + + if absol ~= "/" then + if spath ~= "" then spath = basis.."."..spath + else spath = basis end + end + if info == "" then info = "basic" end + + local ts = splitdot(spath) + table.insert(ts, 2, "_H") + + if #info == 1 then + if info == "a" then info = "all" + elseif order[info] then info = cases[order[info]] end + end + + local tt = {info} + if info == "all" then + for k, v in ipairs(cases) do tt[k] = v end + end + + local H = _G + local CHARSET = H[ts[1]][ts[2]]["_CHARSET"] + local UTF8 = detectutf8() + + for k = 1, #ts do + if H then H = H[ts[k]] end + end + if H == nil and info ~= "list" then + io.stderr:write("No information about '/"..spath.."^"..info.."'\n") + io.stderr:flush() + return + end + + if info == "list" then tt = {"list"} end + + local function fill (t) + local pH = "" + local nc, ic, lef = 3, 1, 25 + + for j, s in ipairs(t) do + pH = pH..s..(" "):rep(lef-#s) + if ic == nc then pH = pH.."\n" end + ic = ic+1 + if ic > nc then ic = 1 end + end + return pH.."\n" + end + + for k, v in ipairs(tt) do + local H1, pH = H["_"..v], "" + if v == "list" then + -- special case : list + local al = {} + for a in pairs(H) do + if a:sub(1,1) ~= "_" then al[#al+1] = a end + end + if #al > 0 then + table.sort(al) + io.stderr:write(">>>>>>>>>> "..spath..":"..v.." <<<<<<<<<<\n") + pH = 'List of functions in "'..spath..'":\n\n'..fill(al) + io.stderr:write(pH) + line("-") + end + elseif H1 and type(H1) ~= "table" then + io.stderr:write(">>>>>>>>>> "..spath.." ^ "..v.." <<<<<<<<<<\n") + H1 = convertcs(H1, CHARSET, UTF8) + io.stderr:write(pH..H1.."\n") + line("-") + elseif info ~= "all" then + io.stderr:write(">>No information about '/"..spath.." ^ "..info.."'\n") + end + end + + io.stderr:flush() +end + +_H.base = { +_basic = [[Establishes a *basis prefix* for help searching]], +_usage = [[`]]..NAME..[[.base(basis)` + +@params: + +1. `basis`, string. + +@returns: nothing. + +This function changes the `basis` (a string) to add as a prefix in the +desired information path when this does not begin with a slash, `/`. + +When providind `basis` the slashdot `"/"` is not required. + +Initially basis has the value `"]]..NAME..[["` but the loading of a module +sets the current `basis` to the module name. + +When calling `]]..NAME..[[.base""` the current basis is displayed. + +Calling `]]..NAME..[[.base(nil)` establishes `"]]..NAME..[["` as basis.]] +} + +function base (b) + if b == nil or type(b) ~= "string" then b = NAME end + if b~= "" then + if b:sub(1,1) == "/" then b = b:sub(2) end + basis, now = b + io.stderr:write('--> Changing help basis to "'..basis..'"\n') + else + io.stderr:write('--> Help basis is "'..basis..'"\n') + end +end + +_H.doc = { +_basic = [[Create `html` documentation for a module]], +_usage = [[`]]..NAME..[[.doc(modulename, filename)` + +@params: + +1. `modulename`: string (optional) is the name of the module + of which we want the documentation. + If not provided then the basis is used. +2. `filename`: string (optional) is the name of the output file, + in `html` format. If not given the name of the module is used. + If one of the extensions `".html"` or `".htm"` (in lowercase) is + not provided then automatically `".html"` is added to the filename. + +@returns: nothing + +@effects: it creates a file. + +For generating the documentation module +[markdown.lua](http://www.frykholm.se/programming.html) from Niklas Frykholm +must be accessible. +(Note: the version in [luaforge.net](http://luaforge.net) is obsolete.)]], +_more = [[A CSS file called `default.css`, which is possible +to customize, is used. This file is embeded in the `html` output file. +If not provided the system uses an internal style. + +The resulting `html` file can be converted, v.g., to PS +with [html2ps](http://user.it.uu.se/~jan/html2ps.html), from Jan Kärrman. +After that `ps2pdf` can be used to convert it to PDF format.]], +} + +function doc (name, filename) + if type(name) ~= "string" then + io.stderr:write("A name of a module must be given\n") + return + end + + if name == "" then name = basis end + if _G[name] == nil then + io.stderr:write("Module "..name.." not found\n") + return + end + + local gn = _G[name] + if gn._H == nil then + io.stderr:write("Module "..name.." has no help information\n") + return + end + name = gn._H._Name + local CHARSET = gn._H._CHARSET + + filename = filename or name + if type(filename) ~= "string" then + io.stderr:write("A filename for output must be given\n") + return + end + + -- check for .html or .htm extension in filename + if filename:sub(-5) ~= ".html" and filename:sub(-4) ~= ".htm" then + filename = filename..".html" + end + + local f = io.open(filename, "w") + if f == nil then + io.stderr:write('"'..filename..' cannot be created\n') + return + end + + local md, markdown = pcall(require, "markdown") + if not md then + io.stderr:write("Markdown not found. No conversion is applied\n") + markdown = function (s) return s end + end + + local cart = {} + + gn = gn._H + + local function pref (s) + return ''..s..'' + end + + local function listfun (gn) + -- prepare an alphabetical list of (sub)functions + local al = {} + for a in pairs(gn) do + if a:sub(1,1) ~= "_" then al[#al+1] = {name = a} end + end + if #al > 0 then + table.sort(al, function (a, b) return a.name < b.name end) + end + for i, a in ipairs(al) do + local d = gn[a.name] + if d then + local b = listfun(d) + if #b > 0 then al[i][1] = b end + end + end + return al + end + + local allfun = listfun(gn) + + local function showlist (allfun, prev) + local cart = "" + for i, a in ipairs(allfun) do + local pr = prev == "" and "" or prev.."_" + cart = cart..'
  • '..''..a.name..'' + if #a > 0 then + cart = cart.."\n" + end + cart = cart..'
  • \n' + end + return cart + end + + local function navigate (gn, level, allfun) + -- check in order _-beginning fields + for i, c in ipairs(cases) do + local d = gn["_"..c] + local Ca = Cases[i] + if cases[i] == "usage" then + Ca = Ca..(level == 2 and "the module" or "function") + end + if d or c == "list" then + cart[#cart+1] = "
  • "..''..Ca.."
  • \n" + end + if c == "description" then + if #allfun > 0 then + cart[#cart+1] = "
  • "..''..Ca.."\n" + cart[#cart+1] = '
  • \n' + end + end + end + end + + -- navigation menu + + cart[#cart+1] = '\n' + + -- main part + + local function inside (gn, level, previous) + -- prepare an alphabetical list of (sub)functions + local al = {} + for a in pairs(gn) do + if a:sub(1,1) ~= "_" then al[#al+1] = a end + end + if #al > 0 then table.sort(al) end + + -- check in order _-beginning fields + for i, c in ipairs(cases) do + local d = gn["_"..c] + local Ca = Cases[i] + if cases[i] == "usage" then + Ca = Ca..(level == 2 and "the module" or "function") + end + local pH = "\n" + if c == "list" and #al > 0 then + local ts = splitdot(previous) + table.remove(ts, 1) + local s = table.concat(ts, "_") + if #s > 0 then s = s.."_" end + pH = pH..'

    \n' + for j, fun in ipairs(al) do + pH = pH..''..fun..'\n' + end + pH = pH.."

    \n\n" + local cc = level == 2 and ' id ="'..c..'"' or "" + cart[#cart+1] = '\n'..Ca..'\n' + cart[#cart+1] = pH + cart[#cart+1] = "\n" + end + if c == "description" and #al > 0 then + table.sort(al) + cart[#cart+1] = '\nDescription of functions\n\n' + local level1 = level+1 + for i, a in ipairs(al) do + local pre = previous == "" and a or previous.."."..a + local ts = splitdot(pre) + table.remove(ts, 1) + local s = table.concat(ts, "_") + cart[#cart+1] = ''..pre..'\n' + local d = gn[a] + if d then inside(d, level1+1, pre) end + end + end + if d then + local cc = level == 2 and ' id ="'..c..'"' or "" + --local cc = ' id ="'..c..'"' + cart[#cart+1] = '\n'..Ca..'\n' + cart[#cart+1] = pH..markdown(d) + cart[#cart+1] = "\n" + end + end + + end + + cart[#cart+1] = '
    \n' + cart[#cart+1] = "

    Module "..name.."

    \n" + + inside(gn, 2, name) + + cart[#cart+1] = '
    \n' + + -- printing + local s = table.concat(cart, "\n") + + + local head = [[ + + + + %s]] + local head1 = "\n \n" + + local foot = "\n\n" + + fprintf(f, head, "Module "..name) + + -- embeding CSS file + + printcss(f, "default.css") + + f:write(head1) + f:write(s) + f:write(foot) + + f:close() +end + +io.stderr:write('A help system is available. Call '..NAME..'(nil) for information.\n') + +base(NAME) + +__call = function (t, s, ...) + if s == nil then + about(nil) + elseif type(s) == "string" then + about(s) + else + return t[s](...) + end +end + +setmetatable(_G[NAME], _G[NAME]) + diff --git a/cmake/FindLua.cmake b/cmake/FindLua.cmake new file mode 100644 index 0000000..6991b4a --- /dev/null +++ b/cmake/FindLua.cmake @@ -0,0 +1,127 @@ +# Locate Lua library +# This module defines +# LUA_EXECUTABLE, if found +# LUA_FOUND, if false, do not try to link to Lua +# LUA_LIBRARIES +# LUA_INCLUDE_DIR, where to find lua.h +# LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8) +# +# Note that the expected include convention is +# #include "lua.h" +# and not +# #include +# This is because, the lua location is not standardized and may exist +# in locations other than lua/ + +#============================================================================= +# Copyright 2007-2009 Kitware, Inc. +# Modified to support Lua 5.2 by LuaDist 2012 +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) +# +# The required version of Lua can be specified using the +# standard syntax, e.g. FIND_PACKAGE(Lua 5.1) +# Otherwise the module will search for any available Lua implementation + +# Always search for non-versioned lua first (recommended) +SET(_POSSIBLE_LUA_INCLUDE include include/lua) +SET(_POSSIBLE_LUA_EXECUTABLE lua) +SET(_POSSIBLE_LUA_LIBRARY lua) + +# Determine possible naming suffixes (there is no standard for this) +IF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) + SET(_POSSIBLE_SUFFIXES "${Lua_FIND_VERSION_MAJOR}${Lua_FIND_VERSION_MINOR}" "${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}" "-${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}") +ELSE(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) + SET(_POSSIBLE_SUFFIXES "52" "5.2" "-5.2" "51" "5.1" "-5.1") +ENDIF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) + +# Set up possible search names and locations +FOREACH(_SUFFIX ${_POSSIBLE_SUFFIXES}) + LIST(APPEND _POSSIBLE_LUA_INCLUDE "include/lua${_SUFFIX}") + LIST(APPEND _POSSIBLE_LUA_EXECUTABLE "lua${_SUFFIX}") + LIST(APPEND _POSSIBLE_LUA_LIBRARY "lua${_SUFFIX}") +ENDFOREACH(_SUFFIX) + +# Find the lua executable +FIND_PROGRAM(LUA_EXECUTABLE + NAMES ${_POSSIBLE_LUA_EXECUTABLE} +) + +# Find the lua header +FIND_PATH(LUA_INCLUDE_DIR lua.h + HINTS + $ENV{LUA_DIR} + PATH_SUFFIXES ${_POSSIBLE_LUA_INCLUDE} + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw # Fink + /opt/local # DarwinPorts + /opt/csw # Blastwave + /opt +) + +# Find the lua library +FIND_LIBRARY(LUA_LIBRARY + NAMES ${_POSSIBLE_LUA_LIBRARY} + HINTS + $ENV{LUA_DIR} + PATH_SUFFIXES lib64 lib + PATHS + ~/Library/Frameworks + /Library/Frameworks + /usr/local + /usr + /sw + /opt/local + /opt/csw + /opt +) + +IF(LUA_LIBRARY) + # include the math library for Unix + IF(UNIX AND NOT APPLE) + FIND_LIBRARY(LUA_MATH_LIBRARY m) + SET( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") + # For Windows and Mac, don't need to explicitly include the math library + ELSE(UNIX AND NOT APPLE) + SET( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") + ENDIF(UNIX AND NOT APPLE) +ENDIF(LUA_LIBRARY) + +# Determine Lua version +IF(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") + FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua .+\"") + + STRING(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([^\"]+)\".*" "\\1" LUA_VERSION_STRING "${lua_version_str}") + UNSET(lua_version_str) +ENDIF() + +# Lua 5.2 +IF(NOT LUA_VERSION_STRING) + FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define LUA_VERSION_[A-Z]+[ \t]+\"[0-9]+\"") + STRING(REGEX REPLACE ".*#define LUA_VERSION_MAJOR[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_MAJOR ${lua_version_str}) + STRING(REGEX REPLACE ".*#define LUA_VERSION_MINOR[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_MINOR ${lua_version_str}) + STRING(REGEX REPLACE ".*#define LUA_VERSION_RELEASE[ \t]+\"([0-9]+)\".*" "\\1" LUA_VERSION_RELEASE ${lua_version_str}) + SET(LUA_VERSION_STRING ${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}.${LUA_VERSION_RELEASE}) +ENDIF() + +INCLUDE(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if +# all listed variables are TRUE +FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua + REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR + VERSION_VAR LUA_VERSION_STRING) + +MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY LUA_EXECUTABLE) + diff --git a/cmake/dist.cmake b/cmake/dist.cmake new file mode 100644 index 0000000..310ef94 --- /dev/null +++ b/cmake/dist.cmake @@ -0,0 +1,321 @@ +# LuaDist CMake utility library. +# Provides sane project defaults and macros common to LuaDist CMake builds. +# +# Copyright (C) 2007-2012 LuaDist. +# by David Manura, Peter Drahoš +# Redistribution and use of this file is allowed according to the terms of the MIT license. +# For details see the COPYRIGHT file distributed with LuaDist. +# Please note that the package source code is licensed under its own license. + +## Extract information from dist.info +if ( NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/dist.info ) + message ( FATAL_ERROR + "Missing dist.info file (${CMAKE_CURRENT_SOURCE_DIR}/dist.info)." ) +endif () +file ( READ ${CMAKE_CURRENT_SOURCE_DIR}/dist.info DIST_INFO ) +if ( "${DIST_INFO}" STREQUAL "" ) + message ( FATAL_ERROR "Failed to load dist.info." ) +endif () +# Reads field `name` from dist.info string `DIST_INFO` into variable `var`. +macro ( _parse_dist_field name var ) + string ( REGEX REPLACE ".*${name}[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" + ${var} "${DIST_INFO}" ) + if ( ${var} STREQUAL DIST_INFO ) + message ( FATAL_ERROR "Failed to extract \"${var}\" from dist.info" ) + endif () +endmacro () +# +_parse_dist_field ( name DIST_NAME ) +_parse_dist_field ( version DIST_VERSION ) +_parse_dist_field ( license DIST_LICENSE ) +_parse_dist_field ( author DIST_AUTHOR ) +_parse_dist_field ( maintainer DIST_MAINTAINER ) +_parse_dist_field ( url DIST_URL ) +_parse_dist_field ( desc DIST_DESC ) +message ( "DIST_NAME: ${DIST_NAME}") +message ( "DIST_VERSION: ${DIST_VERSION}") +message ( "DIST_LICENSE: ${DIST_LICENSE}") +message ( "DIST_AUTHOR: ${DIST_AUTHOR}") +message ( "DIST_MAINTAINER: ${DIST_MAINTAINER}") +message ( "DIST_URL: ${DIST_URL}") +message ( "DIST_DESC: ${DIST_DESC}") +string ( REGEX REPLACE ".*depends[ \t]?=[ \t]?[\"']([^\"']+)[\"'].*" "\\1" + DIST_DEPENDS ${DIST_INFO} ) +if ( DIST_DEPENDS STREQUAL DIST_INFO ) + set ( DIST_DEPENDS "" ) +endif () +message ( "DIST_DEPENDS: ${DIST_DEPENDS}") +## 2DO: Parse DIST_DEPENDS and try to install Dependencies with automatically using externalproject_add + + +## INSTALL DEFAULTS (Relative to CMAKE_INSTALL_PREFIX) +# Primary paths +set ( INSTALL_BIN bin CACHE PATH "Where to install binaries to." ) +set ( INSTALL_LIB lib CACHE PATH "Where to install libraries to." ) +set ( INSTALL_INC include CACHE PATH "Where to install headers to." ) +set ( INSTALL_ETC etc CACHE PATH "Where to store configuration files" ) +set ( INSTALL_SHARE share CACHE PATH "Directory for shared data." ) + +# Secondary paths +option ( INSTALL_VERSION + "Install runtime libraries and executables with version information." OFF) +set ( INSTALL_DATA ${INSTALL_SHARE}/${DIST_NAME} CACHE PATH + "Directory the package can store documentation, tests or other data in.") +set ( INSTALL_DOC ${INSTALL_DATA}/doc CACHE PATH + "Recommended directory to install documentation into.") +set ( INSTALL_EXAMPLE ${INSTALL_DATA}/example CACHE PATH + "Recommended directory to install examples into.") +set ( INSTALL_TEST ${INSTALL_DATA}/test CACHE PATH + "Recommended directory to install tests into.") +set ( INSTALL_FOO ${INSTALL_DATA}/etc CACHE PATH + "Where to install additional files") + +# Tweaks and other defaults +# Setting CMAKE to use loose block and search for find modules in source directory +set ( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true ) +set ( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH} ) +option ( BUILD_SHARED_LIBS "Build shared libraries" ON ) + +# In MSVC, prevent warnings that can occur when using standard libraries. +if ( MSVC ) + add_definitions ( -D_CRT_SECURE_NO_WARNINGS ) +endif () + +# RPath and relative linking +option ( USE_RPATH "Use relative linking." ON) +if ( USE_RPATH ) + string ( REGEX REPLACE "[^!/]+" ".." UP_DIR ${INSTALL_BIN} ) + set ( CMAKE_SKIP_BUILD_RPATH FALSE CACHE STRING "" FORCE ) + set ( CMAKE_BUILD_WITH_INSTALL_RPATH FALSE CACHE STRING "" FORCE ) + set ( CMAKE_INSTALL_RPATH $ORIGIN/${UP_DIR}/${INSTALL_LIB} + CACHE STRING "" FORCE ) + set ( CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE CACHE STRING "" FORCE ) + set ( CMAKE_INSTALL_NAME_DIR @executable_path/${UP_DIR}/${INSTALL_LIB} + CACHE STRING "" FORCE ) +endif () + +## MACROS +# Parser macro +macro ( parse_arguments prefix arg_names option_names) + set ( DEFAULT_ARGS ) + foreach ( arg_name ${arg_names} ) + set ( ${prefix}_${arg_name} ) + endforeach () + foreach ( option ${option_names} ) + set ( ${prefix}_${option} FALSE ) + endforeach () + + set ( current_arg_name DEFAULT_ARGS ) + set ( current_arg_list ) + foreach ( arg ${ARGN} ) + set ( larg_names ${arg_names} ) + list ( FIND larg_names "${arg}" is_arg_name ) + if ( is_arg_name GREATER -1 ) + set ( ${prefix}_${current_arg_name} ${current_arg_list} ) + set ( current_arg_name ${arg} ) + set ( current_arg_list ) + else () + set ( loption_names ${option_names} ) + list ( FIND loption_names "${arg}" is_option ) + if ( is_option GREATER -1 ) + set ( ${prefix}_${arg} TRUE ) + else () + set ( current_arg_list ${current_arg_list} ${arg} ) + endif () + endif () + endforeach () + set ( ${prefix}_${current_arg_name} ${current_arg_list} ) +endmacro () + + +# install_executable ( executable_targets ) +# Installs any executables generated using "add_executable". +# USE: install_executable ( lua ) +# NOTE: subdirectories are NOT supported +set ( CPACK_COMPONENT_RUNTIME_DISPLAY_NAME "${DIST_NAME} Runtime" ) +set ( CPACK_COMPONENT_RUNTIME_DESCRIPTION + "Executables and runtime libraries. Installed into ${INSTALL_BIN}." ) +macro ( install_executable ) + foreach ( _file ${ARGN} ) + if ( INSTALL_VERSION ) + set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} + SOVERSION ${DIST_VERSION} ) + endif () + install ( TARGETS ${_file} RUNTIME DESTINATION ${INSTALL_BIN} + COMPONENT Runtime ) + endforeach() +endmacro () + +# install_library ( library_targets ) +# Installs any libraries generated using "add_library" into apropriate places. +# USE: install_library ( libexpat ) +# NOTE: subdirectories are NOT supported +set ( CPACK_COMPONENT_LIBRARY_DISPLAY_NAME "${DIST_NAME} Development Libraries" ) +set ( CPACK_COMPONENT_LIBRARY_DESCRIPTION + "Static and import libraries needed for development. Installed into ${INSTALL_LIB} or ${INSTALL_BIN}." ) +macro ( install_library ) + foreach ( _file ${ARGN} ) + if ( INSTALL_VERSION ) + set_target_properties ( ${_file} PROPERTIES VERSION ${DIST_VERSION} + SOVERSION ${DIST_VERSION} ) + endif () + install ( TARGETS ${_file} + RUNTIME DESTINATION ${INSTALL_BIN} COMPONENT Runtime + LIBRARY DESTINATION ${INSTALL_LIB} COMPONENT Runtime + ARCHIVE DESTINATION ${INSTALL_LIB} COMPONENT Library ) + endforeach() +endmacro () + +# helper function for various install_* functions, for PATTERN/REGEX args. +macro ( _complete_install_args ) + if ( NOT("${_ARG_PATTERN}" STREQUAL "") ) + set ( _ARG_PATTERN PATTERN ${_ARG_PATTERN} ) + endif () + if ( NOT("${_ARG_REGEX}" STREQUAL "") ) + set ( _ARG_REGEX REGEX ${_ARG_REGEX} ) + endif () +endmacro () + +# install_header ( files/directories [INTO destination] ) +# Install a directories or files into header destination. +# USE: install_header ( lua.h luaconf.h ) or install_header ( GL ) +# USE: install_header ( mylib.h INTO mylib ) +# For directories, supports optional PATTERN/REGEX arguments like install(). +set ( CPACK_COMPONENT_HEADER_DISPLAY_NAME "${DIST_NAME} Development Headers" ) +set ( CPACK_COMPONENT_HEADER_DESCRIPTION + "Headers needed for development. Installed into ${INSTALL_INC}." ) +macro ( install_header ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} + COMPONENT Header ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_INC}/${_ARG_INTO} + COMPONENT Header ) + endif () + endforeach() +endmacro () + +# install_data ( files/directories [INTO destination] ) +# This installs additional data files or directories. +# USE: install_data ( extra data.dat ) +# USE: install_data ( image1.png image2.png INTO images ) +# For directories, supports optional PATTERN/REGEX arguments like install(). +set ( CPACK_COMPONENT_DATA_DISPLAY_NAME "${DIST_NAME} Data" ) +set ( CPACK_COMPONENT_DATA_DESCRIPTION + "Application data. Installed into ${INSTALL_DATA}." ) +macro ( install_data ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} + DESTINATION ${INSTALL_DATA}/${_ARG_INTO} + COMPONENT Data ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_DATA}/${_ARG_INTO} + COMPONENT Data ) + endif () + endforeach() +endmacro () + +# INSTALL_DOC ( files/directories [INTO destination] ) +# This installs documentation content +# USE: install_doc ( doc/ doc.pdf ) +# USE: install_doc ( index.html INTO html ) +# For directories, supports optional PATTERN/REGEX arguments like install(). +set ( CPACK_COMPONENT_DOCUMENTATION_DISPLAY_NAME "${DIST_NAME} Documentation" ) +set ( CPACK_COMPONENT_DOCUMENTATION_DESCRIPTION + "Application documentation. Installed into ${INSTALL_DOC}." ) +macro ( install_doc ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} + COMPONENT Documentation ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_DOC}/${_ARG_INTO} + COMPONENT Documentation ) + endif () + endforeach() +endmacro () + +# install_example ( files/directories [INTO destination] ) +# This installs additional examples +# USE: install_example ( examples/ exampleA ) +# USE: install_example ( super_example super_data INTO super) +# For directories, supports optional PATTERN/REGEX argument like install(). +set ( CPACK_COMPONENT_EXAMPLE_DISPLAY_NAME "${DIST_NAME} Examples" ) +set ( CPACK_COMPONENT_EXAMPLE_DESCRIPTION + "Examples and their associated data. Installed into ${INSTALL_EXAMPLE}." ) +macro ( install_example ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} + COMPONENT Example ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_EXAMPLE}/${_ARG_INTO} + COMPONENT Example ) + endif () + endforeach() +endmacro () + +# install_test ( files/directories [INTO destination] ) +# This installs tests and test files, DOES NOT EXECUTE TESTS +# USE: install_test ( my_test data.sql ) +# USE: install_test ( feature_x_test INTO x ) +# For directories, supports optional PATTERN/REGEX argument like install(). +set ( CPACK_COMPONENT_TEST_DISPLAY_NAME "${DIST_NAME} Tests" ) +set ( CPACK_COMPONENT_TEST_DESCRIPTION + "Tests and associated data. Installed into ${INSTALL_TEST}." ) +macro ( install_test ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} + COMPONENT Test ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_TEST}/${_ARG_INTO} + COMPONENT Test ) + endif () + endforeach() +endmacro () + +# install_foo ( files/directories [INTO destination] ) +# This installs optional or otherwise unneeded content +# USE: install_foo ( etc/ example.doc ) +# USE: install_foo ( icon.png logo.png INTO icons) +# For directories, supports optional PATTERN/REGEX argument like install(). +set ( CPACK_COMPONENT_OTHER_DISPLAY_NAME "${DIST_NAME} Unspecified Content" ) +set ( CPACK_COMPONENT_OTHER_DESCRIPTION + "Other unspecified content. Installed into ${INSTALL_FOO}." ) +macro ( install_foo ) + parse_arguments ( _ARG "INTO;PATTERN;REGEX" "" ${ARGN} ) + _complete_install_args() + foreach ( _file ${_ARG_DEFAULT_ARGS} ) + if ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${_file}" ) + install ( DIRECTORY ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} + COMPONENT Other ${_ARG_PATTERN} ${_ARG_REGEX} ) + else () + install ( FILES ${_file} DESTINATION ${INSTALL_FOO}/${_ARG_INTO} + COMPONENT Other ) + endif () + endforeach() +endmacro () + +## CTest defaults + +## CPack defaults +set ( CPACK_GENERATOR "ZIP" ) +set ( CPACK_STRIP_FILES TRUE ) +set ( CPACK_PACKAGE_NAME "${DIST_NAME}" ) +set ( CPACK_PACKAGE_VERSION "${DIST_VERSION}") +set ( CPACK_PACKAGE_VENDOR "LuaDist" ) +set ( CPACK_COMPONENTS_ALL Runtime Library Header Data Documentation Example Other ) +include ( CPack ) diff --git a/cmake/lua.cmake b/cmake/lua.cmake new file mode 100644 index 0000000..ee2e35d --- /dev/null +++ b/cmake/lua.cmake @@ -0,0 +1,309 @@ +# LuaDist CMake utility library for Lua. +# +# Copyright (C) 2007-2012 LuaDist. +# by David Manura, Peter Drahos +# Redistribution and use of this file is allowed according to the terms of the MIT license. +# For details see the COPYRIGHT file distributed with LuaDist. +# Please note that the package source code is licensed under its own license. + +set ( INSTALL_LMOD ${INSTALL_LIB}/lua + CACHE PATH "Directory to install Lua modules." ) +set ( INSTALL_CMOD ${INSTALL_LIB}/lua + CACHE PATH "Directory to install Lua binary modules." ) + +option ( LUA_SKIP_WRAPPER + "Do not build and install Lua executable wrappers." OFF ) +option ( LUA_STATIC_MODULE "Build modules for static linking" OFF ) + +# List of (Lua module name, file path) pairs. +# Used internally by add_lua_test. Built by add_lua_module. +set ( _lua_modules ) + +# utility function: appends path `path` to path `basepath`, properly +# handling cases when `path` may be relative or absolute. +macro ( _append_path basepath path result ) + if ( IS_ABSOLUTE "${path}" ) + set ( ${result} "${path}" ) + else () + set ( ${result} "${basepath}/${path}" ) + endif () +endmacro () + +# install_lua_executable ( target source ) +# Automatically generate a binary if srlua package is available +# The application or its source will be placed into /bin +# If the application source did not have .lua suffix then it will be added +# USE: lua_executable ( sputnik src/sputnik.lua ) +macro ( install_lua_executable _name _source ) + get_filename_component ( _source_name ${_source} NAME_WE ) + # Find srlua and glue + find_program( SRLUA_EXECUTABLE NAMES srlua ) + find_program( GLUE_EXECUTABLE NAMES glue ) + # Executable output + set ( _exe ${CMAKE_CURRENT_BINARY_DIR}/${_name}${CMAKE_EXECUTABLE_SUFFIX} ) + if ( NOT SKIP_LUA_WRAPPER AND SRLUA_EXECUTABLE AND GLUE_EXECUTABLE ) + # Generate binary gluing the lua code to srlua, this is a robuust approach for most systems + add_custom_command( + OUTPUT ${_exe} + COMMAND ${GLUE_EXECUTABLE} + ARGS ${SRLUA_EXECUTABLE} ${_source} ${_exe} + DEPENDS ${_source} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + VERBATIM + ) + # Make sure we have a target associated with the binary + add_custom_target(${_name} ALL + DEPENDS ${_exe} + ) + # Install with run permissions + install ( PROGRAMS ${_exe} DESTINATION ${INSTALL_BIN} COMPONENT Runtime) + # Also install source as optional resurce + install ( FILES ${_source} DESTINATION ${INSTALL_FOO} COMPONENT Other ) + else() + # Install into bin as is but without the lua suffix, we assume the executable uses UNIX shebang/hash-bang magic + install ( PROGRAMS ${_source} DESTINATION ${INSTALL_BIN} + RENAME ${_source_name} + COMPONENT Runtime + ) + endif() +endmacro () + +macro ( _lua_module_helper is_install _name ) + parse_arguments ( _MODULE "LINK;ALL_IN_ONE" "" ${ARGN} ) + # _target is CMake-compatible target name for module (e.g. socket_core). + # _module is relative path of target (e.g. socket/core), + # without extension (e.g. .lua/.so/.dll). + # _MODULE_SRC is list of module source files (e.g. .lua and .c files). + # _MODULE_NAMES is list of module names (e.g. socket.core). + if ( _MODULE_ALL_IN_ONE ) + string ( REGEX REPLACE "\\..*" "" _target "${_name}" ) + string ( REGEX REPLACE "\\..*" "" _module "${_name}" ) + set ( _target "${_target}_all_in_one") + set ( _MODULE_SRC ${_MODULE_ALL_IN_ONE} ) + set ( _MODULE_NAMES ${_name} ${_MODULE_DEFAULT_ARGS} ) + else () + string ( REPLACE "." "_" _target "${_name}" ) + string ( REPLACE "." "/" _module "${_name}" ) + set ( _MODULE_SRC ${_MODULE_DEFAULT_ARGS} ) + set ( _MODULE_NAMES ${_name} ) + endif () + if ( NOT _MODULE_SRC ) + message ( FATAL_ERROR "no module sources specified" ) + endif () + list ( GET _MODULE_SRC 0 _first_source ) + + get_filename_component ( _ext ${_first_source} EXT ) + if ( _ext STREQUAL ".lua" ) # Lua source module + list ( LENGTH _MODULE_SRC _len ) + if ( _len GREATER 1 ) + message ( FATAL_ERROR "more than one source file specified" ) + endif () + + set ( _module "${_module}.lua" ) + + get_filename_component ( _module_dir ${_module} PATH ) + get_filename_component ( _module_filename ${_module} NAME ) + _append_path ( "${CMAKE_CURRENT_SOURCE_DIR}" "${_first_source}" _module_path ) + list ( APPEND _lua_modules "${_name}" "${_module_path}" ) + + if ( ${is_install} ) + install ( FILES ${_first_source} DESTINATION ${INSTALL_LMOD}/${_module_dir} + RENAME ${_module_filename} + COMPONENT Runtime + ) + endif () + else () # Lua C binary module + enable_language ( C ) + find_package ( Lua REQUIRED ) + include_directories ( ${LUA_INCLUDE_DIR} ) + + set ( _module "${_module}${CMAKE_SHARED_MODULE_SUFFIX}" ) + + get_filename_component ( _module_dir ${_module} PATH ) + get_filename_component ( _module_filenamebase ${_module} NAME_WE ) + foreach ( _thisname ${_MODULE_NAMES} ) + list ( APPEND _lua_modules "${_thisname}" + "${CMAKE_CURRENT_BINARY_DIR}/\${CMAKE_CFG_INTDIR}/${_module}" ) + endforeach () + + # Static module (not linking to lua) + if ( LUA_STATIC_MODULE ) + add_library( ${_target} STATIC ${_MODULE_SRC}) + target_link_libraries ( ${_target} ${_MODULE_LINK} ) + else () + # Dynamic module + add_library( ${_target} MODULE ${_MODULE_SRC}) + target_link_libraries ( ${_target} ${LUA_LIBRARY} ${_MODULE_LINK} ) + endif () + + set_target_properties ( ${_target} PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY "${_module_dir}" + LIBRARY_OUTPUT_DIRECTORY "${_module_dir}" + PREFIX "" + OUTPUT_NAME "${_module_filenamebase}" ) + if ( ${is_install} ) + install ( TARGETS ${_target} + LIBRARY DESTINATION ${INSTALL_CMOD}/${_module_dir} + COMPONENT Runtime + ARCHIVE DESTINATION ${INSTALL_CMOD}/${_module_dir} + COMPONENT Library ) + endif () + endif () +endmacro () + +# add_lua_module +# Builds a Lua source module into a destination locatable by Lua +# require syntax. +# Binary modules are also supported where this function takes sources and +# libraries to compile separated by LINK keyword. +# USE: add_lua_module ( socket.http src/http.lua ) +# USE2: add_lua_module ( mime.core src/mime.c ) +# USE3: add_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) +# USE4: add_lua_module ( ssl.context ssl.core ALL_IN_ONE src/context.c src/ssl.c ) +# This form builds an "all-in-one" module (e.g. ssl.so or ssl.dll containing +# both modules ssl.context and ssl.core). The CMake target name will be +# ssl_all_in_one. +# Also sets variable _module_path (relative path where module typically +# would be installed). +macro ( add_lua_module ) + _lua_module_helper ( 0 ${ARGN} ) +endmacro () + + +# install_lua_module +# This is the same as `add_lua_module` but also installs the module. +# USE: install_lua_module ( socket.http src/http.lua ) +# USE2: install_lua_module ( mime.core src/mime.c ) +# USE3: install_lua_module ( socket.core ${SRC_SOCKET} LINK ${LIB_SOCKET} ) +macro ( install_lua_module ) + _lua_module_helper ( 1 ${ARGN} ) +endmacro () + +# Builds string representing Lua table mapping Lua modules names to file +# paths. Used internally. +macro ( _make_module_table _outvar ) + set ( ${_outvar} ) + list ( LENGTH _lua_modules _n ) + if ( ${_n} GREATER 0 ) # avoids cmake complaint + foreach ( _i RANGE 1 ${_n} 2 ) + list ( GET _lua_modules ${_i} _path ) + math ( EXPR _ii ${_i}-1 ) + list ( GET _lua_modules ${_ii} _name ) + set ( ${_outvar} "${_table} ['${_name}'] = '${_path}'\;\n") + endforeach () + endif () + set ( ${_outvar} +"local modules = { +${_table}}" ) +endmacro () + +# add_lua_test ( _testfile [ WORKING_DIRECTORY _working_dir ] ) +# Runs Lua script `_testfile` under CTest tester. +# Optional named argument `WORKING_DIRECTORY` is current working directory to +# run test under (defaults to ${CMAKE_CURRENT_BINARY_DIR}). +# Both paths, if relative, are relative to ${CMAKE_CURRENT_SOURCE_DIR}. +# Any modules previously defined with install_lua_module are automatically +# preloaded (via package.preload) prior to running the test script. +# Under LuaDist, set test=true in config.lua to enable testing. +# USE: add_lua_test ( test/test1.lua [args...] [WORKING_DIRECTORY dir]) +macro ( add_lua_test _testfile ) + if ( NOT SKIP_TESTING ) + parse_arguments ( _ARG "WORKING_DIRECTORY" "" ${ARGN} ) + include ( CTest ) + find_program ( LUA NAMES lua lua.bat ) + get_filename_component ( TESTFILEABS ${_testfile} ABSOLUTE ) + get_filename_component ( TESTFILENAME ${_testfile} NAME ) + get_filename_component ( TESTFILEBASE ${_testfile} NAME_WE ) + + # Write wrapper script. + # Note: One simple way to allow the script to find modules is + # to just put them in package.preload. + set ( TESTWRAPPER ${CMAKE_CURRENT_BINARY_DIR}/${TESTFILENAME} ) + _make_module_table ( _table ) + set ( TESTWRAPPERSOURCE +"local CMAKE_CFG_INTDIR = ... or '.' +${_table} +local function preload_modules(modules) + for name, path in pairs(modules) do + if path:match'%.lua' then + package.preload[name] = assert(loadfile(path)) + else + local name = name:gsub('.*%-', '') -- remove any hyphen prefix + local symbol = 'luaopen_' .. name:gsub('%.', '_') + --improve: generalize to support all-in-one loader? + local path = path:gsub('%$%{CMAKE_CFG_INTDIR%}', CMAKE_CFG_INTDIR) + package.preload[name] = assert(package.loadlib(path, symbol)) + end + end +end +preload_modules(modules) +arg[0] = '${TESTFILEABS}' +table.remove(arg, 1) +return assert(loadfile '${TESTFILEABS}')(unpack(arg)) +" ) + if ( _ARG_WORKING_DIRECTORY ) + get_filename_component ( + TESTCURRENTDIRABS ${_ARG_WORKING_DIRECTORY} ABSOLUTE ) + # note: CMake 2.6 (unlike 2.8) lacks WORKING_DIRECTORY parameter. + set ( _pre ${CMAKE_COMMAND} -E chdir "${TESTCURRENTDIRABS}" ) + endif () + file ( WRITE ${TESTWRAPPER} ${TESTWRAPPERSOURCE}) + add_test ( NAME ${TESTFILEBASE} COMMAND ${_pre} ${LUA} + ${TESTWRAPPER} "${CMAKE_CFG_INTDIR}" + ${_ARG_DEFAULT_ARGS} ) + endif () + # see also http://gdcm.svn.sourceforge.net/viewvc/gdcm/Sandbox/CMakeModules/UsePythonTest.cmake + # Note: ${CMAKE_CFG_INTDIR} is a command-line argument to allow proper + # expansion by the native build tool. +endmacro () + + +# Converts Lua source file `_source` to binary string embedded in C source +# file `_target`. Optionally compiles Lua source to byte code (not available +# under LuaJIT2, which doesn't have a bytecode loader). Additionally, Lua +# versions of bin2c [1] and luac [2] may be passed respectively as additional +# arguments. +# +# [1] http://lua-users.org/wiki/BinToCee +# [2] http://lua-users.org/wiki/LuaCompilerInLua +function ( add_lua_bin2c _target _source ) + find_program ( LUA NAMES lua lua.bat ) + execute_process ( COMMAND ${LUA} -e "string.dump(function()end)" + RESULT_VARIABLE _LUA_DUMP_RESULT ERROR_QUIET ) + if ( NOT ${_LUA_DUMP_RESULT} ) + SET ( HAVE_LUA_DUMP true ) + endif () + message ( "-- string.dump=${HAVE_LUA_DUMP}" ) + + if ( ARGV2 ) + get_filename_component ( BIN2C ${ARGV2} ABSOLUTE ) + set ( BIN2C ${LUA} ${BIN2C} ) + else () + find_program ( BIN2C NAMES bin2c bin2c.bat ) + endif () + if ( HAVE_LUA_DUMP ) + if ( ARGV3 ) + get_filename_component ( LUAC ${ARGV3} ABSOLUTE ) + set ( LUAC ${LUA} ${LUAC} ) + else () + find_program ( LUAC NAMES luac luac.bat ) + endif () + endif ( HAVE_LUA_DUMP ) + message ( "-- bin2c=${BIN2C}" ) + message ( "-- luac=${LUAC}" ) + + get_filename_component ( SOURCEABS ${_source} ABSOLUTE ) + if ( HAVE_LUA_DUMP ) + get_filename_component ( SOURCEBASE ${_source} NAME_WE ) + add_custom_command ( + OUTPUT ${_target} DEPENDS ${_source} + COMMAND ${LUAC} -o ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo + ${SOURCEABS} + COMMAND ${BIN2C} ${CMAKE_CURRENT_BINARY_DIR}/${SOURCEBASE}.lo + ">${_target}" ) + else () + add_custom_command ( + OUTPUT ${_target} DEPENDS ${SOURCEABS} + COMMAND ${BIN2C} ${_source} ">${_target}" ) + endif () +endfunction() diff --git a/dist.info b/dist.info new file mode 100644 index 0000000..fa21f9c --- /dev/null +++ b/dist.info @@ -0,0 +1,15 @@ +--- This file is part of the LuaDist project + +name = "asklua" +version = "0.1" + +desc = "AskLua provides a module that implements a help integrated system for on line use in the interactive interpreter, and for generating documentation in html and printed formats." +author = "Julio Manuel Fernandez-Diaz" +license = "Public domain" +url = "http://alien.luaforge.net" +maintainer = "Peter Drahos" + +depends = { + "lua ~> 5.1", + "markdown >= 0.3" +} diff --git a/doc/ask.html b/doc/ask.html new file mode 100644 index 0000000..be43b73 --- /dev/null +++ b/doc/ask.html @@ -0,0 +1,442 @@ + + + + + Module ask + + + +
    + +

    Module ask

    + + +

    Basic information

    + + + +

    ask is a system that provides help for modules.

    + +

    It searches eight types of information:

    + +
    basic   list   usage   more   seealso   example   version   notes
    +
    + +

    Execute ask"/ask^usage" and ask"/ask^more" for more explanation.

    + +

    Apart from this, documentation in html format can +be generated (see ask.doc).

    + + + + +

    List of functions

    + + +

    +about +base +doc +

    + + + + + +

    Usage of the module

    + + + +

    ask(search)

    + +

    @params:

    + +
      +
    1. search is a string in the form "what^kind" (a caret + separates two parts of the argument).
    2. +
    + +

    @returns: nothing.

    + +

    @effects: it prints in io.stderr.

    + +

    what is what are looking for; if the first character in it is / +an absolute path is searched; if not the string defined in ask.base is used as a basis;

    + +

    kind is the type of information we want, that can be:

    + +
    "basic"   | "list"    | "usage"   | "more"   | "seealso"
    +"b"       | "l"       | "u"       | "m"      | "s"
    +
    +"example" | "version" | "notes"   | "all"  
    +"e"       | "v"       | "n"       | "a"
    +
    + +

    (as we see, we can use only the first letter). +If all is requested then all information +(basic, list, usage, seealso, example, version, notes) if exists is given. +If no kind is provided (in this case the caret is optional) "basic" is assumed.

    + +

    ask(nil) and ask.about(nil) are equivalent to +ask"/ask^basic", that is, help about this helping system +is given.

    + + + + +

    More specific information

    + + + +

    The search of information is controlled by two strings: +what and kind. Both are typed separated by a caret, "^".

    + +

    The first, what, is what we are searching. Normally this +is a sequence name1[.name2[.name3 ...] ] in which name1 is a module +name, and name2, name3, ... indicate functions in the module or +tables with functions in the module.

    + +

    If what begins with a slash, /, then an absolute path is searched. +Otherwise the help system adds as a prefix the string stored in a local +variable assigned with the function ask.base. This improves the +interactivity because the user is not enforced to always type the +complete path of help.

    + +

    The second, kind, is the type of information we want:

    + +
      +
    • basic: the purpose of the module or function inside a module,

    • +
    • list: the listing of functions in the module,

    • +
    • usage: the use of a function, describing the arguments and returns,

    • +
    • more: more specific information,

    • +
    • seealso: some related information,

    • +
    • example: an example of use,

    • +
    • version: information about version and author,

    • +
    • notes: other information, usually license one,

    • +
    • all: show all the previous information.

    • +
    + +

    For activate the help system the user (interactively) or some module +should invoke

    + +
     require"ask"
    +
    + +

    Note: the present help system manages a module variable _H. +This means that _H cannot be used for other purposes in +the module.

    + + + + +

    Examples

    + + + +

    Some examples (with equivalences):

    + +

    We assume the first invoking after require"ask" (by the user +or by some module):

    + +
    ask""                   --><--    ask"/ask^basic"
    +ask"^a"                 --><--    ask"/ask^all"
    +ask"/somemodule.fun^u"  --><--    ask"/somemodule.fun^usage"
    +
    +ask.base"somemodule"    -- changes basis for searching
    +
    +ask"^l"                 --><--    ask"/somemodule^list"
    +ask"fun^u"              --><--    ask"/somemodule.fun^usage"
    +ask"/ask^m"             --><--    ask"/ask^more"
    +
    + + + + +

    Description of functions

    + + +

    ask.about

    + + +

    Basic information

    + + + +

    Main function for on line help (synonym of ask)

    + + + + +

    Usage of function

    + + + +

    ask.about(search)

    + + +

    ask(search)

    + +

    @params:

    + +
      +
    1. search is a string in the form "what^kind" (a caret + separates two parts of the argument).
    2. +
    + +

    @returns: nothing.

    + +

    @effects: it prints in io.stderr.

    + +

    what is what are looking for; if the first character in it is / +an absolute path is searched; if not the string defined through +ask.base is used as a basis;

    + +

    kind is the type of information we want, that can be:

    + +
    "basic"   | "list"    | "usage"   | "more"   | "seealso"
    +"b"       | "l"       | "u"       | "m"      | "s"
    +
    +"example" | "version" | "notes"   | "all"  
    +"e"       | "v"       | "n"       | "a"
    +
    + +

    (as we can see we can use only the first letter). +If all is requested then all information, +(basic, list, usage, seealso, example, version, notes) if exists is given. +If no kind is provided "basic" is assumed.

    + +

    Spaces typed by the user in the set what^kind are deleted before +the search of the help. Also, multiple "." are collapsed.

    + +

    ask(nil) and ask.about(nil) are equivalent to +ask"/ask^basic", that is, help about this helping system +is given.

    + + + +

    ask.base

    + + +

    Basic information

    + + + +

    Establishes a basis prefix for help searching

    + + + + +

    Usage of function

    + + + +

    ask.base(basis)

    + +

    @params:

    + +
      +
    1. basis, string.
    2. +
    + +

    @returns: nothing.

    + +

    This function changes the basis (a string) to add as a prefix in the +desired information path when this does not begin with a slash, /.

    + +

    When providind basis the slashdot "/" is not required.

    + +

    Initially basis has the value "ask" but the loading of a module +sets the current basis to the module name.

    + +

    When calling ask.base"" the current basis is displayed.

    + +

    Calling ask.base(nil) establishes "ask" as basis.

    + + + +

    ask.doc

    + + +

    Basic information

    + + + +

    Create html documentation for a module

    + + + + +

    Usage of function

    + + + +

    ask.doc(modulename, filename)

    + +

    @params:

    + +
      +
    1. modulename: string (optional) is the name of the module + of which we want the documentation. + If not provided then the basis is used.
    2. +
    3. filename: string (optional) is the name of the output file, + in html format. If not given the name of the module is used. + If one of the extensions ".html" or ".htm" (in lowercase) is + not provided then automatically ".html" is added to the filename.
    4. +
    + +

    @returns: nothing

    + +

    @effects: it creates a file.

    + +

    For generating the documentation module +markdown.lua from Niklas Frykholm +must be accessible. +(Note: the version in luaforge.net is obsolete.)

    + + + + +

    More specific information

    + + + +

    A CSS file called default.css, which is possible +to customize, is used. This file is embeded in the html output file. +If not provided the system uses an internal style.

    + +

    The resulting html file can be converted, v.g., to PS +with html2ps, from Jan Kärrman. +After that ps2pdf can be used to convert it to PDF format.

    + + + + +

    Version

    + + + +

    by Julio M. Fernández-Díaz, Dept. of Physics, +University of Oviedo, Spain, Version 0.1, February 2010

    + +

    julio a t uniovi d o t es

    + + + + +

    Notes

    + + + +

    THIS CODE IS HEREBY PLACED IN PUBLIC DOMAIN.

    + +

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE.

    + + + +
    + + diff --git a/doc/ask.pdf b/doc/ask.pdf new file mode 100644 index 0000000..54219d8 Binary files /dev/null and b/doc/ask.pdf differ diff --git a/doc/asklua.html b/doc/asklua.html new file mode 100644 index 0000000..966c8ed --- /dev/null +++ b/doc/asklua.html @@ -0,0 +1,865 @@ + + + + + <em>asklua</em>: adding interactive help to other modules + + + + + +

    asklua: adding interactive help to other modules

    + + +

    Julio Manuel Fernández-Díaz

    + +

    Profesor Titular, +Department of Physics, University of Oviedo (Spain), +February 2010

    + +

    Abstract:

    + +
    +

    We present AskLua, a system for managing help: for on line use in + the interactive interpreter, and for generating documentation in html + and printed formats.

    + +

    Module ask, provided by AskLua, is little intrusive and, although + it occupies some memory, it can be deleted by the user at any time if + he/she does not want to continue with the help on line.

    + +

    The system is fairly integrated, in such a way that it is possible to + easily add help for an existing module, even of binary type.

    +
    + + + +

    1 Introduction

    + +

    When we learn a new subject it is suitable to have information, as +complete as possible, about the topic. Often this is achieved through an +expert (teacher). Nevertheless, we have not always access to one. For +this it is necessary to be self-taught. In these occasions a good helping +system, as interactive as possible, is very interesting.

    + +

    Both Python and Matlab, for example, have interactive help on +line. This allows the beginners a comfortable access to the system during +the first phases of their learning.

    + +

    Obviously tools like the mentioned languages will have more use +with an easy access to the documentation. And this is very interesting +for designers of Lua modules.

    + +

    Lua has a quite minimalist approach, almost opposite to two mentioned. +Therefore, it is practically a chimera the inclusion of a help system +in the standard.

    + +

    Lua does not impose policies, but it is very extensible. This allows us +to easily include a system (module) of help, which is loaded if we wish.

    + +

    Literate programming, very aimed to the programmer, does not convince +me as a mechanism to solve our problem, since it is necessary to learn +another pseudo-language and it really serves for another thing.

    + +

    The system of documentation that accompanies stdlua with @param, +@returns, etc., is not of my taste, since it consists of +comments in the code. This is nice for checking and studying Lua +code, but to obtain a system of documentation on line it is +relatively complex. In addition, the documentation is also aimed to +the programmer and not so much to the user of some module (for example, +an engineer who wants to use Numlua).

    + +

    A system very similar to stdlua, though more advanced +is Luadoc, which allows the creation of documentation in html format, +also starting from comments in the modules. The final format is friendly +enough for managing the documentation in a browser, but it does +not serve for on line help, which is what I initially wished.

    + +

    In any case, both stdlua and Luadoc can be easily adapted +to our purposes (below explained): it is enough to integrate the +comments (by hand, before their use) in the corresponding help tables.

    + +

    When I was ending this documentation I discovered in LuaRocks +a module named LuaHelp (being December 13, 2009, the date of the +first version). Initially I thought that my work had been useless since +others have done the module that I was wishing. (Anyhow, new things are +always learnt on programming.) I have to say that the announce of it in +lua list goes unnoticed to me.

    + +

    Once analyzed the topic more deeply, I have realized the differences +(although LuaHelp might be adapted in many ways, obviously): the +documentation that I propose is included in the own module, whereas the +other one is included in auxiliary files placed in a concrete path. +In addition, the method here presented allows to consult separately +specific parts of the help: how a function in a module is used, +examples of use, the list of functions of a module, etc. They are two +different approaches with its advantages and disadvantages (that the +user can verify).

    + +

    The unique trouble is that we was compelled to change the name of my +module. Now its name is ask (and I had preferred help). That way +they can use both modules simultaneously. Anyhow, if the user wishes to +rename ask and to call it help only renaming ask.lua to help.lua +is sufficient. In this case it is interesting to change the notices that +a module gives when loaded (see section +4.3 How we know that a module has help).

    + +

    Another specific possibility is to load module ask, by referencing it +with a variable help in the following way:

    + +
    local help = require "ask"
    +
    + +

    Et voilà. We can choose both ask and help for using the present module +(from this moment we cannot use LuaHelp, obviously).

    + + + +

    2 The problem

    + +

    It has two aspects:

    + +
      +
    1. On the one hand, for the programmer of a module who wants to add help. + It will have to be easy to implement, with the minimal modifications + to his/her module.

    2. +
    3. On the other hand, for the module users. The browsing of the + documentation will have to be as complete as possible but of easy managing.

    4. +
    + +

    As programmers, we are interested in the ease of +adding documentation, as complete as possible. Also, if possible several +formats are welcome:

    + +
      +
    • on line in interactive Lua,
    • +
    • on line in a browser, which requires html format (although some + others exist, html is a standard),
    • +
    • printed format.
    • +
    + +

    In addition, the programmer is very interested in introducing the +documentation only once, and the existence of an automatic system to +produce several formats is nearly a must. This avoids many mistakes and the +modifications, when necessary, have to be done only in one place.

    + +

    From the user point of view, he/she will want that documentation is:

    + +
      +
    • easy to access, giving only the required information (not more, nor less);
    • +
    • the most complete possible, covering many aspects, even secondary in + occasions;
    • +
    • relational, that is, it can allow us to go from one point to another in + the documentation (if a relationship exists).
    • +
    + + +

    After many thoughts, many tests, and designing several versions of the help +module, finally the system explained below arised.

    + + +

    3 The solution

    + +

    The information of help in Lua can go only (more or less) in tables, with +strings, or in files (as in LuaHelp). It has seemed to me that +file managing would be cumbersome enough since files must be accessible and +possibly where the module resides. It would be +necessary to search in the paths of Lua and there might be more than one +file with the name for that we are looking for (and not to correspond the help to +what it is wished). Therefore I have preferred the other option.

    + +

    Our module ask must be able to access to these tables (if they +exist, but throwing a warning if no available information about a certain +module exists), then giving the proper required information.

    + + +

    I will describe module ask.

    + + +

    3.1 Where information is placed

    + +

    The first one is where is held the help information of a module. +In Lua it is obvious that a table is the right place, with possible +sub-tables, and finally text (strings). Besides, help for a module +(v.g., mininum, the example attached to ask) must be associated +to it in some way: Lua have to know the help like it knows about +module functions (otherwise it cannot "help" us).

    + +

    The help must be sub-divided. The final idea consists of including in +the module (v.g., inside module.lua) a table _H with fields and +sub-tables. Here is the structure:

    + +
    _H = {
    +  _CHARSET = "iso-8985-15",   -- or typically "utf-8"
    +  _Name    = [[Name of the module]],
    +
    +  _basic   = [[Basic help]],
    +  _usage   = [[Usage help]],
    +  _more    = [[Complementary information]],
    +  _seealso = [[Links to more information (internal or external)]],
    +  _example = [[An example of use]],
    +  _notes   = [[Notes (Copyright, etc.)]]
    +  _version = [[Version, name of the author and other subjects]],
    +}
    +
    + +

    (Note: _Name has a capital "N".)

    + + +

    _CHARSET identifies what encoding has been used in the texts of +help. For help in html format ask directly uses the value +in this field.

    + +

    For on line help, ask detects the operating system character encoding to convert +from the set of characters indicated in _CHARSET to that +one of the system (this way help is correctly visualized on screen). +At the moment this system only is implemented with UFT-8 and iso-8859: +If ask does not detect UTF-8 then it thinks iso-8859 is the enconding +in the operating system.

    + +

    This table is included in any point of the file module.lua (although its +logical place is near the beginning). None of the fields is mandatory: +if the programmer does not gives an example the field _example is +not specified.

    + +

    As we see, in order that the minimal interference exists with other +variables and functions of the module, I have put "_" in front of the +table name. In this way, we will avoid collisions of names: it is rare to +use names of this type (except for local variables). In any case, the user +of ask can change in the source code _H into another name with a text +editor (since the source code is provided) and to perform accordingly.

    + +

    Every function of the module (v.g., about, base and doc +in the module ask) can have its associate help. For example, for the +first function, it is:

    + +
    _H.about = {
    +  _basic   = [[Basic help]],
    +  _usage   = [[Usage help]],
    +  _more    = [[Complementary information]],
    +  _seealso = [[Links to more information (internal or external)]],
    +  _example = [[An example of use]],
    +}
    +
    + +

    In some cases we have tables of functions. For example, if tabfun +is a table with functions, namely f1 y f2, we could put:

    + +
    _H.tabfun = { ... }
    +
    +_H.tabfun.f1 = {
    +  _basic   = [[ ... ]],
    +  _usage   = [[ ... ]],
    +  _example = [[ ... ]],
    +}
    +
    +_H.tabfun.f2 = { ... }
    +
    + +

    Obviously if no information exists about some function, then we do not +have to specify it (as _more and _seealso in previous f1).

    + +

    Initially the format of the string going between [[ ]] was pure +text. With it, for the help on line it is enough. Nevertheless, it was +my desire that the information can also be printed. Then, a more +advanced format is better. It leaded me to the format markdown, +which in addition has an implementation in pure Lua, markdown.lua, +done by Niklas Frykholm. (Note: the version appearing in luaforge.net +is obsolete, but the one in Luarocks is correct.) +Actually, this document has been prepared in markdown format.

    + +

    This format is not much intrusive. This is important because I did not +want to filter help on line, but I wanted it untouched in the strings +of help.

    + + +

    3.2 How information is accessed

    + +

    We have already the information in its place (Lua tables). When the +module is loaded also these tables are. Then, from this moment they +are accessible (evidently they occupy space; in +5 How to use the help interactively +we will see how to free it if wished).

    + +

    Now we must talk about how to access the information. +For that, ask has three functions: about, base and doc.

    + + +

    | about |

    + +

    This was also the first one in being designed: it provides the +information on line. It is necessary to tell it about we want help +and which of the diverse information piceces we wish (for example +basic, for the basic information). Function about would be invoked, +for example:

    + +
    ask.about"/mininum.root^basic"
    +
    + +

    being mininum the name of the module, root the name of one of the +functions inside the module, and basic the type of information we wish +(I will explain below the inclusion of the initial "/"). +A caret "^" separates what we wish from the type of information +we want.

    + +

    In another example we would have:

    + +
    ask.about"/somemod.tabfun.f1^usage"
    +
    + +

    that is, we ask for usage help of the function f1 inside the table +tabfun of the module somemod.

    + +

    Another important information for the user exists: the list of functions +inside the module. The designer of the module has not to introduce it +because ask obtains this directly. For displaying it in +screen we have to use:

    + +
    ask.about"/mininum^list"
    +
    + + +

    Also it is possible to ask for all the information that exists about +something, by means of:

    + +
    ask.about"/mininum.root^all"
    +
    + +

    As we see, the user in this basic format of invocation of about has to +type much. The following thing that I did was to admit abbreviations for +the information type:

    + +
      +
    • b instead of basic, that provides basic information;
    • +
    • e instead of example, that shows us an example;
    • +
    • l instead of list, that shows a list of functions in the module + (or in a table of functions);
    • +
    • m instead of more, that shows additional information;
    • +
    • s instead of seealso, that cites us links to more information + (external or internal);
    • +
    • u instead of usage, that shows us how to use the module or function;
    • +
    • v instead of version, that shows us version information, name of the + authors, etc.
    • +
    • n instead of notes, that shows us notes as the Copyright, etc.
    • +
    • a instead of all, that provides all the previous information.
    • +
    + +

    In this way we have the equivalence:

    + +
    ask.about"/mininum^list"  --><--  ask.about"/mininum^l"
    +
    + +

    The spaces typed in the argument of ask.about are removed before +the search. Also about collapses multiple "." that could appear in the +searching string. For example, we would have the equivalence:

    + +
    ask.about "/ mininum . root ^ usage" --><-- ask.about"/mininum.root^u"
    +
    + +

    When information is not provided, by default it is supposed basic.

    + +

    By using nil as argument of ask.about we obtain basic information +about the help system. Then,

    + +
    ask.about()  --><--  ask.about("/ask^basic")
    +
    + +

    are equivalent.

    + +

    It is necessary to remember that ask provides the information the +designer of the module has included in the proper help tables (except for +list and all, obviously). That is, the information might be +incorrect, for example, if data about the version is included in _more.

    + + +

    | base |

    + +

    The reader can see we have still to type very much. +Normally when one works with a module (let's say Numlua) +we need help in an implicit way for it (and not for other module). +For it, I have included the function ask.base. +This function allows to implicitly assume an information searching prefix. +For example, by putting:

    + +
    ask.base"mininum"
    +-->  Changing help basis to "mininum"
    +
    + +

    from this moment the prefix "/mininum" is added to the search path +(sometimes with an added point ".", when referring to a module or to +a table of functions inside the module and we wish information about an +specific function). Then, these are equivalent:

    + +
    ask.about"/mininum^list"  --><--  ask.about"^l"
    +
    + +

    I have called this prefix basis (stored in a local variable).

    + +

    Invoking ask.base using as parameter an empty string "", it +shows us the current basis:

    + +
    ask.base""  
    +-->   Help basis is "mininum"
    +
    + +

    On the other hand, there has seemed suitable to me that:

    + +
    ask.base(nil)
    +
    + +

    changes basis to "ask".

    + +

    The on line help system is already operative, but I liked no much the +necessity of writing ask.about whenever I wanted help (altough, as read +ask.about"/mininum.root^usage" is nearly equivalent to ask about +mininum.root usage). The solution was to include the chunk of code:

    + +
    __call = function (t, s, ...)
    +   if s == nil then
    +      about(nil)
    +   elseif type(s) == "string" then
    +      about(s)
    +   else
    +      return t[s](...)
    +   end
    +end
    +
    +setmetatable(_G.ask, ask)
    +
    + +

    at the end of ask.lua. The last line allows us to use directly ask as +a global variable after doing require "ask". The previous chunk of code +allows the function about is automatically invoked when ask +receives a string as argument. Then

    + +
    ask.about"/mininum.root^usage" --><-- ask"root^u"
    +
    + +

    are equivalent (with basis = "mininum"). The simplification is +manifest, and it is near impossible to type less when asking help. +Actually, slightly less we can type if the module ask is invoked in +the way: h = require "ask"; from this moment we can put h"root^u". +Nevertheless, it seems to me that ask is not much writing, is more +descriptive, and it is possible that h has another use inside our +program.

    + + +

    | doc |

    + +

    With the previous function I had already the module with the wished +functionality. But it is a pity to have the information only on line and +not to be able to print it on a decent format. Therefore, I have designed +the function doc. The first idea was to adapt a bit the text output. But +a bit more of expense when writing the help, using the markdown +format, allows us an output in html format suitable enough. It was +easy, since a module exists, markdown.lua (that obviously must be +installed), which can be invoked from ask.

    + +

    The html version of help is complementary to the on line one. +It allows a more global vision of a given module, surfing the +information by browsing among the different functions inside it. +Hence, it is interesting enough.

    + +

    In this point system ask seems Luadoc: both generate html +format. Nevertheless, the starting points are very different.

    + +

    It is also possible to obtain the help in printed format, printing from +a browser. Probably it is better to use a program like html2ps +by Jan Kärrman, that generates a Postscript (PS) format (and then PDF, +also the latter with active hyperlinks).

    + +

    The function ask.doc has been re-designed several times, and +I am still not much happy with the result (though it works +correctly, without being very elegant). It generates html. +In this case, partial information of functions is not needed inside a module. +Therefore, the generated html file contains all the information +corresponding to the module (for it, basis must contain the name of the +module and not of one of the functions inside). +For example:

    + +
    ask.doc""
    +
    + +

    would create html help for basis (v.g., "mininum" previously), +with the name "mininum.html", and:

    + +
    ask.doc"ask"
    +
    + +

    would generate html help for ask.

    + + +

    The html file generated by ask.doc contains first generic information +about the module (the content of _H._basic, _H._usage, etc.), then +the help of all functions (in alphabetical order), to finish with +_H._version and _H._notes.

    + +
    + +

    I apologize since my programming is a little "dirty" in some parts +of the module. My other occupations have not allowed me more time for +improving it, and as one of my professors (León Garzón) said "the best +is enemy of the good".

    + + +

    4 How modules are adapted to add them help

    + +

    4.1 Why markdown format

    + +

    The markdown format is simple enough in order that a module +programmer learns it in an hour. It is not necessary for +the help on line (even some people can think it is +something annoying). I believe that it does not introduce much +noise in the on line help, and nevertheless it allows a good +presentation in html (and in PDF after other conversions).

    + +

    It is possible to look up in the indicated address, but there also +exists a two pages PDF document with a simple help of the +system Markdown Syntax Cheat Sheet (the truth, it is necessary +little more than this simple help to learn markdown).

    + +

    Thinking a bit more about the help in html and printed formats we could +introduce even references to images. It is evident that in the on line help +(always text mode) it is impossible to visualize an image, but its +reference can be given in markdown format (not much +intrusive). It would be in the following way:

    + +
    ![Alternative text](/path/to/image.jpg "Optional title")
    +
    + +

    (Instead of JPG format we can also use PNG or GIF.)

    + +

    The markdown format also allows to show links to other documents +(internal or external ones) in a simple way. In the on line help this is +not relevant but in html format is. For example, _seealso +can be designed including links in the way:

    + +
    internal:          [label](#reference)
    +
    +external:          [label](http://what.we.want)
    +
    + + +

    4.2 Preparing help data

    + +

    The general idea is simple: doing it in an incremental way. A complete +documentation can be a long and tedious task. Therefore, it would be +convenient to begin with _basic help, then _usage, since they are +both primary aspects. Since it is usual to put those as comments in +the head of functions, what we have to do is to convert the comments +already written in a piece of _H.

    + +

    Later, other parts that still remain can be added. An example (commented +if wished) is convenient for the user could try it (even copying and +pasting code).

    + +

    The logical place of introducing this information is before the +corresponding function (as usual), and in case of modules near the +beginning of the file.

    + +

    If one wants to prepare the help for a compiled module we have to do +the following.

    + +

    If the binary module is ours, and we can recompile it, we can internally +change the name of the table if we wish, renaming also the compiled +file. Then one creates a Lua source file, with the included help, with +the old name and extension .lua) that loads the compiled file.

    + +

    A second possibility is to name in a different way the compiled module +and the one that we are going to load with require. For example, +we have a module compiled in binary format of name mymodule.so (or +mymodule.dll). We will create a Lua file with name mymodule1.lua +of the form:

    + +
    -- file mymodule1.lua
    +require "mymodule"
    +mymodule._H = { ... }
    +
    + +

    To load mymodule we must use:

    + +
    require "mymodule1"
    +
    + +

    and we have available the functions mymodule.<function>, +and also the help mymodule._H, since this moment.

    + +

    Another third possibility is to utilize the same name, +using the fact that Lua +first looks for source files in LUA_PATH and then binary ones in +LUA_CPATH. Then, we would create a file mymodule.lua in some place +in LUA_PATH, in the way:

    + +
    -- file mymodule.lua
    +module(..., package.seeall)
    +
    +local p = package.path
    +package.path = ""
    +package.loaded["mymodule"] = nil
    +
    +require"mymodule"
    +package.path = p
    +
    +mymodule._H = [[ ... help ... ]]
    +
    + +

    In this way we can still use:

    + +
    require "mymodule"
    +
    + +

    as before, but now with included help.

    + +

    Anyhow, this third system might fail if the binary module mymodule.so +(or mymodule.dll) loads in turn modules in Lua source code because +during its load the search in package.path is deactivated.

    + + +

    4.3 How we know that a module has help

    + + +

    At the end of the Lua file containing the module it is possible to +include the following chunk of code to detect if Lua has been launched +in interactive mode:

    + +
    -- checks if Lua calling was interactive;
    +-- it does not work in all cases, but it does in the normal ones
    +local interactive = true
    +if _G.arg then
    +   for _, v in pairs(_G.arg) do
    +     interactive = false
    +     if v == "-i" then
    +       interactive = true
    +       break
    +     end
    +   end
    +end
    +
    + +

    This system of detection does not always work well (and it allows to +generate the html documentation with a simple line in the +operating system shell; we will see it in the following +section).

    + +

    It would be interesting that in interactive mode the Lua interpreter +creates a variable indicating it. Or at least it could use the existent +variable _PROMPT for it: at present, it is created only if the user +defines it at interpreter launch time, otherwise it is nil. +It would be enough to define _PROMPT in interactive mode to be +">" if the user did not declare another value, and leave it nil +in non-interactive mode.

    + +

    After detecting interactivity we have the following chunk:

    + +
    if interactive then
    +   -- reusing interactive
    +   interactive = pcall(require, "ask")                          --**
    +   if interactive then
    +      io.stderr:write('Module "mininum" loaded. ')
    +      io.stderr:write('To obtain help invoke ask"mininum".\n')  --**
    +      io.stderr:write('Documentation occupies memory. ')
    +      io.stderr:write('For freeing it let execute:\n')
    +      io.stderr:write('\n    mininum._H = nil\n\n')
    +
    +      ask.base"mininum"
    +   else
    +      io.stderr:write('Module "mininum" loaded. It has help\n')
    +      io.stderr:write('but module "ask" is not accesible.\n')   --**
    +      io.stderr:write('Help removed.\n')
    +   end
    +end
    +
    +if not interactive then
    +   -- deleting _H
    +   _H = nil
    +   _G.ask = nil
    +   collectgarbage()
    +end
    +
    + +

    This frees not necessary memory in non-interactive mode, and in +addition it shows on screen some messages in interactive mode.

    + +

    If we want to change the name of ask.lua to another one we will have +also to change the lines indicated by --** above in a similar way.

    + +

    Apart from this, in the future there is possible the inclusion of an +external utility to adapt the format that accompanies Luadoc (and +also the one that accompanies LuaHelp) for its use in the module +ask. This allows to depart not from void for modules whose documentation +was already prepared in another way.

    + + +

    5 How to use the help interactively

    + +

    As before we have exposed, a module that takes help will have to show when +loaded with require in order to the user knows that help is available.

    + +

    The last loaded module that holds help will change basis to the name +of the module. The user will have to bear in mind this fact. Anyhow, at +any time the user will be able to change basis or he/she will be punctually +able to use the system with the complete path (that begins with "/").

    + +

    If one wants to delete the help from memory it is easy. +Let's suppose that we have the module mininum loaded with:

    + +
    require "mininum"
    +
    + +

    then, it is enough to type:

    + +
    mininum._H = nil   -- repeat this with each module
    +collectgarbage()
    +
    + +

    After that, if we want to completely delete the help system, we will +write:

    + +
    _G.ask = nil
    +collectgarbage()
    +
    + +

    From this moment on, the memory is freed of the helping system and the +help information of module mininum (although, obviously its functions +are still loaded).

    + + +

    6 How html (and PDF) documentation is generated

    + +

    In this type of documents it is important to indicate the set of +characters used in the help. At these moments I use iso-8859-15 and +therefore, in the help the field is included:

    + +
    _H._CHARSET = "iso-8859-15"  -- "utf-8" sometimes
    +
    + +

    If one want to use utf-8 (more current these days) it is necessary +to change it in identical way. If not done so, some characters as "ü", +"ñ", "á", will appear incorrectly. Normally, as we present modules help +in English, people names and similar are usually the only words affected +by this matter.

    + +

    A very simple way to obtain the documentation in html format would be +the following one in interactive mode:

    + +
    $ lua
    +> require "mininum"
    +> ask.doc""
    +
    + +

    This would create mininum.html. Nevertheless, due to a small problem in +the detection of the interactivity (see the following section), the same +documentation can be also obtained by means of:

    + +
    lua -e "require'mininum'; ask.doc''"
    +
    + +

    Once file mininum.html is created it is possible to visualize it +with a browser to consult the help. The given style sheet default.css +places at the left part of the page a fixed menu with the links of the +whole document, including the list of functions inside the module.

    + +

    The style of the html output is customizable, through a style sheet +with name default.css (hardcoded inside ask.lua). I recomend to +do it only with knowledge of CSS, but it does not hurt to play with it.

    + +

    When a lot of functions exist in the module it is possible that some links +disappear at the bottom of the screen. In this case it is necessary +to diminish the font size (or to visualize the document without style, +though ugly, it is still operative). Occasionally the font size should +be diminished to see the examples (the lines do not wrap in the html +elements of type <pre>; the examples are converted to <pre> elements).

    + +

    Apart from viewing the html document it is also possible to +print it, in the browser (not advisable) or with a +specific program. We recommend the use of html2ps, +which converts html in PS, with a style sheet a bit simplified +(see the documentation provided in the link). In this case, the menu of +navigation is not printed.

    + +

    Later, if wished, we can convert the PS file into PDF by means +of ps2pdf that accompanies Ghostscript. This method keeps +the links, and we can navigate also in the PDF file.

    + + +

    7 Weaknesses and possible improvements

    + +

    Firstly, due to the fact that we modify some tables (internal ones in +the module that is been modified) it is possible that some collateral +effects appear (which I have still not discovered).

    + +

    On the other hand, a module can be loaded in a local variable, v.g., +in the way:

    + +
    local m = require"mininum"
    +
    + +

    to use "m." as prefix for the functions of mininum and to write less. +From this moment it is possible to access the help with mininum and with +m. This does not matter very much in interactive mode (as it is possible to +access the functions with m.fun and with mininum.fun).

    + +

    Nonetheless, ask.doc"m" creates a file mininum.html with the correct name +(and not with the name m.html).

    + +

    Functions in the module that are going to have help must not begin +with "_" since ask supposes they are a part of +its system. We can begin a function with "_" but it cannot have help.

    + +

    Sometimes the interactivity does not work well. For example:

    + +
    lua -e "require'mininum'" somefile.lua
    +
    + +

    shows an initial message and in addition it does not erase the help. Then, +when it executes somefile.lua the help tables are in memory (what can +be counter-productive).

    + +

    Nevertheless it does not matter too much, because it is enough to +include:

    + +
    require"mininum"
    +
    + +

    at the beginning of somefile.lua, to invoke lua without the +option -e, and then the module unloads ask and the help of +mininum (as soon as it detects that the mode is non-interactive) +before executing the rest of the program.

    + +

    In interactive mode, in the future it is possible to include http links +for more complete help (for example for a description of an algorithm +or to give a link to a scientific article), or to verify if there are new +versions of a given module. When some of these links appear +a browser might be started when the user was asking for help of this type +(there would be necessary to include a new type, v.g., link with +k as abbreviation).

    + +

    Tests could also be added (for every function in the module; partly they +might be the executable examples). For example, two fields would +be included: _test with code and _testsolution with text. +On having executed the first one it should obtain the second one +(the module ask would verify that they are equal). I do not know if +this is interesting. There would be necessary to study it more deeply.

    + +

    The system as presented here neither is closed nor is the definitive +solution. It can have much improvement. The author wishes this version +of the module can serve as an incentive for other programmers think about +the topic and develop even better systems (or complete this).

    + + +

    8 Conclusions

    + +

    We have designed an interesting system to provide help for +other modules. This is versatile, and it allows on line +access, html doc generation for its review in a browser, +and printed format (PS and PDF).

    + +

    This system is not perfect, but operative. The author believes +that it can be the seed of a more complete help system +for Lua. The advanced programmers can do the modifications +they wish for their personal use. Actually, AskLua is released +in the public domain. It is awaiting improvements.

    + + + + \ No newline at end of file diff --git a/doc/asklua.pdf b/doc/asklua.pdf new file mode 100644 index 0000000..7e718d9 Binary files /dev/null and b/doc/asklua.pdf differ diff --git a/doc/asklua_spanish.html b/doc/asklua_spanish.html new file mode 100644 index 0000000..3b8a574 --- /dev/null +++ b/doc/asklua_spanish.html @@ -0,0 +1,899 @@ + + + + + <em>AskLua</em>: añadiendo ayuda interactiva a otros módulos + + + + + +

    AskLua: añadiendo ayuda interactiva a otros módulos

    + + +

    Julio Manuel Fernández-Díaz

    + +

    Profesor Titular, +Departamento de Física de la Universidad de Oviedo (España), +Febrero de 2010

    + +

    Resumen:

    + +
    +

    Se presenta AskLua un sistema de + gestión de ayuda para Lua: en línea desde + el intérprete interactivo, en formato html y en formato impreso.

    + +

    El módulo ask, proporcionado por AskLua, + es poco intrusivo ocupando memoria que puede ser + liberada por el usuario en cualquier momento si no desea seguir con + la ayuda en línea.

    + +

    El sistema está bastante bien integrado, de tal manera que + se puede añadir fácilmente ayuda para un módulo ya existente, + incluso de tipo binario.

    +
    + + + +

    1 Introducción

    + +

    Cuando aprendemos una nueva materia es conveniente disponer de información +lo más completa posible sobre el tema. Muchas veces esto se logra con +un experto (profesor). Sin embargo, no siempre tenemos acceso a uno, +por lo que hay que ser autodidacta. En esas ocasiones un buen sistema +de ayuda, lo más interactivo posible, es de gran ayuda.

    + +

    Tanto Python como Matlab, por poner dos ejemplos, tienen +ayuda interactiva en línea. Esto permite a los principiantes un +cómodo acceso durante las primeras fases de aprendizaje del sistema.

    + +

    Obviamente una herramienta como los lenguajes citados tendrá más +uso si se facilita el acceso a la documentación. Y eso le interesa +mucho al diseñador de un módulo concreto.

    + +

    Lua tiene un enfoque casi contrario a los dos lenguajes citados. +Por tanto, es prácticamente una quimera que pueda incluir +un sistema de ayuda en el estándar.

    + +

    Lua no impone políticas, pero es muy +extensible. Esto nos permite incluir fácilmente un sistema (módulo) +de ayuda, que se carga cuando se desea.

    + +

    La Programación literaria, muy orientada al programador, no me +convence como mecanismo, pues hay que aprender otro pseudolenguaje +y realmente sirve para otra cosa.

    + +

    El sistema de documentación que acompaña a stdlua con @param, +@returns, etc., tampoco es de mi agrado, pues consiste en comentarios al +código. Esto está bien para revisar y estudiar código Lua, pero conseguir +un sistema de documentación en línea en este caso es relativamente +complejo. Además la documentación está también orientada al programador +y no tanto al usuario de algún módulo (por ejemplo, un ingeniero que +quiere usar Numlua).

    + +

    Un sistema muy similar al que viene en stdlua, aunque más avanzado +es Luadoc, que permite generar documentación en formato html +a partir también de comentarios en los módulos. El formato final +es bastante amigable para consultar la documentación en un navegador, +pero no sirve para consultas en línea, que es lo que se deseaba +inicialmente.

    + +

    En cualquier caso, tanto el sistema de stdlua como el de Luadoc pueden +adaptarse fácilmente a lo que se propone más abajo: basta con integrar +los comentarios en las correspondientes tablas de ayuda.

    + +

    Cuando estaba acabando esta documentación he descubierto en LuaRocks +un módulo denominado LuaHelp (con fecha de la primera versión de +13 de diciembre de 2009). Al principio pensé que mi +trabajo había sido en vano pues ya estaba hecho el módulo que yo deseaba. +(De todos modos siempre se aprenden cosas nuevas al programar.) +He de decir que se me pasó su anuncio en la lista de correo de lua.

    + +

    Una vez analizado el tema con más profundidad, me he dado cuenta de +las diferencias (aunque LuaHelp se podría adaptar de muchas maneras +evidentemente): la documentación que yo propongo va incluida en el propio +módulo, mientras que la otra se incluye en ficheros auxiliares situados en +un camino de búsqueda concreto. Además el método aquí presentado permite +consultar por separado partes concretas de la ayuda: cómo se usa una +función dentro de un módulo, ejemplos de uso, la lista de funciones de un +módulo, etc. Son dos enfoques distintos con sus ventajas e inconvenientes +(que el usuario puede comprobar por sí mismo).

    + +

    La única pega es que me ha hecho cambiar el nombre de mi módulo. Ahora se +denomina ask (y yo hubiera preferido help). De esa manera se pueden +utilizar ambos a la vez. De todos modos, si se desea renombrar ask +y llamarlo help sólo renombrando ask.lua a help.lua es suficiente. +En ese caso es interesante cambiar los avisos que un módulo da al cargarse +(véase el apartado 4.3 Cómo sabemos que un módulo tiene ayuda).

    + +

    Otra posibilidad puntual es cargar el módulo ask referenciándolo con +variable de nombre help de la manera siguiente:

    + +
    local help = require`ask`
    +
    + +

    Et voilà. Ya podemos usar tanto ask como help para usar el +presente módulo (a partir de ese momento no podremos usar LuaHelp +evidentemente).

    + + + +

    2 El problema

    + +

    Tiene dos aspectos:

    + +
      +
    1. Por un lado, para el programador de un módulo al que se quiere + añadir ayuda. Deberá ser fácil de implementar, con las mínimas + modificaciones a su módulo.

    2. +
    3. Por otro, para los usuarios del módulo. La consulta de la + documentación deberá ser lo más completa posible pero de fácil + manejo.

    4. +
    + +

    En el primer aspecto, nos interesa como programadores que sea +fácil de añadir la documentación, lo más completa posible. También +que sea fácil darle uno de los tres formatos siguientes:

    + +
      +
    • en línea en Lua interactivo,
    • +
    • en línea en un navegador que requiere evidentemente formato html + (es preferible este formato estándar que otros existentes),
    • +
    • en formato impreso.
    • +
    + +

    Además, al programador le interesa muchísimo tener que +introducir la documentación una sola vez +y que un sistema automático genere los diferentes formatos. +Esto evita muchos errores y si hay que hacer algún cambio lo +deberá hacer en un solo lugar.

    + +

    Desde el punto de vista del usuario, éste querrá que sea:

    + +
      +
    • fácil la consulta, proporcionando sólo la información que se pida en + cada momento, ni más ni menos;
    • +
    • lo más completa posible, cubriendo muchos aspectos, incluso + colaterales a veces;
    • +
    • relacional, o sea que nos permita ir de un lugar a otro de + la documentación siempre que existan relaciones entre diferentes + partes de la misma.
    • +
    + +

    Tras darle vueltas a la cuestión, hacer diversas probaturas y +versiones del módulo de ayuda, al final he llegado al +sistema que se explica en el apartado siguiente.

    + + +

    3 La solución

    + +

    La información de ayuda en Lua sólo (bueno más o menos) puede ir en +tablas, con strings, o en ficheros (como en LuaHelp). Me ha parecido +que el manejo de ficheros sería bastante engorroso pues deben estar +accesibles y deberían estar posiblemente donde el módulo propiamente +dicho. Habría que buscar en los path de Lua y podría haber más de un +fichero con el nombre que buscamos y no corresponder la ayuda a lo que +se desea. Por tanto he preferido la otra opción.

    + +

    Nuestro módulo de ayuda ask debe poder acceder a esas +tablas (si existen y sino avisar de que no hay disponible +información sobre un determinado módulo) y procesar la +ayuda como se le pida.

    + +

    Vamos a describir el módulo ask.

    + + + +

    3.1 Dónde se almacena la información

    + +

    Lo primero es dónde se almacena la ayuda relativa a un módulo. +En Lua es evidente que debe ir en una tabla, con posibles subtablas, +y al final texto (strings). Además, la ayuda de un módulo (digamos +mininum, el ejemplo que se adjunta con ask), debe estar asociada +al mismo de alguna manera: Lua debe conocer la ayuda al igual que conoce +las funciones del módulo (sino no podría "ayudarnos").

    + +

    La ayuda debe estar compartimentada. La idea final consiste en +incluir en el módulo una tabla _H con campos y también subtablas. +He aquí la estructura:

    + +
    _H = {
    +  _CHARSET = "iso-8985-15",   -- otras veces "utf-8"
    +  _Name    = [[Nombre del módulo]],
    +
    +  _basic   = [[Ayuda básica]],
    +  _usage   = [[Ayuda de cómo se utiliza]],
    +  _more    = [[Información complementaria]],
    +  _seealso = [[Enlaces a más información interna o externa]],
    +  _example = [[Un ejemplo de utilización]],
    +  _version = [[Versión, nombre del autor y otras cuestiones]],
    +  _notes   = [[Notas, como pueden ser las de Copyright, etc.]]
    +}
    +
    + +

    (Nota: fíjense en la N mayúscula de _Name.)

    + +

    _CHARSET indica qué codificación se ha usado en los textos de +ayuda. Para ayuda generada en formato html el sistema usa +directamente el valor indicado en este campo.

    + +

    Para ayuda en línea, ask detecta la codificación de caracteres +del sistema operativo para convertir la ayuda del conjunto de +caracteres indicado en _CHARSET al del sistema (y así visualizarse +correctamente en pantalla). De momento este sistema sólo está +implementado con UTF-8 e iso-8859: si ask no detecta UTF-8 +entonces piensa que el sistema operativo trabaja con iso-8859.

    + +

    Esta tabla se incluye en cualquier punto del fichero módulo.lua +(aunque lo normal es que vaya cerca del principio). Ninguno de los +campos es obligatorio. Digamos que si no se presenta un ejemplo +el campo _example no se especifica.

    + +

    Como vemos, para que exista la mínima interferencia con otras +variables y funciones del módulo le he puesto delante del nombre +"_". Eso evitará colisiones de nombres: es raro utilizar nombres +de ese tipo (excepto para variables locales). En cualquier caso +el usuario de ask puede cambiar en el código fuente del mismo +_H por otro nombre con un editor de textos (ya que se proporciona +el código fuente) y tenerlo en cuenta en su propio módulo.

    + +

    Cada una de las funciones del módulo (v.g. about, base y doc +en el módulo ask) debe tener su ayuda asociada de la manera (para +la primera de las funciones):

    + +
    _H.about = {
    +  _basic   = [[Una descripción somera del propósito de la función]],
    +  _usage   = [[Una descripción completa de la manera de invocar
    +               la función, con sus argumentos y sus retornos]],
    +  _more    = [[Información complementaria]],
    +  _seealso = [[Enlaces a más información interna o externa]],
    +  _example = [[Un ejemplo de utilización]],
    +}
    +
    + +

    En algunos casos se tiene tablas de funciones. Si por ejemplo tabfun +es una tabla con funciones, digamos f1 y f2, deberíamos poner:

    + +
    _H.tabfun = { ... }
    +
    +_H.tabfun.f1 = {
    +  _basic   = [[ ... ]],
    +  _usage   = [[ ... ]],
    +  _example = [[ ... ]],
    +}
    +
    +_H.tabfun.f2 = { ... }
    +
    + +

    Obviamente si no existe algún tipo de información pues no se especifica +(como _more y _seealso en la f1 anterior).

    + +

    Inicialmente el formato del texto que va entre [[ ]] era texto +puro. Con eso para la ayuda en línea es suficiente. Sin embargo +era mi deseo que la información también se pudiera imprimir y +entonces algo de formato más avanzado es mejor. Eso me llevó +al formato markdown, que además tiene una implementación +en Lua puro markdown.lua realizada por Niklas Frykholm. +(Nota: la versión que aparece en luaforge.net está anticuada, +pero la que aparece en LuaRocks es correcta.) +Realmente, este documento ha sido preparado usando markdown.

    + +

    Ese formato no es muy intrusivo. Esto es importante porque no +deseaba hacer un filtrado de la ayuda en línea, sino que quería +que apareciera tal cual está en los strings de ayuda.

    + + +

    3.2 Cómo se accede a la información

    + +

    Bueno, ya tenemos la información en su sitio (tablas de Lua). +Cuando se carga el módulo también se cargan esas tablas que +están accesibles desde ese momento (evidentemente ocupan espacio; +luego, en 5 Cómo usar la ayuda interactivamente +veremos cómo liberarlo si se desea).

    + +

    Ahora debemos hablar de cómo se accede a la información. +Para ello ask tiene tres funciones: about, base y doc.

    + + +

    | about |

    + +

    Ésta fue la primera en ser diseñada: +proporciona la información en línea. Hay que indicarle sobre +qué queremos ayuda y cuál de las diversas partes de la +información deseamos (por ejemplo basic, para la información +básica). Al final la función about se invocaría por ejemplo:

    + +
    ask.about"/mininum.root^basic"
    +
    + +

    siendo mininum el nombre del módulo, root el nombre de una de +las funciones dentro del mismo, y basic el tipo de información que +deseamos (luego explico la aparición de "/" al principio del nombre). +Como vemos separamos con un acento circunflejo "^" sobre qué se desea +información de qué tipo de información queremos.

    + +

    En otro ejemplo tendríamos:

    + +
    ask.about"/nombre.tabfun.f1^usage"
    +
    + +

    o sea, le pedimos ayuda de uso para la función f1 dentro de la +tabla tabfun del módulo nombre.

    + +

    Existe otra información importante para el usuario que no hace +falta que la introduzca el diseñador del módulo: la lista de +funciones dentro del módulo. Esa información la obtiene ask +directamente y se presenta en pantalla mediante:

    + +
    ask.about"/mininum^list"
    +
    + +

    También se puede pedir de una vez toda la información que +exista sobre algo mediante:

    + +
    ask.about"/mininum.root^all"
    +
    + +

    Como vemos, el usuario en este formato básico de invocación de +about tiene que teclear bastante. Lo siguiente que hice fue +admitir abreviaturas para el tipo de información pedida:

    + +
      +
    • b por basic, que proporciona información básica;
    • +
    • e por example, que nos muestra un ejemplo;
    • +
    • l por list, que nos da una lista de funciones en el módulo + o en la tabla de funciones sobre la que pedimos ayuda;
    • +
    • m por more, que nos da información complementaria;
    • +
    • s por seealso, que nos cita enlaces a más información interna + o externa,
    • +
    • u por usage, que nos muestra cómo se usa el módulo o la función;
    • +
    • v por version, que nos muestra información sobre la versión, + nombre del autor y otras cuestiones;
    • +
    • n por notes, que nos muestra notas como las de Copyright, etc.
    • +
    • a por all, que proporciona toda la información anterior.
    • +
    + +

    De esta manera se tendría la equivalencia:

    + +
    ask.about"/mininum^list"  --><--  ask.about"/mininum^l"
    +
    + +

    Los espacios tecleados como argumento de ask.about se eliminan antes +de la búsqueda. También se colapsan múltiples "." que pudieran aparecer +en el string de búsqueda. Por ejemplo, tendríamos la equivalencia:

    + +
    ask.about"/ mininum . root ^ usage"  --  ask.about"/mininum.root^u"
    +
    + + +

    Por defecto se supone basic como información si no se proporciona.

    + +

    Usando nil como argumento de ask.about obtenemos información +básica sobre el sistema de ayuda:

    + +
    ask.about()  --><--  ask.about("/ask^basic")
    +
    + +

    son equivalentes.

    + +

    Hay que recordar que ask proporciona la información que el diseñador +del módulo haya incluido en las correspondientes tablas de ayuda (excepto +para list y para all, evidentemente). O sea que la información podría +ser incorrecta si se introducen, por ejemplo, datos sobre la version como +si fuera more.

    + + +

    | base |

    + +

    El lector verá que todavía tenemos +que teclear mucho. Normalmente cuando se trabaja con un módulo +(pongamos Numlua) necesitamos ayuda de manera implícita +del mismo y no de otro. Para eso he incluido la función ask.base. +Esta función permite asumir implícitamente un prefijo de búsqueda +de información. Por ejemplo, poniendo:

    + +
    ask.base"mininum"
    +-->  Changing help basis to "mininum"
    +
    + +

    a partir de ese momento se añade el prefijo "/mininum" a la ruta de +búsqueda (a veces con un punto "." al final si basis se refiere +a un módulo o a una tabla de funciones dentro del módulo y deseamos +información sobre una función concreta), y entonces son equivalentes:

    + +
    ask.about"/mininum^list" --><-- ask.about"^l"
    +
    + +

    A ese prefijo lo he llamado basis (se guarda en una variable +local).

    + +

    Invocando ask.base usando como parámetro un string vacío +"" nos devuelve la basis actual:

    + +
    ask.base""
    +-->   Help basis is "mininum"
    +
    + +

    Por otro lado, me ha parecido conveniente que

    + +
    ask.base(nil)
    +
    + +

    establezca "ask" como basis.

    + +

    El sistema de ayuda en línea ya está operativo, pero me gustaba poco +tener que teclear ask.about cada vez que quería ayuda (aunque según +se lee ask.about"/mininum.root^usage" es casi equivalente a la frase +inglesa ask about mininum.root usage). La solución fue incluir el +trozo de código:

    + +
    __call = function (t, s, ...)
    +   if s == nil then
    +      about(nil)
    +   elseif type(s) == "string" then
    +      about(s)
    +   else
    +      return t[s](...)
    +   end
    +end
    +
    +setmetatable(_G.ask, ask)
    +
    + +

    al final de ask.lua. La última línea nos permite utilizar directamente +ask como variable global después de hacer require"ask". El +trozo previo de código permite que la función about sea invocada +automáticamente cuando a ask se le pasa un string como argumento. +O sea que a partir de este momento son equivalentes:

    + +
    ask.about"/mininum.root^usage"  --  ask"root^u"
    +
    + +

    (con basis = "mininum"). Como podemos observar, la simplificación +es manifiesta, y menos ya no se puede teclear al pedir ayuda (algo +menos si se invoca al módulo ask de la manera: +h = require"ask" pues a partir de ese momento se podría +poner h"root^u"; sin embargo me parece que ask no es mucho +escribir, es más descriptivo, y h es posible que tenga otra +utilidad dentro de nuestro programa).

    + + +

    | doc |

    + +

    Con lo anterior ya tenía el módulo con la funcionalidad deseada. Pero es +una pena tener la información sólo en línea y no poder imprimirla en un +formato decente. Por eso mismo he diseñado la función doc. La primera +idea era adaptar un poco la salida de texto. Pero un poco más de gasto al +escribir la ayuda, usando el formato markdown, permite una salida en +formato html bastante conveniente. Eso fue fácil pues existe un módulo +markdown.lua (que obviamente hay que tener instalado), el cual se +puede invocar desde ask.

    + +

    La versión html es complementaria a la versión en línea de la ayuda. +Permite una visión más global de un módulo dado, y se puede consultar +navegando entre la ayuda de las diferentes funciones. Por tanto, es +bastante interesante.

    + +

    Es en este punto donde se parece el sistema ask y Luadoc, en que +ambos generan formato html. Sin embargo los puntos de partida son +muy diferentes.

    + +

    También se puede obtener en formato impreso, imprimiendo desde el +navegador, pero quizá es mejor usar un programa como html2ps de +Jan Kärrrman, el cual genera un formato de impresión (PS y luego podemos +obtener PDF, también este último con hiperenlaces activos).

    + +

    La función ask.doc, que ha sido rediseñada varias veces, y todavía +no he quedado totalmente conforme con el resultado (aunque funciona +correctamente, sin ser muy elegante), es la que genera html. +En este caso, no se necesita información parcial de funciones dentro de +un módulo. Por tanto, la misma genera un fichero html con toda la +información correspondiente al módulo (para ello basis debe +contener el nombre del módulo y no de una de las funciones del mismo). +Por ejemplo:

    + +
    ask.doc""
    +
    + +

    generaría la ayuda para basis (v.g., "mininum" anteriormente), +en formato html con el nombre "mininum.html", y:

    + +
    ask.doc"ask"
    +
    + +

    generaría la ayuda para ask.

    + +

    El fichero html generado por ask.doc contiene primero información +genérica sobre el módulo (el contenido de _H._basic, _H._usage, +etc.) Luego, la ayuda de todas las funciones (en orden alfabético), +para finalizar con _H._version y _H._notes.

    + +
    + +

    He de pedir disculpas por la programación un poco "sucia" de algunas +partes del módulo. Mis otras ocupaciones no me han permitido más tiempo +de momento, y como decía uno de mis profesores (León Garzón) "lo mejor +es enemigo de lo bueno".

    + + + +

    4 Cómo adaptar módulos para usar la ayuda

    + +

    4.1 Por qué el formato markdown

    + +

    El formato markdown es lo bastante simple como para que +un programador de un módulo lo domine en una hora. No es necesario +para la ayuda en línea (incluso hay algunos que pueden pensar +en que es algo molesto). Yo creo que no introduce mucho +ruido en la ayuda en línea y sin embargo permite una buena +presentación en html (y en PDF tras otras conversiones).

    + +

    Se puede consultar en la dirección indicada, pero también +existe un documento PDF de un par de páginas impresas +con una ayuda simple del sistema Markdown Syntax Cheat Sheet +(la verdad es que hace falta poco más que esta ayuda simple +para dominar markdown).

    + +

    Pensando un poco más en la ayuda en formato html e impreso +se pueden incluso introducir referencias a alguna imagen. Es +evidente que en la ayuda en línea, en modo texto, es imposible +visualizar una imagen, pero se puede dar su referencia en +formato markdown que no es demasiado intrusiva. Sería +de la manera siguiente:

    + +
    ![Texto alternativo](/path/to/image.jpg "Título opcional")
    +
    + +

    (En vez de formato JPG se puede usar también PNG o GIF.)

    + +

    El formato markdown permite también indicar enlaces +a otros documentos (internos o externos) de una manera +simple. En la ayuda en línea no es relevante pero sí en +formato html. Por ejemplo, _seealso puede diseñarse +incluyendo enlaces de la manera:

    + + +
    internos:          [etiqueta](#referencia)
    +
    +externos:          [etiqueta](http://lo.que.sea)
    +
    + + +

    4.2 Preparando la información de ayuda

    + +

    La idea general es sencilla: hacerlo de manera incremental. +La documentación completa puede ser una labor larga y tediosa. +Por tanto, habría que comenzar implementando _basic y luego +_usage, pues son los dos aspectos primarios. Como ese +suele coincidir con lo que se pone como comentario en la cabecera +de una función lo que tenemos que hacer es convertir los comentarios +ya escritos en parte de _H.

    + +

    Posteriormente se pueden ir añadiendo diferentes partes que nos fueron +quedando. Un ejemplo (comentado si se desea) suele ser conveniente para +que el usuario pueda probarlo (incluso copiando y pegando código).

    + +

    El lugar lógico de introducir esa información es delante de la +función correspondiente (como se suele hacer con los comentarios +descriptivos de su funcionalidad) y en el caso de módulos cerca +del principio del fichero.

    + +

    Si se desea preparar la ayuda para un módulo compilado tenemos +que hacer lo siguiente.

    + +

    Si el módulo binario es nuestro, y podemos recompilarlo a nuestro gusto, +podemos cambiar el nombre de la tabla internamente si lo deseamos, +y renombrar también el fichero compilado. Luego se crea un fichero +fuente Lua, con la ayuda incluida, con el nombre antiguo pero extensión +.lua que carga el fichero compilado.

    + +

    Una segunda posibilidad es denominar de manera diferente el módulo +compilado y el que vamos a cargar con require. Sea por ejemplo +un módulo compilado en formato binario de nombre mymodule.so (o +mymodule.dll). Crearemos un fichero Lua con nombre mymodule1.lua +de la forma:

    + +
    -- fichero mymodule1.lua
    +require "mymodule"
    +mymodule._H = { ... }
    +
    + +

    Para cargar mymodule debemos usar:

    + +
    require "mymodule1"
    +
    + +

    y tendremos a nuestra disposición las funciones mymodule.<function> +y también la ayuda mymodule._H, a partir de ese momento.

    + +

    Otra tercera posibilidad es usar el mismo nombre, usando el hecho de +que Lua primero busca ficheros fuente en LUA_PATH y luego binarios en +LUA_CPATH. Crearíamos entonces un fichero mymodule.lua en el mismo +lugar donde está el binario, de la manera:

    + +
    -- fichero mymodule.lua
    +module(..., package.seeall)
    +
    +local p = package.path
    +package.path = ""
    +package.loaded["mymodule"] = nil
    +
    +require"mymodule"
    +package.path = p
    +
    +mymodule._H = [[ayuda]]
    +
    + +

    De esa manera podemos seguir usando

    + +
    require "mymodule"
    +
    + +

    como siempre, pero ahora con ayuda incluida.

    + +

    De todos modos, este último método podría fallar si el módulo +binario mymodule.so (o mymodule.dll) carga a su vez módulos +en código fuente Lua pues durante el momento de su carga está +desactivada la búsqueda en package.path.

    + + +

    4.3 Cómo sabemos que un módulo tiene ayuda

    + +

    Al final de fichero Lua que contiene el módulo se puede incluir +el siguiente trozo de código para detectar si Lua ha sido +lanzado de manera interactiva:

    + +
    -- checks if Lua calling was interactive;
    +-- it does not work in all cases, but it does in the normal ones
    +local interactive = true
    +if _G.arg then
    +   for _, v in pairs(_G.arg) do
    +     interactive = false
    +     if v == "-i" then
    +       interactive = true
    +       break
    +     end
    +   end
    +end
    +
    + +

    Este sistema de detección no siempre funciona bien (y eso permite generar +la documentación html con una simple línea dentro del intérprete de +comando del sistema operativo, como veremos en el siguiente apartado).

    + +

    Sería interesante que en modo interactivo Lua crease una variable que +lo indicara o al menos usara _PROMPT para ello: si no se expresa +explicitamente _PROMPT está sin definir y Lua usa ">" para ese +cometido directamente. Bastaría sólo con definir _PROMPT en modo +interactivo (">" si el usuario no definió otro) y dejarlo sin definir +en otro caso.

    + +

    Una vez detectada la interactividad del intérprete tendríamos +el siguiente bloque de código:

    + +
    if interactive then
    +   -- reusing interactive
    +   interactive = pcall(require, "ask")                          --**
    +   if interactive then
    +      io.stderr:write('Module "mininum" loaded. ')
    +      io.stderr:write('To obtain help invoke ask"mininum".\n')  --**
    +      io.stderr:write('Documentation occupies memory. ')
    +      io.stderr:write('For freeing it let execute:\n')
    +      io.stderr:write('\n    mininum._H = nil\n\n')
    +
    +      ask.base"mininum"
    +   else
    +      io.stderr:write('Module "mininum" loaded. It has help\n')
    +      io.stderr:write('but module "ask" is not accesible.\n')   --**
    +      io.stderr:write('Help removed.\n')
    +   end
    +end
    +
    +if not interactive then
    +   -- deleting _H
    +   _H = nil
    +   _G.ask = nil
    +   collectgarbage()
    +end
    +
    + +

    Esto libera memoria no necesaria en modo no interactivo, y además +imprime en pantalla algunos mensajes en modo interactivo.

    + +

    Si deseamos cambiar el nombre de ask.lua a otro +nombre deberemos cambiar también las líneas indicadas por --** +más arriba de manera acorde.

    + +

    En otro orden de cosas, en el futuro es posible la inclusión de una +utilidad externa para adaptar el formato que acompaña a Luadoc +(y también el que acompaña a LuaHelp) para su uso con el módulo +ask. Esto permite no partir de vacío para módulos cuya documentación +ya estaba preparada de algún modo.

    + + +

    5 Cómo usar la ayuda interactivamente

    + +

    Como antes hemos expuesto, un módulo que lleve ayuda deberá +indicarlo al cargarse con require para que el usuario sepa +que tiene disponible la ayuda.

    + +

    El último módulo cargado que posea ayuda cambiará basis por el nombre +del módulo. Esto deberá tenerlo presente el usuario. De todos modos, +en cualquier momento éste podrá cambiar basis a su gusto o podrá +de manera puntual usar el sistema con la ruta completa (que empieza +con "/").

    + +

    Si se desea eliminar la ayuda de la memoria es fácil. Supongamos +que tenemos el módulo mininum cargado con

    + +
    require "mininum"
    +
    + +

    pues entonces basta con teclear:

    + +
    mininum._H = nil   -- repetir esto con cada módulo
    +collectgarbage()
    +
    + +

    Luego, si queremos eliminar completamente el sistema de ayuda +teclearemos:

    + +
    _G.ask = nil
    +collectgarbage()
    +
    + +

    A partir de este momento ya no ocupa memoria ni la ayuda concreta +del módulo mininum (aunque evidentemente sus funciones siguen +cargadas), ni el sistema ask.

    + + + +

    6 Cómo generar documentación en formato html (y PDF)

    + +

    En este tipo de documentos es importante indicar el +conjunto de caracteres usado en la ayuda. En estos momentos +yo estoy trabajando con iso-8859-15 y por tanto, en la +ayuda se incluye el campo:

    + +
    _H._CHARSET = "iso-8859-15"  -- "utf-8" en otros casos
    +
    + +

    Si se está usando utf-8 (más corriente estos días) se debe cambiar +de manera acorde. Si no se hace así algunos caracteres como "ü", "ñ", +"á", saldrán incorrectamente. Se suele presentar la documentación en +inglés por lo que el presente tema típicamente sólo afectaría a nombres +de personas con esos caracteres.

    + +

    Una manera muy sencilla de obtener la documentación en formato +html sería la siguiente en modo interactivo:

    + +
    $ lua
    +> require "mininum"
    +> ask.doc""
    +
    + +

    Esto generaría mininum.html. Sin embargo, debido a un pequeño problema +en la detección de la interactividad (véase el siguiente apartado), +esta misma documentación también se puede lograr mediante:

    + +
    lua -e "require'mininum'; ask.doc''"
    +
    + +

    Una vez se tiene el fichero con formato html se puede visualizar +con un navegador cualquiera para consultar la ayuda. La hoja de estilo +default.css proporcionada sitúa en la parte izquierda un menú fijo +con los enlaces de todo el documento, incluyendo la lista de funciones +dentro del módulo.

    + +

    El estilo del fichero html de salida es modificable, a través de la +hoja de estilo default.css (nombre incluido dentro de ask.lua). +Se recomienda sólo hacerlo en caso de conocimientos de CSS, pero no +hace daño jugar con él.

    + +

    Si existen muchas funciones en el módulo es posible que no se vea el +final de la lista dentro de la pantalla. En ese caso se debe disminuir el +tamaño de letra (o visualizar el documento sin estilo, que aunque es más +feo, es todavía operativo). En ocasiones también tiene que disminuirse +el tamaño de letra para ver los ejemplos (las líneas no dan vuelta en +los elementos html de tipo <pre>, a los que se convierten +los ejemplos).

    + +

    Aparte de visualizar el documento html también se puede imprimir, ya sea +con el navegador (no recomendable) o con un programa específico. Aquí se +recomienda la utilización de html2ps, que convierte html en PS, +con una hoja de estilo un poco simplificada (véase la documentación +proporcionada en el enlace). En este caso, el menú de navegación no +se imprime.

    + +

    Posteriormente, si se desea, se puede convertir el fichero PS en +PDF mediante el programa ps2pdf que acompaña a Ghostscript. Tiene +este sistema la particularidad de que los enlaces se mantienen, pudiendo +también navegarse con enlaceds en el fichero PDF.

    + + +

    7 Debilidades y posibles mejoras

    + +

    En primer lugar, debido a que modificamos algunas tablas +(internas en el módulo del que se quiere ayuda) es posible +que haya algunos efectos colaterales (que yo todavía no he +descubierto).

    + +

    Por otro lado, cuando se carga un módulo en una variable local +de la manera, por ejemplo:

    + +
    local m = require"mininum"
    +
    + +

    para usar "m." como prefijo para las funciones de mininum y escribir +menos, a partir de ese momento se puede acceder a la ayuda con mininum +y con m, lo que no importa mucho en modo interactivo (al igual que se +puede acceder a las funciones con m.fun y con mininum.fun).

    + +

    Si embargo, ask.doc"m" genera un fichero mininum.html con el nombre +del módulo mininum (y no el fichero m.html).

    + +

    Las funciones en el módulo que vayan a tener ayuda no deben empezar por +"_" pues ask supone que son parte de su sistema (no es que no se +puedan definir y usar, sino que no se puede proporcionar ayuda).

    + +

    Algunas veces la interactividad no funciona bien. Por ejemplo:

    + +
    lua -e "require'mininum'" somefile.lua
    +
    + +

    imprime un mensaje al principio y además no borra la ayuda. O sea +que cuando se ejecuta somefile.lua las tablas de ayuda están en +memoria (lo que puede ser contraproducente).

    + +

    Sin embargo no importa demasiado pues basta incluir

    + +
    require"mininum"
    +
    + +

    al principio de somefile.lua e invocar a lua sin la opción -e, +y entonces se descarga el módulo ask y la ayuda de mininum (una vez +que detecta que el modo no es interactivo) antes de ejecutar el resto +del programa.

    + +

    En modo interactivo, en el futuro se puede hacer que haya enlaces http +para ayuda más completa (por ejemplo para una descripción de un algoritmo +o para dar un enlace a un artículo científico), o para comprobar si hay +nuevas versiones de un módulo dado. Cuando apareciera uno de esos enlaces +se podría lanzar un navegador cuando el usuario pidiera ayuda de ese +tipo (habría que incluir un nuevo tipo, v.g., link equivalente en el +modo de una letra a k).

    + +

    También se pueden añadir test ejecutables (o sea pruebas en cada función +del módulo; en parte podrían ser los ejemplos ejecutados). Por ejemplo, +se incluirían dos campos: _test con código y _testsolution con +texto. Al ejecutar el primero de ellos se debería obtener el segundo +(el módulo ask comprobaría que son iguales). No sé si esto es o no +interesante. Habría que estudiarlo más.

    + +

    El sistema según se ha presentado no está cerrado ni es la solución +definitiva. Puede tener muchas mejoras y así lo desea su autor, +que sea un acicate para que otros programadores piensen sobre el tema +y desarrollen incluso mejores sistemas (o completen éste).

    + + +

    8 Conclusiones

    + +

    Se ha diseñado un interesante sistema para proporcionar ayuda +en otros módulos. El mismo es versátil, permitiendo ayuda +en línea, en formato html para su consulta en un navegador +y en formato PS o PDF para impresión.

    + +

    El sistema no es perfecto, pero sí operativo. El autor cree +que puede ser la semilla de un sistema más completo. +El lector avezado en Lua puede hacer las modificaciones y +adaptaciones que crea convenientes para su uso personal. +Realmente, AskLua se distribuye como dominio público. +Está esperando mejoras.

    + + + + + + + \ No newline at end of file diff --git a/doc/asklua_spanish.pdf b/doc/asklua_spanish.pdf new file mode 100644 index 0000000..5c99c44 Binary files /dev/null and b/doc/asklua_spanish.pdf differ diff --git a/doc/default.css b/doc/default.css new file mode 100644 index 0000000..a66e48e --- /dev/null +++ b/doc/default.css @@ -0,0 +1,127 @@ + diff --git a/example/mininum.html b/example/mininum.html new file mode 100644 index 0000000..7dcfbde --- /dev/null +++ b/example/mininum.html @@ -0,0 +1,438 @@ + + + + + Module mininum + + + +
    + +

    Module mininum

    + + +

    Basic information

    + + + +

    mininum is a minimal numerical library (with only three +functions), developed for accompanying the ask helping system. The +purpose is only to serve as an example.

    + +

    mininum displays error messages in stderr.

    + +

    Note: this library is very simple, and it is not intended +for heavy calculation (but it is usable).

    + +

    Call ask"<function>" for information on <function>.

    + + + + +

    List of functions

    + + +

    +derivative +quadrature +root +

    + + + + + +

    Description of functions

    + + +

    mininum.derivative

    + + +

    Basic information

    + + + +

    Calculates the first derivative of a function

    + + + + +

    Usage of function

    + + + +

    mininum.derivative(f, x, aerr)

    + +

    @params:

    + +
      +
    1. f: a function of a real variable.

    2. +
    3. x: number, is the abscissa at which f'(x) is calculated.

    4. +
    5. aerr: number (optional), is the intended absolute error of the solution + (1.0e-6 as default). The minimum value of aerr is 1.0e-15.

    6. +
    + +

    @returns: number, the first derivative of function f at x.

    + + + + +

    More specific information

    + + + +

    Function mininum.derivative uses the central difference formula:

    + +

    f'(x0) ~ [f(x0+h)-f(x0-h)]/(2 h)

    + +

    with successive decreasing values of h: h/2, h/4, h/8, etc., +and applying the Richardson extrapolation method to improve +the convergence.

    + + + + +

    See also

    + + + +

    Press et al. (1992), +Numerical Recipes in Fortran, p. 180, CUP.

    + + + + +

    Examples

    + + + +
    require"mininum"
    +
    +local eval = 0
    +local function g (x)
    +   eval = eval+1
    +   return sin(x)
    +end
    +
    +local aerr = 1.e-6
    +local d1 = mininum.derivative(g, 2, aerr)
    +local e1 = cos(2)
    +
    +print("calculated solution = ", d1)    -- -0.41614683654713
    +print("actual solution     = ", e1)    -- -0.41614683654714
    +print("intended error      = ", aerr)  -- 1e-06
    +print("actual absol. error = ", d1-e1) -- 9.9364960703952e-15
    +print("function evaluations= ", eval)  -- 10
    +
    + + + +

    mininum.quadrature

    + + +

    Basic information

    + + + +

    Calculates the definite integral of a function

    + + + + +

    Usage of function

    + + + +

    mininum.quadrature(f, a, b, rerr)

    + +

    @params:

    + +
      +
    1. f: a function of a real variable.

    2. +
    3. a: number, the lower limit in the integral.

    4. +
    5. b: number, the upper limit in the integral.

    6. +
    7. rerr: number (optional), the relative intended error in the solution + (if not given 1.0e-6 is assumed). The minimum value of rerr is 1.0e-15.

    8. +
    + +

    @returns: number, the definite integral of f between abscissas a and b.

    + + + + +

    More specific information

    + + + +

    Function mininum.quadrature uses the midpoint formula:

    + +

    I ~ h*[sum f(xi)] with i = 1/2, 3/2, ...

    + +

    being n the number of intervals, h = (b-a)/n, and xi = a+h*i.

    + +

    This formula works even for quadratures when the function at +one or both limits is infinite but the integral exists.

    + +

    The method is iterative, multiplying n by 3 at each step, +until the relative error is achieved or a maximum of 14 iterations +are reached (1594323 function evaluations). +At least 9 ordinates are calculated.

    + +

    At each iteration an Aitken-delta^2 process is performed. +This normally accelerates very much the convergence.

    + + + + +

    See also

    + + + +

    Press et al. (1992), +Numerical Recipes in Fortran, p. 129 and p. 160, CUP.

    + +

    For the Aitken acceleration see the +Wikipedia.

    + + + + +

    Examples

    + + + +
    require"mininum"
    +
    +local eval
    +local function s (x)
    +   eval = eval+1
    +   -- a difficult case for Romberg quadrature
    +   return 1/sqrt(x)
    +end
    +
    +local rerr = 1.0e-5
    +local eval = 0
    +local q = mininum.quadrature(s, 0, 0.5, rerr)
    +local e = 2*sqrt(0.5)
    +
    +print("calculated solution = ", q)       -- 1.414213736863
    +print("actual solution     = ", e)       -- 1.4142135623731
    +print("intended error      = ", rerr)    -- 1e-05
    +print("actual relat. error = ", (q-e)/e) -- 1.2338296867489e-07
    +print("function evaluations= ", eval)    -- 243
    +
    + + + +

    mininum.root

    + + +

    Basic information

    + + + +

    Determines a root of a function between two abscissas

    + + + + +

    Usage of function

    + + + +

    mininum.root(f, a, b, errx)

    + +

    @params:

    + +
      +
    1. f: function of a real variable, of which we want the root, + x such that f(x) = 0.

    2. +
    3. a: number.

    4. +
    5. b: number. The root is searched between the abscissas a and b.

    6. +
    7. errx: number (optional). The function returns a value when + the difference between two successive approximations of the + root is less than errx (in absolute value). + If no value is provided for errx, then 1.0e-6 is assumed. + The minimum value of errx is 1.0e-15.

    8. +
    + +

    @returns: number, an estimation of the root.

    + + + + +

    More specific information

    + + + +

    function mininum.root uses regula falsi method.

    + +

    The function must be continuous, and the provided +abscissas a and b must accomplish f(a)*f(b) < 0. +In this case the method always converge towards a solution.

    + +

    It is an iterative method, with (somewhat) superlinear convergence. +As much 30 iterations are done.

    + +

    Here the "Illinois" version of the method is used.

    + + + + +

    See also

    + + + +

    Wikipedia.

    + + + + +

    Examples

    + + + +
    require"mininum"
    +
    +local eval = 0
    +local function f (x)
    +   eval = eval+1
    +   return x*(3+x*(-4+2*x)) -- difficult for classical regula falsi
    +end
    +
    +local errx = 1.0e-8
    +local x = mininum.root(f, -1, 1, errx)
    +
    +print("calculated solution = ", x)     -- 3.9008079929199e-19
    +print("actual solution     = ", 0)     -- 0
    +print("intended error      = ", errx)  -- 1e-08
    +print("actual error        = ", x)     -- 3.9008079929199e-19
    +print("function evaluations= ", eval)  -- 14
    +
    + + + + +

    Version

    + + + +

    by Julio M. Fernández-Díaz, Dept. of Physics, +University of Oviedo, Spain, Version 0.1, February 2010

    + +

    julio a t uniovi d o t es

    + + + + +

    Notes

    + + + +

    THIS CODE IS HEREBY PLACED IN PUBLIC DOMAIN.

    + +

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE.

    + + + +
    + + diff --git a/example/mininum.lua b/example/mininum.lua new file mode 100644 index 0000000..53ba6c7 --- /dev/null +++ b/example/mininum.lua @@ -0,0 +1,357 @@ +--[[ + A simple numerical library + (only for describing the use of help module "ask") + + Author: Julio Manuel Fernandez-Diaz + Date: Feb 10, 2010 + (For Lua 5.1) + + Error messages are displayed in stderr +--]] + +-- PACKAGE: mininum + +-- change this line if help system has other name +local HELPNAME = "ask" + +module(..., package.seeall) + +-- help information + +_H = {_Name = ..., +_CHARSET = "iso-8859-15", -- modify this for other cases, v.g., "utf-8" +_basic = [[`mininum` is a **mini**mal **num**erical library (with only three +functions), developed for accompanying the `]]..HELPNAME..[[` helping system. The +purpose is only to serve as an example. + +`mininum` displays error messages in stderr. + +Note: this library is very simple, and it is not intended +for heavy calculation (but it is usable). + +Call `]]..HELPNAME..[[""` for information on ``.]], +_version = [[by Julio M. Fernández-Díaz, Dept. of Physics, +University of Oviedo, Spain, Version 0.1, February 2010 + +julio a t uniovi d o t es]], +_notes = [[THIS CODE IS HEREBY PLACED IN PUBLIC DOMAIN. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE.]] +} + +-- some private functions and data + +local max, abs, floor = math.max, math.abs, math.floor + +_H.root = { +_basic = [[Determines a root of a function between two abscissas]], +_usage = [[`mininum.root(f, a, b, errx)` + +@params: + +1. `f`: function of a real variable, of which we want the root, + _x_ such that _f(x) = 0_. + +2. `a`: number. + +3. `b`: number. The root is searched between the abscissas `a` and `b`. + +4. `errx`: number (optional). The function returns a value when + the difference between two successive approximations of the + root is less than `errx` (in absolute value). + If no value is provided for `errx`, then 1.0e-6 is assumed. + The minimum value of `errx` is 1.0e-15. + +@returns: number, an estimation of the root.]], +_more = [[function `mininum.root` uses **regula falsi** method. + +The function must be continuous, and the provided +abscissas _a_ and _b_ must accomplish _f(a)*f(b) < 0_. +In this case the method always converge towards a solution. + +It is an iterative method, with (somewhat) superlinear convergence. +As much 30 iterations are done. + +Here the "Illinois" version of the method is used.]], +_seealso = [[ [Wikipedia](http://en.wikipedia.org/wiki/False_position_method).]], +_example = [[ require"mininum" + + local eval = 0 + local function f (x) + eval = eval+1 + return x*(3+x*(-4+2*x)) -- difficult for classical regula falsi + end + + local errx = 1.0e-8 + local x = mininum.root(f, -1, 1, errx) + + print("calculated solution = ", x) -- 3.9008079929199e-19 + print("actual solution = ", 0) -- 0 + print("intended error = ", errx) -- 1e-08 + print("actual error = ", x) -- 3.9008079929199e-19 + print("function evaluations= ", eval) -- 14]], +} + +function root (f, a, b, errx) + assert(type(f) == "function", "f must be a function") + assert(type(a) == "number" and type(b) == "number", "a and b must be numbers") + errx = errx or 1.0e-6 + assert(type(errx) == "number", "errx must be a number") + assert(errx >= 1.0e-15, "errx must be greater than 1.0e-15") + + local xa, fa = a, f(a) + if fa == 0 then return a end + local xb, fb = b, f(b) + if fb == 0 then return b end + assert(fa*fb < 0, "a and b choice does not ensure root finding; try others") + + local side = 0 + if fb < 0 then + xa, xb = xb, xa + fa, fb = fb, fa + end + + local del, x + for j = 1, 30 do + x = xa+(xb-xa)*fa/(fa-fb) + local fx = f(x) + if fx == 0 then return x end + if fx < 0 then + del, xa, fa = abs(xa-x), x, fx + if side == -1 then fb = fb/2 end + side = -1 + else + del, xb, fb = abs(xb-x), x, fx + if side == 1 then fa = fa/2 end + side = 1 + end + if del < errx then return x end + end + io.stderr:write("root: warning, maximum number of iterations reached\n") + io.stderr:write("actual absolute error: "..del.."\n") + return x +end + +_H.derivative = { +_basic = [[Calculates the first derivative of a function]], +_usage = [[`mininum.derivative(f, x, aerr)` + +@params: + +1. `f`: a function of a real variable. + +2. `x`: number, is the abscissa at which _f'(x)_ is calculated. + +3. `aerr`: number (optional), is the intended absolute error of the solution + (1.0e-6 as default). The minimum value of `aerr` is 1.0e-15. + +@returns: number, the first derivative of function _f_ at _x_.]], +_more = [[Function `mininum.derivative` uses the central difference formula: + +_f'(x0) ~ [f(x0+h)-f(x0-h)]/(2 h)_ + +with successive decreasing values of _h: h/2, h/4, h/8_, etc., +and applying the Richardson extrapolation method to improve +the convergence.]], +_seealso = [[Press et al. (1992), +Numerical Recipes in Fortran, p. 180, CUP.]], +_example = [[ require"mininum" + + local eval = 0 + local function g (x) + eval = eval+1 + return sin(x) + end + + local aerr = 1.e-6 + local d1 = mininum.derivative(g, 2, aerr) + local e1 = cos(2) + + print("calculated solution = ", d1) -- -0.41614683654713 + print("actual solution = ", e1) -- -0.41614683654714 + print("intended error = ", aerr) -- 1e-06 + print("actual absol. error = ", d1-e1) -- 9.9364960703952e-15 + print("function evaluations= ", eval) -- 10]] +} + +function derivative (f, x, aerr) + assert(type(f) == "function", "f must be a function") + assert(type(x) == "number", "x must be a number") + + aerr = aerr or 1.0e-6 + assert(type(aerr) == "number", "err must be a number") + assert(aerr >= 1.0e-15, "err must be greater than 1.0e-15") + + local aberr = math.huge + local h, j, n = 1, 1, 15 + local d = {} + d[1] = {} + d[1][1]=(f(x+h)-f(x-h))/(2*h) + + while aberr > aerr and j < n do + h = h/2 + d[j+1] = {} + d[j+1][1] = (f(x+h)-f(x-h))/(2*h) + local fac = 1 + for k = 1, j do + fac = 4*fac + d[j+1][k+1] = d[j+1][k]+(d[j+1][k]-d[j][k])/(fac-1) + end + aberr = abs(d[j+1][j+1]-d[j][j]) + j = j+1 + end + if j == n then + io.stderr:write("derivative: warning, maximum number of iterations reached\n") + io.stderr:write("estimated absolute error: "..aberr.."\n") + end + return d[j][j] +end + +_H.quadrature = { +_basic = [[Calculates the definite integral of a function]], +_usage = [[`mininum.quadrature(f, a, b, rerr)` + +@params: + +1. `f`: a function of a real variable. + +2. `a`: number, the lower limit in the integral. + +3. `b`: number, the upper limit in the integral. + +4. `rerr`: number (optional), the relative intended error in the solution + (if not given 1.0e-6 is assumed). The minimum value of `rerr` is 1.0e-15. + +@returns: number, the definite integral of `f` between abscissas `a` and `b`.]], +_more = [[Function `mininum.quadrature` uses the **midpoint formula**: + +_I ~ h*[sum f(xi)] with i = 1/2, 3/2, ..._ + +being _n_ the number of intervals, _h = (b-a)/n_, and _xi = a+h*i_. + +This formula works even for quadratures when the function at +one or both limits is infinite but the integral exists. + +The method is iterative, multiplying _n_ by 3 at each step, +until the relative error is achieved or a maximum of 14 iterations +are reached (1594323 function evaluations). +At least 9 ordinates are calculated. + +At each iteration an _Aitken-delta^2_ process is performed. +This normally accelerates very much the convergence.]], +_seealso = [[Press et al. (1992), +Numerical Recipes in Fortran, p. 129 and p. 160, CUP. + +For the Aitken acceleration see the +[Wikipedia](http://en.wikipedia.org/wiki/Aitken's_delta-squared_process).]], +_example = [[ require"mininum" + + local eval + local function s (x) + eval = eval+1 + -- a difficult case for Romberg quadrature + return 1/sqrt(x) + end + + local rerr = 1.0e-5 + local eval = 0 + local q = mininum.quadrature(s, 0, 0.5, rerr) + local e = 2*sqrt(0.5) + + print("calculated solution = ", q) -- 1.414213736863 + print("actual solution = ", e) -- 1.4142135623731 + print("intended error = ", rerr) -- 1e-05 + print("actual relat. error = ", (q-e)/e) -- 1.2338296867489e-07 + print("function evaluations= ", eval) -- 243]] +} + +function quadrature (f, a, b, rerr) + assert(type(f) == "function", "f must be a function") + assert(type(a) == "number" and type(b) == "number", "a and b must be numbers") + rerr = rerr or 1.0e-6 + assert(type(rerr) == "number", "err must be a number") + assert(rerr >= 1.0e-15, "err must be greater than 1.0e-15") + + local h = b-a + local sum = f((a+b)/2) -- partial sums + local qo, qn, qp, qe, qeo, qep, den = sum*h + local it, n, nt = 1, 2, 14 + + -- because a final extrapolation is done we reduce the intended + -- error; this does not always work but the final relative error + -- often has the same magnitud order of rerr + local err1 = 100*rerr + + while n <= nt do + h = h/3 + for j = 1, it do + local x = a+(3*j-2.5)*h + sum = sum+f(x)+f(x+2*h) + end + qn = sum*h + if qp then -- Aitken-delta^2 process + den = qn-2*qo+qp + qe = den == 0 and qn or qn-(qn-qo)^2/den + if qeo and abs(qe-qeo) < err1*abs(qe) then break end + end + qo, qp, qeo, qep = qn, qo, qe, qeo + it = 3*it + n = n+1 + end + + if n > nt then + io.stderr:write("quadrature: warning, maximum number of iterations reached\n") + io.stderr:write("estimated absolute error: "..abs((qe-qeo)/qe).."\n") + end + + -- a final extrapolation over extrapolation if possible + if qep then + den = qe-2*qeo+qep + qe = den == 0 and qe or qe-(qe-qeo)^2/den + end + return qe +end + +-- checks if Lua calling was interactive; +-- it does not work for all cases, but it does +-- in the normal ones +local inter = true +if _G.arg then + for _, v in pairs(_G.arg) do + inter = false + if v == "-i" then + inter = true + break + end + end +end + +if inter then + inter = pcall(require, HELPNAME) + if inter then + io.stderr:write('Module "'.._H._Name..'" loaded.\n') + io.stderr:write('To obtain help invoke '..HELPNAME..'"'.._H._Name..'".\n') + io.stderr:write('Documentation occupies memory. For freeing it let execute:\n') + io.stderr:write('\n '.._H._Name..'._H = nil\n\n') + + _G[HELPNAME].base(_H._Name) + else + io.stderr:write('Module "mininum" loaded.\n') + io.stderr:write('It has help but module "'..HELPNAME..'" is not accesible.\n') + io.stderr:write('Help removed.\n') + end +end + +if not inter then + -- deleting _H + _H = nil + _G[HELPNAME] = nil + collectgarbage() +end + diff --git a/example/mininum.pdf b/example/mininum.pdf new file mode 100644 index 0000000..686bf35 Binary files /dev/null and b/example/mininum.pdf differ diff --git a/example/mininum_test.lua b/example/mininum_test.lua new file mode 100644 index 0000000..deaf48e --- /dev/null +++ b/example/mininum_test.lua @@ -0,0 +1,58 @@ +require"mininum" + +local sin, cos, log, sqrt = math.sin, math.cos, math.log, math.sqrt +local eval + +print("\nroot test\n") +local function f (x) + eval = eval+1 + return x*(3+x*(-4+2*x))-- difficult for classical regula falsi +end + +local errx = 1.0e-8 +eval = 0 +local x = mininum.root(f, -1, 1, errx) + +print("calculated solution = ", x) +print("actual solution = ", 0) +print("intended error = ", errx) +print("actual absol. error = ", x) +print("function evaluations= ", eval) + +-------------------------------------------------- +print("\nderivative test\n") +local function g (x) + eval = eval+1 + return sin(x) +end + +eval = 0 +local err = 1.0e-6 +local d1 = mininum.derivative(g, 2, err) +local e1 = cos(2) + +print("first derivative") +print("calculated solution = ", d1) +print("actual solution = ", e1) +print("intended error = ", err) +print("actual absol. error = ", d1-e1) +print("function evaluations= ", eval) + +-------------------------------------------------- +print("\nquadrature test\n") +local function s (x) + eval = eval+1 + -- a difficult case for Romberg quadrature + return 1/sqrt(x) +end + +local rerr = 1.0e-5 +eval = 0 +local q = mininum.quadrature(s, 0, 0.5, rerr) +local e = 2*sqrt(0.5) + +print("calculated solution = ", q) +print("actual solution = ", e) +print("intended error = ", rerr) +print("actual relat. error = ", (q-e)/e) +print("function evaluations= ", eval) diff --git a/license.txt b/license.txt new file mode 100644 index 0000000..430927e --- /dev/null +++ b/license.txt @@ -0,0 +1,15 @@ +=========================================================================== + +THIS CODE IS HEREBY PLACED IN PUBLIC DOMAIN. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +=========================================================================== + diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..5084966 --- /dev/null +++ b/readme.txt @@ -0,0 +1,93 @@ +--------------------------------------------------------------- +Module: AskLua + +Purpose: to add interactive help to other modules + +Author: Julio Manuel Fernandez-Diaz + Profesor Titular, Department of Physics + University of Oviedo (Spain) + +Date: February 2010 + +Version: 0.1 + +License: Public domain +--------------------------------------------------------------- + + +Introduction +------------ + +AskLua implements a help integrated system for on line use in the +interactive interpreter, and for generating documentation in "html" +and printed formats. + +AskLua provides a unique module, "ask", which is little intrusive and, +although it occupies some memory, it can be deleted by the user at any +time if he/she does not want to continue with the help on line. + +The system is fairly integrated, in such a way that it is possible to +easily add help for an existing module, even of binary type. + + +Files provided +-------------- + +readme.txt -- this file +license.txt -- license information + +doc/asklua.pdf -- descriptive document (in English) about `asklua` +doc/asklua_spanish.pdf -- documento descriptivo (en español) sobre `asklua` + +ask.lua -- the module +doc/ask.html -- "html" file created by + lua -e "require'ask'; ask.doc''" +doc/ask.pdf -- the same converted to PDF + +doc/default.css -- style sheet used in the "html" generation + +example/mininum.lua -- a numerical sample module to accompany "ask" +example/mininum.html -- "html" file created by + lua -e "require'mininum'; ask.doc''" +example/mininum.pdf -- the same converted to PDF + +example/mininum_test.lua -- lua file for testing "mininum" + + +Installing +---------- + +* From tar.gz and zip formats: + unpack it and move "ask.lua" to a convenient path. + +* From luarocks: + luarocks install asklua + + +Using it +-------- + +$ lua +> require "ask" + +From this point we have help for the module "ask". + +If other module with help, v.g., "mininum" that accompanies "ask", +is loaded: + +> require "mininum" + +From this point we have help for "mininum", v.g.: + +> ask"^l" + +list the functions in "mininum", and: + +> ask"root^u" + +shows the usage help for function "root" in "mininum". + +To generate all the documentation about "mininum" in "html" format: + +$ lua -e "require'mininum'; ask.doc''" +