diff options
Diffstat (limited to 'Master/texmf-dist/source/luatex/luatexbase/luatexbase-modutils.dtx')
-rw-r--r-- | Master/texmf-dist/source/luatex/luatexbase/luatexbase-modutils.dtx | 469 |
1 files changed, 274 insertions, 195 deletions
diff --git a/Master/texmf-dist/source/luatex/luatexbase/luatexbase-modutils.dtx b/Master/texmf-dist/source/luatex/luatexbase/luatexbase-modutils.dtx index dd5b118f9aa..81394be2f93 100644 --- a/Master/texmf-dist/source/luatex/luatexbase/luatexbase-modutils.dtx +++ b/Master/texmf-dist/source/luatex/luatexbase/luatexbase-modutils.dtx @@ -115,11 +115,7 @@ See source file '\inFileName' for details. % Grave accent \` Left brace \{ Vertical bar \| % Right brace \} Tilde \~} % -% \title{The \pk{luatexbase-modutils} package} -% \date{v0.2 2010-05-12} -% \author{% -% Manuel P\'egouri\'e-Gonnard \\ \email{mpg@elzevir.fr} \and -% \'Elie Roux \\ \email{elie.roux@telecom-bretagne.eu}} +% \pkdate{luatexbase-modutils}{v0.2 2010-05-12} % % \maketitle % @@ -129,15 +125,16 @@ See source file '\inFileName' for details. % \cs{documentclass} and \cs{RequirePackage} or \cs{ProvidesClass} and % \cs{ProvidesFiles}} or more precisely the part of these macros that deals % with identification and version checking (no attempt is done at implementing -% an option mechanism). Functions for error reporting are provided too. -% -% It also loads \pk{luatexbase-loader}. +% an option mechanism). It also provides functions for reporting errors and +% warnings in a standardised format. % \end{abstract} % % \tableofcontents % % \section{Documentation} % +% \subsection{Scope of this package} +% % Lua's standard function |require()| is similar to \tex's |\input| primitive % but is somehow more evolved in that it makes a few checks to avoid loading % the same module twice. In the \tex world, this needs to be taken care of by @@ -147,57 +144,148 @@ See source file '\inFileName' for details. % implements a complex option system, and does some identification and version % checking. The present package doesn't try to provide anything for options, % but implements a system for identification and version checking similar to -% \latex's system. Both \tex macros and Lua functions are provided. +% \latex's system. % -% This package also provides Lua functions for reporting errors, warnings, -% etc. +% \medskip % % It is important to notice that Lua's standard function |module()| is -% completely unrelated to the present package. It has nothing to do with -% identification and deals only with namespaces.\footnote{More precisely, it -% modifies the current environment.} So, you should continue to -% use it normally, unlike the |require()| function which can be replaced with -% this package's |luatexbase.require_module()|. +% completely orthogonal with the present package. It has nothing to do with +% identification and deals only with namespaces: more precisely, it +% modifies the current environment. So, you should continue to +% use it normally regardless of whether you chose to use this package's +% features for identification. +% +% It is recommended to always use |module()| or any other method that ensure +% the global name space remains clean. For example, you may heavily use the +% |local| keyword and explicitly qualify the name of every non-local symbol. +% Chapter 15 of \href{http://www.lua.org/pil/}{Programming in Lua, 1st ed.} +% discusses various methods for managing packages. % % \subsection{\tex macros} % -% The two macros |\luatexUseModule| and |\luatexRequireModule| are very -% similar and are interfaces to the Lua functions |use_module| and -% |require_module|. The only difference between those macros is the number of -% arguments (just as the underlying Lua functions): |\luatexUseModule| only -% take one argument: the module name\footnote{without extension} while -% |\luatexRequireModule| takes another argument for specifying a minimal -% version (see below). With \latex, this argument is the first and is -% optional. Otherwise, it's the second one and it's mandatory. +% \begin{qcode} +% \cs{RequireLuaModule}\marg{name}\oarg{date} +% \end{qcode} +% +% The macro |\RequireLuaModule| is an interface to the Lua function +% |require_module|; it take the same arguments with the same meaning. The +% second argument is optional. % % \subsection{Lua functions} % -% The main functions are |luatexbase.require_module| and -% |luatexbase.use_module| which may be used as a replacement to |require()|. -% The only difference between these functions is, |require_module| accepts a -% second, optional argument in order to specify a minimal version. They do the -% same as |require()| but also make sure the module loaded correctly -% identifies itself with the name given, and its version is greater than the -% minimal version required. The version can be given either as a (floating -% point) number or as a date in YYYY/MM/DD format. -% -% Modules identify themselves using |luatexbase.provides_module|, whose only -% argument is a table with some information about the module. The mandatory -% fields are |name| (a string), |version| (a number), |date| (a string) and -% |description| (a string). Other fields are optional and ignored, and usually -% include |copyright|, |author| and |license|. -% -% \bigskip -% -% Functions for reporting are provided; similarly to \latex's |\PackageError| -% etc. they take the module name as their first argument and include it in the -% printed message in an appropriate way. The remaining arguments are passed to -% |string.format()| before being printed. -% -% The functions provided (all found in the |luatexbase| table) are -% |module_error|, |module_warning|, |module_info| (writes to terminal and -% log), |module_log| (writes only to the log file) and |module_term| (writes -% only to the terminal). +% \begin{qcode} +% luatexbase.require_module({\meta{name}} [, \meta{required date}]) +% \end{qcode} +% +% The function |luatexbase.require_module()| may be used as a replacement to +% |require()|. If only one argument is given, the only difference with +% |require()| is it checks that the module properly identifies itself (as +% explained below) with the same name. +% +% The second argument is optional; if used, it must be a +% string\footnote{Previous versions of the package supported floating-point +% version numbers as well, but it caused confusion with authors trying to use +% version strings such as |0.3a| and probably isn't worth the trouble.} +% containing a date in |YYYY//MM/DD| format which specifies the minimum +% version of the module required. +% +% \begin{qcode} +% luatexbase.provides_module(\meta{info}) +% \end{qcode} +% This function is used by modules to identify themselves; the argument is a +% table containing information about the module. The required field |name| +% must contain the name of the module. It is recommended to provide a field +% |date| with the same format as above. Optional fields |version| (number or +% string) and |description| may be used if present. Other fields are ignored. +% +% If a date was required, then a warning is issued if the required date is +% strictly newer than the declared date (or if no date was declared). A list +% of loaded modules and their associated information is kept, and used to +% check the date without reloading the module (since |require()| won't reload +% it anyway) if a module is required several times. +% +% \begin{qcode} +% luatexbase.module_error(\meta{name}, \meta{message}, ...) +% luatexbase.module_warning(\meta{name}, \meta{message}, ...) +% luatexbase.module_info(\meta{name}, \meta{message}, ...) +% luatexbase.module_log(\meta{name}, \meta{message}, ...) +% \end{qcode} +% These functions are similar to \latex's |\PackageError|, |\PackageWarning| +% and |\PackageInfo| in the way they format the output. No automatic line +% breaking is done, you may still use |\n| as usual for that, and the name of +% the package will be prepended to each output line (except for |log| which is +% intended for short messages in a non-verbose format). The first argument +% is the name of the current module; the remaining arguments are passed to +% |string.format()|. +% +% Note that |module_error| raises an actual Lua error with |error()|, which +% currently means a call stack will be dumped. While this may not look pretty, +% at least it provides useful information for tracking the error down. +% +% \begin{qcode} +% local err, warn, info, log = luatexbase.errwarinf(\meta{name}) +% local err, warn, info, log = luatexbase.provides_module(\meta{name}) +% \end{qcode} +% Customised versions of the above commands maybe obtained by invoking +% |errwarinf()| and are also returned by |provides_module()|. They don't take +% the name of the module as their first argument any more, so that you don't +% need to repeat it all over the place. (Notice that |error| is the name of a +% standard Lua function, so you may want to avoid overwriting it, hence the +% use of |err| in the above example.) +% +% \subsection{Templates} +% +% Let me emphasize again that, while |luatexbase.require_module()| is meant to +% be used as a replacement for |require()|, the function +% |luatexbase.provides_module()| \emph{is not} a replacement for |module()|: +% they just don't do the same thing (declaring information vs changing the +% current name space). +% +% Now, here is how you module may begin: +% \begin{verbatim} +% local err, warn, info, log = luatexbase.provides_module({ +% -- required +% name = 'mymodule', +% -- recommended +% date = '1970/01/01', +% version = 0.0, -- or version = '0.0a', +% description = 'a Lua module template', +% -- optional and ignored +% author = 'A. U. Thor', +% licence = 'LPPL v1.3+', +% }) +% +% module('mynamespace', package.seeall) +% -- or any other method (see chapter 15 of PIL for examples) +% \end{verbatim} +% +% Alternatively, if you don't want to assume \pk{luatexbase-modutils} is +% loaded, you may load your module with: +% \begin{verbatim} +% (luatexbase.require_module or require)('mymodule') +% \end{verbatim} +% and begin your module's code with: +% \begin{verbatim} +% if luatexbase._provides_module then +% luatexbase.provides_module({ +% -- required +% name = 'mymodule', +% -- recommended +% date = '1970/01/01', +% version = 0.0, -- or version = '0.0a', +% description = 'a Lua module template', +% -- optional and ignored +% author = 'A. U. Thor', +% licence = 'LPPL v1.3+', +% }) +% end +% +% module('mynamespace', package.seeall) +% -- or any other method (see chapter 15 of PIL for examples) +% +% local function err(msg) +% -- etc. +% \end{verbatim} % % \section{Implementation} % @@ -263,7 +351,7 @@ See source file '\inFileName' for details. \let\x\ProvidesPackage \fi \expandafter\endgroup -\x{luatexbase-modutils}[2010/05/27 v0.2a Module utilities for LuaTeX] +\x{luatexbase-modutils}[2010/10/10 v0.3 Module utilities for LuaTeX] % \end{macrocode} % % Make sure \luatex is used. @@ -277,60 +365,94 @@ See source file '\inFileName' for details. \fi \ifluatex\else \begingroup - \expandafter\ifx\csname PackageWarningNoLine\endcsname\relax - \def\x#1#2{\begingroup\newlinechar10 - \immediate\write16{Package #1 warning: #2}\endgroup} + \expandafter\ifx\csname PackageError\endcsname\relax + \def\x#1#2#3{\begingroup \newlinechar10 + \errhelp{#3}\errmessage{Package #1 error: #2}\endgroup} \else - \let\x\PackageWarningNoLine + \let\x\PackageError \fi \expandafter\endgroup - \x{luatexbase-modutils}{LuaTeX is required for this package. Aborting.} + \x{luatexbase-attr}{LuaTeX is required for this package. Aborting.}{% + This package can only be used with the LuaTeX engine^^J% + (command `lualatex' or `luatex').^^J% + Package loading has been stopped to prevent additional errors.} \lltxb@modutils@AtEnd \expandafter\endinput \fi % \end{macrocode} % -% Load \pk{luatexbase-loader} (hence \pk{luatexbase-compat}) and require -% supporting Lua module. +% Load \pk{luatexbase-loader} (hence \pk{luatexbase-compat}), require the +% supporting Lua module and make sure |luaescapestring| is available. % % \begin{macrocode} -\begingroup\expandafter\expandafter\expandafter\endgroup -\expandafter\ifx\csname RequirePackage\endcsname\relax - \input luatexbase-loader.sty -\else +\ifdefined\RequirePackage \RequirePackage{luatexbase-loader} +\else + \input luatexbase-loader.sty \fi \luatexbase@directlua{require('luatexbase.modutils')} -% \end{macrocode} -% -% Make sure the primitives we need are available. -% -% \begin{macrocode} \luatexbase@ensure@primitive{luaescapestring} % \end{macrocode} % -% \subsubsection{User macros} +% \subsection{Auxiliary definitions} % -% Interface to |use_module()|. +% We need a version of |\@ifnextchar|. The definitions for the not-\latex +% case are stolen from \pk{ltxcmds} verbatim, only the prefix is changed. % % \begin{macrocode} -\def\luatexUseModule#1{\luatexbase@directlua{% - luatexbase.use_module("\luatexluaescapestring{#1}")}} +\ifdefined\kernel@ifnextchar + \let\lltxb@ifnextchar\kernel@ifnextchar +\else + \chardef\lltxb@zero0 + \chardef\lltxb@two2 + \long\def\lltxb@ifnextchar#1#2#3{% + \begingroup + \let\lltxb@CharToken= #1\relax + \toks\lltxb@zero{#2}% + \toks\lltxb@two{#3}% + \futurelet\lltxb@LetToken\lltxb@ifnextchar@ + } + \def\lltxb@ifnextchar@{% + \ifx\lltxb@LetToken\lltxb@CharToken + \expandafter\endgroup\the\toks\expandafter\lltxb@zero + \else + \ifx\lltxb@LetToken\lltxb@SpaceToken + \expandafter\expandafter\expandafter\lltxb@@ifnextchar + \else + \expandafter\endgroup\the\toks + \expandafter\expandafter\expandafter\lltxb@two + \fi + \fi + } + \begingroup + \def\x#1{\endgroup + \def\lltxb@@ifnextchar#1{% + \futurelet\lltxb@LetToken\lltxb@ifnextchar@ + }% + }% + \x{ } + \begingroup + \def\x#1{\endgroup + \let\lltxb@SpaceToken= #1% + }% + \x{ } +\fi % \end{macrocode} % -% Interface to |require_module()| with syntax depending on the format. +% \subsubsection{User macro} +% +% Interface to the Lua function for module loading. Avoid passing a second +% argument to the function if empty (most probably not specified). % % \begin{macrocode} -\begingroup\expandafter\expandafter\expandafter\endgroup -\expandafter\ifx\csname newcommand\endcsname\relax - \def\luatexRequireModule#1#2{% - \luatexbase@directlua{luatexbase.require_module( - "\luatexluaescapestring{#1}", "\luatexluaescapestring{#2}")}} -\else - \newcommand\luatexRequireModule[2][0]{% - \luatexbase@directlua{luatexbase.require_module( - "\luatexluaescapestring{#2}", "\luatexluaescapestring{#1}")}} -\fi +\def\RequireLuaModule#1{% + \lltxb@ifnextchar[{\lltxb@requirelua{#1}}{\lltxb@requirelua{#1}[]}} +\def\lltxb@requirelua#1[#2]{% + \luatexbase@directlua{luatexbase.require_module( + "\luatexluaescapestring{#1}" + \expandafter\ifx\expandafter\/\detokenize{#2}\/\else + , "\luatexluaescapestring{#2}" + \fi)}} % \end{macrocode} % % \begin{macrocode} @@ -348,75 +470,75 @@ module("luatexbase", package.seeall) % \subsection{Internal functions and data} % % Tables holding informations about the modules loaded and the versions -% required. +% required. Keys are module names and values are the info tables as passed +% to |provides_module()|. % % \begin{macrocode} local modules = modules or {} -local requiredversions = {} % \end{macrocode} % -% Convert a date in YYYY/MM/DD format into a number +% Convert a date in YYYY/MM/DD format into a number. % % \begin{macrocode} -local function datetonumber(date) +local function date_to_int(date) numbers = string.gsub(date, "(%d+)/(%d+)/(%d+)", "%1%2%3") return tonumber(numbers) end % \end{macrocode} % -% Say if a string is a date in YYYY//MM/DD format. -% -% \begin{macrocode} -local function isdate(date) - for _, _ in string.gmatch(date, "%d+/%d+/%d+") do - return true - end - return false -end -% \end{macrocode} -% -% Parse a version into a table indicating a type (date or number), a -% numeric version and the original version string. -% -% \begin{macrocode} -local date, number = 1, 2 -local function parse_version(version) - if isdate(version) then - return {type = date, version = datetonumber(version), orig = version} - else - return {type = number, version = tonumber(version), orig = version} - end -end -% \end{macrocode} -% % \subsubsection{Error, warning and info function for modules} % -% Here are the reporting functions for the modules. For errors, Lua's -% |error()| is used. For now, the error reports look less good than with -% \tex's |\errmessage|, but hopefully it will be improved in future -% versions of \luatex. We could invoke |\errmessage| using |tex.sprint()|, -% but it may cause problems on the \tex end, and moreover |error()| will -% still be used by Lua for other errors, so it makes messages more -% consistent. +% Here are the reporting functions for the modules. An internal function is +% used for error messages, so that the calling level (last argument of +% |error()| remains constant using either |module_error()| or a custom +% version as returned by |errwarinf()|. % % \begin{macrocode} +local function msg_format(msg_type, mod_name, ...) + local cont = '('..mod_name..')' .. ('Module: '..msg_type):gsub('.', ' ') + return 'Module '..mod_name..' '..msg_type..': ' + .. string.format(...):gsub('\n', '\n'..cont) .. '\n' +end local function module_error_int(mod, ...) - error('Module '..mod..' error: '..string.format(...), 3) + error(msg_format('error', mod, ...), 3) end function module_error(mod, ...) module_error_int(mod, ...) end +% \end{macrocode} +% +% Split the lines explicitly in order not to depend on the value of +% |\newlinechar|. +% +% \begin{macrocode} function module_warning(mod, ...) - texio.write_nl("Module "..mod.." warning: "..string.format(...)) + for _, line in ipairs(msg_format('warning', mod, ...):explode('\n')) do + texio.write_nl(line) + end end function module_info(mod, ...) - texio.write_nl(mod..": "..string.format(...)) + for _, line in ipairs(msg_format('info', mod, ...):explode('\n')) do + texio.write_nl(line) + end end -function module_log(mod, ...) - texio.write_nl('log', mod..": "..string.format(...)) +% \end{macrocode} +% +% No line splitting or advanced formating here. +% +% \begin{macrocode} +function module_log(mod, msg, ...) + texio.write_nl('log', mod..': '..msg:format(...)) end -function module_term(mod, ...) - texio.write_nl('term', mod..": "..string.format(...)) +% \end{macrocode} +% +% Produce custom versions of the reporting functions. +% +% \begin{macrocode} +function errwarinf(name) + return function(...) module_error_int(name, ...) end, + function(...) module_warning(name, ...) end, + function(...) module_info(name, ...) end, + function(...) module_log(name, ...) end end % \end{macrocode} % @@ -424,83 +546,42 @@ end % present module. % % \begin{macrocode} -local function err(...) module_error_int('luatexbase.modutils', ...) end -local function warn(...) module_warning('luatexbase.modutils', ...) end +local err, warn = errwarinf('luatexbase.modutils') % \end{macrocode} % % \subsubsection{module loading and providing functions} % -% Load a module without version checking. +% Load a module with mandatory name checking and optional version checking. % % \begin{macrocode} -function use_module(name) +function require_module(name, req_date) require(name) - if not modules[name] then - warn("Module didn't properly identified itself: %s", name) - end -end -% \end{macrocode} -% -% Load a module with optional version checking. -% -% \begin{macrocode} -function require_module(name, version) - if not version then - return use_module(name) - end - luaversion = parse_version(version) - if modules[name] then - if luaversion.type == date then - if datetonumber(modules[name].date) < luaversion.version then - err("found module `%s' loaded in version %s, " - .."but version %s was required", - name, modules[name].date, version) - end - else - if modules[name].version < luaversion.version then - err("found module `%s' loaded in version %.02f, " - .."but version %s was required", - name, modules[name].version, version) - end + local info = modules[name] + if not info then + warn("module '%s' was not properly identified", name) + elseif version then + if not (info.date and date_to_int(info.date) > date_to_int(req_date)) + then + warn("module '%s' required in version '%s'\n" + .. "but found in version '%s'", name, req_date, info.date) end - else - requiredversions[name] = luaversion - use_module(name) end end % \end{macrocode} % -% Provide identification information for a module. +% Provide identification information for a module. As a bonus, custom +% reporting functions are returned. No need to do any check here, +% everything done in |require_module()|. % % \begin{macrocode} -function provides_module(mod) - if not mod then - err('cannot provide nil module') - return - end - if not mod.version or not mod.name or not mod.date - or not mod.description then - err("invalid module registered: " - .."fields name, version, date and description are mandatory") - return - end - requiredversion = requiredversions[mod.name] - if requiredversion then - if requiredversion.type == date - and requiredversion.version > datetonumber(mod.date) then - err("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 - err("loading module %s in version %.02f, " - .."but version %s was required", - mod.name, mod.version, requiredversion.orig) - end +function provides_module(info) + if not (info and info.name) then + err('provides_module: missing information') end - modules[mod.name] = mod - texio.write_nl('log', string.format("Lua module: %s %s v%.02f %s\n", - mod.name, mod.date, mod.version, mod.description)) + texio.write_nl('log', string.format("Lua module: %s %s %s %s\n", + info.name, info.date or '', info.version or '', info.description or '')) + modules[info.name] = info + return errwarinf(info.name) end % \end{macrocode} % @@ -514,28 +595,26 @@ end % % \begin{macrocode} %<*testdummy> -luatexbase.provides_module { +local err, warn, info, log = luatexbase.provides_module { name = 'test-modutils', date = '2000/01/01', version = 1, description = 'dummy test package', } +info('It works!\nOh, rly?\nYeah rly!') +log("I'm a one-line info.") %</testdummy> % \end{macrocode} % % We just check that the package loads properly, under both LaTeX and Plain -% TeX. Anyway, the test files of other modules using this one already are a -% test\dots +% TeX, is able to load and identify the above dummy module. % % \begin{macrocode} %<testplain>\input luatexbase-modutils.sty %<testlatex>\RequirePackage{luatexbase-modutils} %<*testplain,testlatex> -\luatexUseModule{test-modutils} -\luatexRequireModule -%<testlatex>[1970/01/01] -{test-modutils} -%<testplain>{1970/01/01} +\RequireLuaModule{test-modutils} +\RequireLuaModule{test-modutils}[1970/01/01] %</testplain,testlatex> %<testplain>\bye %<testlatex>\stop |