diff options
| -rw-r--r-- | TODO | 4 | ||||
| -rw-r--r-- | luatexbase-mods.dtx | 507 | 
2 files changed, 511 insertions, 0 deletions
@@ -0,0 +1,4 @@ +- change lua module names +- check for name conflicts ('module' used in mods) +- test + diff --git a/luatexbase-mods.dtx b/luatexbase-mods.dtx new file mode 100644 index 0000000..54f4d73 --- /dev/null +++ b/luatexbase-mods.dtx @@ -0,0 +1,507 @@ +% \iffalse meta-comment +% +% Template : look for DATE DESC VERSION !!! +% and 'derived files' !!! +% +% Written in 2009, 2010 by Manuel Pégourié-Gonnard and Élie Roux. +%     <mpg@elzevir.fr> +%     <elie.roux@telecom-bretagne.eu> +% +% This work is under the CC0 license. +% +% This work consists of the main source file luatexbase-mods.dtx +% and the derived files +%    luatexbase-mods.sty mods.lua ... +% +% Unpacking: +%    tex luatexbase-mods.dtx +% Documentation: +%    pdflatex luatexbase-mods.dtx +% +%    The class ltxdoc loads the configuration file ltxdoc.cfg +%    if available. Here you can specify further options, e.g. +%    use A4 as paper format: +%       \PassOptionsToClass{a4paper}{article} +% +%<*ignore> +\begingroup +  \def\x{LaTeX2e}% +\expandafter\endgroup +\ifcase 0\ifx\install y1\fi\expandafter +         \ifx\csname processbatchFile\endcsname\relax\else1\fi +         \ifx\fmtname\x\else 1\fi\relax +\else\csname fi\endcsname +%</ignore> +%<*install> +\input docstrip.tex + +\keepsilent +\askforoverwritefalse + +\let\MetaPrefix\relax + +\preamble +This is a generated file. + +Written in 2009, 2010 by Manuel Pégourié-Gonnard and Élie Roux. +    <mpg@elzevir.fr> +    <elie.roux@telecom-bretagne.eu> + +This work is under the CC0 license. + +This work consists of the main source file luatexbase-mods.dtx +and the derived files +   luatexbase-mods.sty mods.lua ... + +\endpreamble + +\let\MetaPrefix\DoubleperCent + +\generate{% +  \usedir{tex/luatex/luatexbase}% +  \file{luatexbase-mods.sty}{\from{luatexbase-mods.dtx}{texpackage}}% +} + +\def\MetaPrefix{-- } + +\def\luapostamble{% +  \MetaPrefix^^J% +  \MetaPrefix\space End of File `\outFileName'.% +} + +\def\currentpostamble{\luapostamble}% + +\generate{% +  \usedir{tex/luatex/luatexbase}% +  \file{mods.lua}{\from{luatexbase-mods.dtx}{luamodule}}% +} + +\obeyspaces +\Msg{************************************************************************} +\Msg{*} +\Msg{* To finish the installation you have to move the following} +\Msg{* files into a directory searched by TeX:} +\Msg{*} +\Msg{*     luatexbase-mods.sty mods.lua ...} +\Msg{*} +\Msg{* Happy TeXing!} +\Msg{*} +\Msg{************************************************************************} + +\endbatchfile +%</install> +%<*ignore> +\fi +%</ignore> +%<*driver> +\NeedsTeXFormat{LaTeX2e} +\ProvidesFile{luatexbase-mods.drv} +  [DATE DESC] +\documentclass{ltxdoc} +\EnableCrossrefs +\CodelineIndex +\begin{document} +  \DocInput{luatexbase-mods.dtx}% +\end{document} +%</driver> +% \fi +% +% \CheckSum{} +% +% \CharacterTable +%  {Upper-case    \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +%   Lower-case    \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +%   Digits        \0\1\2\3\4\5\6\7\8\9 +%   Exclamation   \!     Double quote  \"     Hash (number) \# +%   Dollar        \$     Percent       \%     Ampersand     \& +%   Acute accent  \'     Left paren    \(     Right paren   \) +%   Asterisk      \*     Plus          \+     Comma         \, +%   Minus         \-     Point         \.     Solidus       \/ +%   Colon         \:     Semicolon     \;     Less than     \< +%   Equals        \=     Greater than  \>     Question mark \? +%   Commercial at \@     Left bracket  \[     Backslash     \\ +%   Right bracket \]     Circumflex    \^     Underscore    \_ +%   Grave accent  \`     Left brace    \{     Vertical bar  \| +%   Right brace   \}     Tilde         \~} +% +% \GetFileInfo{luatexbase-mods.drv} +% +% \title{The \textsf{luatexbase-mods} package} +% \date{DATE} +% \author{% +%  Manuel P\'egouri\'e-Gonnard \\ \texttt{mpg@elzevir.fr} \and +%   \'Elie Roux \\ \texttt{elie.roux@telecom-bretagne.eu}} +% +% \maketitle +% +% \begin{abstract} +% \end{abstract} +% +% \section{Documentation} +% +% Lua has some embedded module management, with the functions \texttt{module} +% and \texttt{require}. With this package we try get more control on the +% module system, by implementing something close to the \LaTeX 's +% \texttt{\string\usepackage} and \texttt{\string\RequirePackage} macros: the +% \texttt{\string\luatexUseModule} and \texttt{\string\luatexRequireModule} +% that act like them, but for lua files. The functions \texttt{module} and +% \texttt{require} should not be used, in profit of the lua functions +% \texttt{luatextra.provides\_module} and \texttt{luatextra.use\_module} or +% \texttt{luatextra.require\_module}. +% +%    \section{Implementation} +% +%    \subsection{\TeX\ package} +% +%    \begin{macrocode} +%<*texpackage> +%    \end{macrocode} +% +%    \subsubsection{Module handling} +% +%    The \texttt{\string\luatexModuleError} macro is called by the lua function +%    \texttt{luatextra.module\_error}. It is necessary because we can't call +%    directly \texttt{\string\errmessage} in lua. +% +%    \begin{macrocode} + +\def\luatexModuleError#1#2{% +  \errorcontextlines=0\relax +  \immediate\write16{}% +  \errmessage{Module #1 error: #2^^J^^J% +See the module #1 documentation for explanation.^^J ...^^J}% +} + +%    \end{macrocode} +% +%    Then we define +%    \texttt{\string\luatexUseModule} that simply calls +%    \texttt{luatextra.use\_module}. +%    If the package is loaded with Plain, we define +%    \texttt{\string\luaRequireModule} with two mandatory arguments. +% +%    \begin{macrocode} +\def\luatexUseModule#1{\luadirect{luatextra.use_module([[#1]])}} + +\expandafter\ifx\csname ProvidesPackage\endcsname\relax +  \def\luatexRequireModule#1#2{\luadirect{% +      luatextra.require_module([[#1]], [[#2]])}} +\else +%    \end{macrocode} +% +%    If the package is loaded with \LaTeX , we define  +%    \texttt{\string\luaRequireModule} with one mandatory +%    argument (the name of the package) and one optional (the version or the +%    date). +% +%    \begin{macrocode} +  \newcommand\luatexRequireModule[2][0]{\luadirect{luatextra.require_module([[#2]], [[#1]])}} +\fi +%    \end{macrocode} +% +%    \begin{macrocode} +%</texpackage> +%    \end{macrocode} +% +%    \subsection{Lua module} +% +%    \begin{macrocode} +%<*luamodule> +%    \end{macrocode} +% +%    Initialisation borrowed from luatextra +% +%    \begin{macrocode} +module("luatextra", package.seeall) + +local format = string.format + +luatextra.modules = luatextra.modules or {} +%    \end{macrocode} +% +%    Here we define the warning and error functions specific to +%    \texttt{luatextra}. +% +%    \begin{macrocode} + +luatextra.internal_warning_spaces = "                   " + +function luatextra.internal_warning(msg) +    if not msg then return end +    texio.write_nl(format("\nLuaTeXtra Warning: %s\n\n", msg)) +end + +luatextra.internal_error_spaces = "                 " + +function luatextra.internal_error(msg) +    if not msg then return end +    tex.sprint(format("\\immediate\\write16{}\\errmessage{LuaTeXtra error: %s^^J^^J}", msg)) +end + +%    \end{macrocode} +% +%    \subsubsection{Error, warning and info function for modules} +% +%    Some module printing functions are provided, they have the same +%    philosophy as the \LaTeX 's \texttt{\string\PackageError} and +%    \texttt{\string\PackageWarning} macros: their first argument is the name +%    of the module, and the second is the message. These functions are meant +%    to be used by lua module writers. +% +%    \begin{macrocode} + +function luatextra.module_error(package, msg, helpmsg) +    if not package or not msg then +        return +    end +    if helpmsg then +        tex.sprint(format("\\errhelp{%s}", helpmsg)) +    end +    tex.sprint(format("\\luatexModuleError{%s}{%s}", package, msg)) +end + +function luatextra.module_warning(modulename, msg) +    if not modulename or not msg then +        return +    end +    texio.write_nl(format("\nModule %s Warning: %s\n\n", modulename, msg)) +end + +function luatextra.module_log(modulename, msg) +    if not modulename or not msg then +        return +    end +    texio.write_nl('log', format("%s: %s", modulename, msg)) +end + +function luatextra.module_term(modulename, msg) +    if not modulename or not msg then +        return +    end +    texio.write_nl('term', format("%s: %s", modulename, msg)) +end + +function luatextra.module_info(modulename, msg) +    if not modulename or not msg then +        return +    end +    texio.write_nl(format("%s: %s\n", modulename, msg)) +end + +%    \end{macrocode} +% +%    \subsubsection{module loading and providing functions} +% +%    A small function to find a lua module file according to its name, with or +%    without the \texttt{.lua} at the end of the filename. +% +%    \begin{macrocode} + +function luatextra.find_module_file(name) +    if string.sub(name, -4) ~= '.lua' then +        name = name..'.lua' +    end +    path = kpse.find_file(name, 'tex') +    if not path then +      path = kpse.find_file(name, 'texmfscripts') +    end +    return path, name +end + +%    \end{macrocode} +% +%    A small patch, for the \texttt{module} function to work in this file. I +%    can't understand why it doens't otherwise. +% +%    \begin{macrocode} + +luatextra.module = module + +%    \end{macrocode} +% +%    \begin{macro}{luatextra.use module} +% +%    This macro is the one used to simply load a lua module file. It does not +%    reload it if it's already loaded, and prints the filename in the terminal +%    and the log. A lua module must call the macro +%    \texttt{luatextra.provides\_module}. +% +%    \begin{macrocode} + + +function luatextra.use_module(name) +    if not name or luatextra.modules[name] then +        return +    end +    local path, filename = luatextra.find_module_file(name) +    if not path then +        luatextra.internal_error(format("unable to find lua module %s", name)) +    else +        if path:sub(1,2) == "./" then +            path = path:sub(3) +        end +        texio.write_nl('('..path) +        dofile(path) +        if not luatextra.modules[name] then +            luatextra.internal_warning(format("You have requested module `%s',\n%s but the file %s does not provide it.", name, luatextra.internal_warning_spaces, filename)) +        end +        if not package.loaded[name] then +            luatextra.module(name, package.seeall) +        end +        texio.write(')') +    end +end + +%    \end{macrocode} +% +%    \end{macro} +% +%    Some internal functions to convert a date into a number, and to determine +%    if a string is a date. It is useful for +%    \texttt{luatextra.require\_package} to understand if a user asks a +%    version with a date or a version number. +% +%    \begin{macrocode} + +function luatextra.datetonumber(date) +    numbers = string.gsub(date, "(%d+)/(%d+)/(%d+)", "%1%2%3") +    return tonumber(numbers) +end + +function luatextra.isdate(date) +    for _, _ in string.gmatch(date, "%d+/%d+/%d+") do +        return true +    end +    return false +end + +local date, number = 1, 2 + +function luatextra.versiontonumber(version) +    if luatextra.isdate(version) then +        return {type = date, version = luatextra.datetonumber(version), orig = version} +    else +        return {type = number, version = tonumber(version), orig = version} +    end +end + +luatextra.requiredversions = {} + +%    \end{macrocode} +% +%    \begin{macro}{luatextra.require module} +% +%    This function is like the \texttt{luatextra.use\_module} function, but +%    can accept a second argument that checks for the version of the module. +%    The version can be a number or a date (format yyyy/mm/dd). +% +%    \begin{macrocode} + +function luatextra.require_module(name, version) +    if not name then +        return +    end +    if not version then +        return luatextra.use_module(name) +    end +    luaversion = luatextra.versiontonumber(version) +    if luatextra.modules[name] then +        if luaversion.type == date then +            if luatextra.datetonumber(luatextra.modules[name].date) < luaversion.version then +                luatextra.internal_error(format("found module `%s' loaded in version %s, but version %s was required", name, luatextra.modules[name].date, version)) +            end +        else +            if luatextra.modules[name].version < luaversion.version then +                luatextra.internal_error(format("found module `%s' loaded in version %.02f, but version %s was required", name, luatextra.modules[name].version, version)) +            end +        end +    else +        luatextra.requiredversions[name] = luaversion +        luatextra.use_module(name) +    end +end + +%    \end{macrocode} +% +%    \end{macro} +% +%    \begin{macro}{luatextra.provides module} +% +%    This macro is the one that must be called in the module files. It takes a +%    table as argument. You can put any information you want in this table, +%    but the mandatory ones are \texttt{name} (a string), \texttt{version} (a +%    number), \texttt{date} (a string) and \texttt{description} (a string). +%    Other fields are usually \texttt{copyright}, \texttt{author} and +%    \texttt{license}. +% +%    This function logs informations about the module the same way \LaTeX\ +%    does for informations about packages. +% +%    \begin{macrocode} + +function luatextra.provides_module(mod) +    if not mod then +        luatextra.internal_error('cannot provide nil module') +        return +    end +    if not mod.version or not mod.name or not mod.date or not mod.description then +        luatextra.internal_error('invalid module registered, fields name, version, date and description are mandatory') +        return +    end +    requiredversion = luatextra.requiredversions[mod.name] +    if requiredversion then +        if requiredversion.type == date and requiredversion.version > luatextra.datetonumber(mod.date) then +            luatextra.internal_error(format("loading module %s in version %s, but version %s was required", mod.name, mod.date, requiredversion.orig)) +        elseif requiredversion.type == number and requiredversion.version > mod.version then +            luatextra.internal_error(format("loading module %s in version %.02f, but version %s was required", mod.name, mod.version, requiredversion.orig)) +        end +    end +    luatextra.modules[mod.name] = module +    texio.write_nl('log', format("Lua module: %s %s v%.02f %s\n", mod.name, mod.date, mod.version, mod.description)) +end + +%    \end{macrocode} +% +%    \end{macro} +% +%    Here we load the \textsf{luaextra} module, that contains a bunch of very +%    useful functions. See the documentation of \textsf{luaextra} for more +%    details. +% +%    \begin{macrocode} + +luatextra.use_module('luaextra') + +%    \end{macrocode} +% +%    \texttt{luatextra.kpse\_module\_loader} finds a module with the +%    \texttt{kpse} library. This function is then registered in the table of +%    the functions used by the lua function \texttt{require} to look for +%    modules. +% +%    \begin{macrocode} + +function luatextra.kpse_module_loader(mod) +  local file = luatextra.find_module_file(mod) +  if file then +    local loader, error = loadfile(file) +    if loader then +      texio.write_nl("(" .. file .. ")") +      return loader +    end +    return "\n\t[luatextra.kpse_module_loader] Loading error:\n\t" +           .. error +  end +  return "\n\t[luatextra.kpse_module_loader] Search failed" +end + +table.insert(package.loaders, luatextra.kpse_module_loader) + +%    \end{macrocode} +% +%    \begin{macrocode} +%</luamodule> +%    \end{macrocode} +% +% \Finale +\endinput  | 
