diff options
| author | Manuel Pégourié-Gonnard <mpg@elzevir.fr> | 2010-03-29 16:57:52 +0200 | 
|---|---|---|
| committer | Manuel Pégourié-Gonnard <mpg@elzevir.fr> | 2010-03-29 16:57:52 +0200 | 
| commit | f78bfeb6a9d2c5e7ce7e7cf40af76cf3ad517a54 (patch) | |
| tree | a335a60370580a3d06e5de16783005b6035dcea9 /luatexbase-mcb.dtx | |
| parent | 6029a1f236941a9976801b33b95fbd7633cf326a (diff) | |
| download | luatexbase-f78bfeb6a9d2c5e7ce7e7cf40af76cf3ad517a54.tar.gz | |
Rename luamcallbacks.
Diffstat (limited to 'luatexbase-mcb.dtx')
| -rw-r--r-- | luatexbase-mcb.dtx | 800 | 
1 files changed, 800 insertions, 0 deletions
| diff --git a/luatexbase-mcb.dtx b/luatexbase-mcb.dtx new file mode 100644 index 0000000..cec69e9 --- /dev/null +++ b/luatexbase-mcb.dtx @@ -0,0 +1,800 @@ +% \iffalse meta-comment +% +% Copyright (C) 2009 by Elie Roux <elie.roux@telecom-bretagne.eu> +% +% This work is under the CC0 license. +% +% This work consists of the main source file luatexbase-mcb.dtx +% and the derived files +%    luatexbase-mcb.sty, mcb.lua, luatexbase-mcb.pdf, +%    test-mcb-plain.tex test-mcb-latex.tex +% +% Unpacking: +%    tex luatexbase-mcb.dtx +% Documentation: +%    pdflatex luatexbase-mcb.dtx +% +%<*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 + +Copyright (C) 2009 by Elie Roux <elie.roux@telecom-bretagne.eu> + +This work is under the CC0 license. +See source file '\inFileName' for details. + +\endpreamble + +\let\MetaPrefix\DoubleperCent + +\generate{% +  \usedir{doc/luatex/luatexbase}% +  \file{test-mcb-plain.tex}{\from{luatexbase-mcb.dtx}{testplain}}% +  \file{test-mcb-latex.tex}{\from{luatexbase-mcb.dtx}{testlatex}}% +} + +\def\MetaPrefix{-- } + +\def\luapostamble{% +  \MetaPrefix^^J% +  \MetaPrefix\space End of File `\outFileName'.% +} + +\def\currentpostamble{\luapostamble}% + +\generate{% +  \usedir{tex/luatex/luatexbase}% +  \file{mcb.lua}{\from{luatexbase-mcb.dtx}{lua}}% +} + +\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-mcb.sty mcb.lua} +\Msg{*} +\Msg{* Happy TeXing!} +\Msg{*} +\Msg{************************************************************************} + +\endbatchfile +%</install> +%<*ignore> +\fi +%</ignore> +%<*driver> +\documentclass{ltxdoc} +\input{lltxb-dtxstyle} +\begin{document} +  \DocInput{luatexbase-mcb.dtx}% +\end{document} +%</driver> +% \fi +% +% \CheckSum{0} +% +% \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         \~} +% +% \title{The \textsf{luatexbase-mcb} package} +% \date{2009/09/18 v0.93} +% \author{Elie Roux \\ \texttt{elie.roux@telecom-bretagne.eu}} +% +% \maketitle +% +% \begin{abstract} +% This package manages the callback adding and removing, by adding +% \texttt{callback.add} and \texttt{callback.remove}, and overwriting +% \texttt{callback.register}. It also allows to create and call new callbacks. +% For an introduction on this package (among others), please refer to the +% document \texttt{luatextra-reference.pdf}. +% \par\textbf{Warning.} Currently assumes that \textsf{luatexbase-modutils} +% has been previously loaded. (This is a temporary limitation.) +% \end{abstract} +% +% \section{Documentation} +% +% Lua\TeX\ provides an extremely interesting feature, named callbacks. It +% allows to call some lua functions at some points of the \TeX\ algorithm (a +% \emph{callback}), like when \TeX\ breaks likes, puts vertical spaces, etc. +% The Lua\TeX\ core offers a function called \texttt{callback.register} that +% enables to register a function in a callback. +% +% The problem with \texttt{callback.register} is that is registers only one +% function in a callback. For a lot of callbacks it can be common to have +% several packages registering their function in a callback, and thus it is +% impossible with them to be compatible with each other. +% +% This package solves this problem by adding mainly one new function +% \texttt{callback.\\add} that adds a function in a callback. With this +% function it is possible for packages to register their function in a +% callback without overwriting the functions of the other packages. +% +% The functions are called in a certain order, and when a package registers a +% callback it can assign a priority to its function. Conflicts can still +% remain even with the priority mechanism, for example in the case where two +% packages want to have the highest priority. In these cases the packages have +% to solve the conflicts themselves. +% +% This package also privides a way to create and call new callbacks, in +% addition to the default Lua\TeX\ callbacks. +% +% This package contains only a \texttt{.lua} file, that can be called by +% another lua script. +% +%\subsubsection*{Limitations} +% +% This package only works for callbacks where it's safe to add multiple +% functions without changing the functions' signatures. There are callbacks, +% though, where registering several functions is not possible without changing +% the function's signatures, like for example the readers callbacks. These +% callbacks take a filename and give the datas in it. One solution would be to +% change the functions' signature to open it when the function is the first, +% and to take the datas and modify them eventually if they are called after +% the first. But it seems rather fragile and useless, so it's not implemented. +% With these callbacks, in this package we simply execute the first function +% in the list. +% +% Other callbacks in this case are \texttt{define\_font} and +% \texttt{open\_read\_file}. There is though a solution for several packages +% to use these callbacks, see the implementation of \texttt{luatextra}. +% +%    \section{Implementation} +% +%    \subsection{Lua module} +% +%    Module identification. +% +%    \begin{macrocode} +%<*lua> +module('luatexbase.mcb', package.seeall) +luatexbase.provides_module({ +    name          = "luamcallbacks", +    version       = 0.93, +    date          = "2009/09/18", +    description   = "register several functions in a callback", +    author        = "Hans Hagen, Elie Roux and Manuel Pégourie-Gonnard", +    copyright     = "Hans Hagen, Elie Roux and Manuel Pégourie-Gonnard", +    license       = "CC0", +}) +%    \end{macrocode} +% +%    \texttt{callbacklist} is the main list, that contains the callbacks as +%    keys and a table of the registered functions a values. +% +%    \begin{macrocode} + +callbacklist = callbacklist or { } + +%    \end{macrocode} +% +%    A table with the default functions of the created callbacks. See +%    \texttt{create} for further informations. +% +%    \begin{macrocode} + +lua_callbacks_defaults = { } + +local format = string.format + +%    \end{macrocode} +% +%    There are 4 types of callback: +%    \begin{itemize} +%    \item the ones taking a list of nodes and returning a boolean and +%    eventually a new head (\texttt{list}) +%    \item the ones taking datas and returning the modified ones +%    (\texttt{data}) +%    \item the ones that can't have multiple functions registered in them +%    (\texttt{first}) +%    \item the ones for functions that don't return anything (\texttt{simple}) +%    \end{itemize} +% +%    \begin{macrocode} + +local list = 1 +local data = 2 +local first = 3 +local simple = 4 + +%    \end{macrocode} +% +%    \texttt{callbacktypes} is the list that contains the callbacks as keys +%    and the type (list or data) as values. +% +%    \begin{macrocode} + +callbacktypes = callbacktypes or { +buildpage_filter = simple, +token_filter = first, +pre_output_filter = list, +hpack_filter = list, +process_input_buffer = data, +mlist_to_hlist = list, +vpack_filter = list, +define_font = first, +open_read_file = first, +linebreak_filter = list, +post_linebreak_filter = list, +pre_linebreak_filter = list, +start_page_number = simple, +stop_page_number = simple, +start_run = simple, +show_error_hook = simple, +stop_run = simple, +hyphenate = simple, +ligaturing = simple, +kerning = data, +find_write_file = first, +find_read_file = first, +find_vf_file = data, +find_map_file = data, +find_format_file = data, +find_opentype_file = data, +find_output_file = data, +find_truetype_file = data, +find_type1_file = data, +find_data_file = data, +find_pk_file = data, +find_font_file = data, +find_image_file = data, +find_ocp_file = data, +find_sfd_file = data, +find_enc_file = data, +read_sfd_file = first, +read_map_file = first, +read_pk_file = first, +read_enc_file = first, +read_vf_file = first, +read_ocp_file = first, +read_opentype_file = first, +read_truetype_file = first, +read_font_file = first, +read_type1_file = first, +read_data_file = first, +} + +%    \end{macrocode} +% +%    In Lua\TeX\ version 0.43, a new callback called |process_output_buffer| +%    appeared, so we enable it. +% +%    \begin{macrocode} + +if tex.luatexversion > 42 then +    callbacktypes["process_output_buffer"] = data +end + +%    \end{macrocode} +% +%    As we overwrite \texttt{callback.register}, we save it as +%    \texttt{internalregister}. After that we declare some +%    functions to write the errors or the logs. +% +%    \begin{macrocode} + +internalregister = internalregister or callback.register + +local callbacktypes = callbacktypes + +log = log or function(...) +  luatexbase.module_log('luamcallbacks', format(...)) +end + +info = info or function(...) +  luatexbase.module_info('luamcallbacks', format(...)) +end + +warning = warning or function(...) +  luatexbase.module_warning('luamcallbacks', format(...)) +end + +error = error or function(...) +  luatexbase.module_error('luamcallbacks', format(...)) +end + +%    \end{macrocode} +% +%    A simple function we'll use later to understand the arguments of the +%    \texttt{create} function. It takes a string and returns the type +%    corresponding to the string or nil. +% +%    \begin{macrocode} + +function str_to_type(str) +    if str == 'list' then +        return list +    elseif str == 'data' then +        return data +    elseif str == 'first' then +        return first +    elseif str == 'simple' then +        return simple +    else +        return nil +    end +end + +%    \end{macrocode} +% +%    \begin{macro}{create} +% +%    This first function creates a new callback. The signature is +%    \texttt{create(name, ctype, default)} where \texttt{name} is the name of +%    the new callback to create, \texttt{ctype} is the type of callback, and +%    \texttt{default} is the default function to call if no function is +%    registered in this callback. +% +%    The created callback will behave the same way Lua\TeX\ callbacks do, you +%    can add and remove functions in it. The difference is that the callback +%    is not automatically called, the package developer creating a new +%    callback must also call it, see next function. +% +%    \begin{macrocode} + +function create(name, ctype, default) +    if not name then +        error(format("unable to call callback, no proper name passed", name)) +        return nil +    end +    if not ctype or not default then +        error(format("unable to create callback '%s', callbacktype or default function not specified", name)) +        return nil +    end +    if callbacktypes[name] then +        error(format("unable to create callback '%s', callback already exists", name)) +        return nil +    end +    local temp = str_to_type(ctype) +    if not temp then +        error(format("unable to create callback '%s', type '%s' undefined", name, ctype)) +        return nil +    end +    ctype = temp +    lua_callbacks_defaults[name] = default +    callbacktypes[name] = ctype +end + +%    \end{macrocode} +% +%    \end{macro} +% +%    \begin{macro}{call} +% +%    This function calls a callback. It can only call a callback created by +%    the \texttt{create} function. +% +%    \begin{macrocode} + +function call(name, ...) +    if not name then +        error(format("unable to call callback, no proper name passed", name)) +        return nil +    end +    if not lua_callbacks_defaults[name] then +        error(format("unable to call lua callback '%s', unknown callback", name)) +        return nil +    end +    local l = callbacklist[name] +    local f +    if not l then +        f = lua_callbacks_defaults[name] +    else +        if callbacktypes[name] == list then +            f = listhandler(name) +        elseif callbacktypes[name] == data then +            f = datahandler(name) +        elseif callbacktypes[name] == simple then +            f = simplehandler(name) +        elseif callbacktypes[name] == first then +            f = firsthandler(name) +        else +            error("unknown callback type") +        end +    end +    return f(...) +end + +%    \end{macrocode} +% +%    \end{macro} +% +%    \begin{macro}{add} +% +%    The main function. The signature is \texttt{add (name, +%    func, description, priority)} with \texttt{name} being the name of the +%    callback in which the function is added; \texttt{func} is the added +%    function; \texttt{description} is a small character string describing the +%    function, and \texttt{priority} an optional argument describing the +%    priority the function will have. +% +%    The functions for a callbacks are added in a list (in +%    \texttt{callbacklist\\.callbackname}). If they have no +%    priority or a high priority number, they will be added at the end of the +%    list, and will be called after the others. If they have a low priority +%    number, the will be added at the beginning of the list and will be called +%    before the others. +% +%    Something that must be made clear, is that there is absolutely no +%    solution for packages conflicts: if two packages want the top priority on +%    a certain callback, they will have to decide the priority they will give +%    to their function themself. Most of the time, the priority is not needed. +% +%    \begin{macrocode} + +function add (name,func,description,priority) +    if type(func) ~= "function" then +        error("unable to add function, no proper function passed") +        return +    end +    if not name or name == "" then +        error("unable to add function, no proper callback name passed") +        return +    elseif not callbacktypes[name] then +        error( +          format("unable to add function, '%s' is not a valid callback", +          name)) +        return +    end +    if not description or description == "" then +        error( +          format("unable to add function to '%s', no proper description passed", +          name)) +        return +    end +    if get_priority(name, description) ~= 0 then +        warning( +          format("function '%s' already registered in callback '%s'", +          description, name)) +    end +    local l = callbacklist[name] +    if not l then +        l = {} +        callbacklist[name] = l +        if not lua_callbacks_defaults[name] then +            if callbacktypes[name] == list then +                internalregister(name, listhandler(name)) +            elseif callbacktypes[name] == data then +                internalregister(name, datahandler(name)) +            elseif callbacktypes[name] == simple then +                internalregister(name, simplehandler(name)) +            elseif callbacktypes[name] == first then +                internalregister(name, firsthandler(name)) +            else +                error("unknown callback type") +            end +        end +    end +    local f = { +        func = func, +        description = description, +    } +    priority = tonumber(priority) +    if not priority or priority > #l then +        priority = #l+1 +    elseif priority < 1 then +        priority = 1 +    end +    if callbacktypes[name] == first and (priority ~= 1 or #l ~= 0) then +        warning(format("several callbacks registered in callback '%s', only the first function will be active.", name)) +    end +    table.insert(l,priority,f) +    log( +      format("inserting function '%s' at position %s in callback list for '%s'", +      description,priority,name)) +end + +%    \end{macrocode} +% +%    \end{macro} +% +%    \begin{macro}{get priority} +% +%    This function tells if a function has already been registered in a +%    callback, and gives its current priority. The arguments are the name of +%    the callback and the description of the function. If it has already been +%    registered, it gives its priority, and if not it returns false. +% +%    \begin{macrocode} + +function get_priority (name, description) +    if not name or name == "" or not callbacktypes[name] or not description then +        return 0 +    end +    local l = callbacklist[name] +    if not l then return 0 end +    for p, f in pairs(l) do +        if f.description == description then +            return p +        end +    end +    return 0 +end + +%    \end{macrocode} +% +%    \end{macro} +% +%    \begin{macro}{remove} +% +%    The function that removes a function from a callback. The signature is +%    \texttt{mcallbacks.remove (name, description)} with \texttt{name} being +%    the name of callbacks, and description the description passed to +%    \texttt{mcallbacks.add}. +% +%    \begin{macrocode} + +function remove (name, description) +    if not name or name == "" then +        error("unable to remove function, no proper callback name passed") +        return +    elseif not callbacktypes[name] then +        error( +          format("unable to remove function, '%s' is not a valid callback", +          name)) +        return +    end +    if not description or description == "" then +        error( +          format("unable to remove function from '%s', no proper description passed", +          name)) +        return +    end +    local l = callbacklist[name] +    if not l then +        error(format("no callback list for '%s'",name)) +        return +    end +    for k,v in ipairs(l) do +        if v.description == description then +            table.remove(l,k) +            log( +              format("removing function '%s' from '%s'",description,name)) +            if not next(l) then +              callbacklist[name] = nil +              if not lua_callbacks_defaults[name] then +                internalregister(name, nil) +              end +            end +            return +        end +    end +    warning( +      format("unable to remove function '%s' from '%s'",description,name)) +end + +%    \end{macrocode} +% +%    \end{macro} +% +%    \begin{macro}{reset} +% +%    This function removes all the functions registered in a callback. +% +%    \begin{macrocode} + +function reset (name) +    if not name or name == "" then +        error("unable to reset, no proper callback name passed") +        return +    elseif not callbacktypes[name] then +        error( +          format("reset error, '%s' is not a valid callback", +          name)) +        return +    end +    if not lua_callbacks_defaults[name] then +        internalregister(name, nil) +    end +    local l = callbacklist[name] +    if l then +        log(format("resetting callback list '%s'",name)) +        callbacklist[name] = nil +    end +end + +%    \end{macrocode} +% +%    \end{macro} +% +%     This function and the following ones are only internal. This one is the +%     handler for the first type of callbacks: the ones that take a list head +%     and return true, false, or a new list head. +% +%    \begin{macro}{listhandler} +% +%    \begin{macrocode} + +function listhandler (name) +    return function(head,...) +        local l = callbacklist[name] +        if l then +            local done = true +            for _, f in ipairs(l) do +                -- the returned value can be either true or a new head plus true +                rtv1, rtv2 = f.func(head,...) +                if type(rtv1) == 'boolean' then +                    done = rtv1 +                elseif type (rtv1) == 'userdata' then +                    head = rtv1 +                end +                if type(rtv2) == 'boolean'  then +                    done = rtv2 +                elseif type(rtv2) == 'userdata' then +                    head = rtv2 +                end +                if done == false then +                    error(format( +                      "function \"%s\" returned false in callback '%s'",  +                      f.description, name)) +                end +            end +            return head, done +        else +            return head, false +        end +    end +end + +%    \end{macrocode} +% +%    \end{macro} +% +%     The handler for callbacks taking datas and returning modified ones. +% +%    \begin{macro}{datahandler} +% +%    \begin{macrocode} + +function datahandler (name) +    return function(data,...) +        local l = callbacklist[name] +        if l then +            for _, f in ipairs(l) do +                data = f.func(data,...) +            end +        end +        return data +    end +end + +%    \end{macrocode} +% +%    \end{macro} +% +%     This function is for the handlers that don't support more than one +%     functions in them. In this case we only call the first function of the +%     list. +% +%    \begin{macro}{firsthandler} +% +%    \begin{macrocode} + +function firsthandler (name) +    return function(...) +        local l = callbacklist[name] +        if l then +            local f = l[1].func +            return f(...) +        else +            return nil, false +        end +    end +end + +%    \end{macrocode} +% +%    \end{macro} +% +%     Handler for simple functions that don't return anything. +% +%    \begin{macro}{simplehandler} +% +%    \begin{macrocode} + +function simplehandler (name) +    return function(...) +        local l = callbacklist[name] +        if l then +            for _, f in ipairs(l) do +                f.func(...) +            end +        end +    end +end + +%    \end{macrocode} +% +%    \end{macro} +% +%    Finally we add some functions to the \texttt{callback} module, and we +%    overwrite \texttt{callback.register} so that it outputs an error. +% +%    \begin{macrocode} + +callback.add = add +callback.remove = remove +callback.reset = reset +callback.create = create +callback.call = call +callback.get_priority = get_priority + +callback.register = function (...) +error("function callback.register has been deleted by luamcallbacks, please use callback.add instead.") +end + +%    \end{macrocode} +% +% \iffalse +%</lua> +% \fi +% +%    \section{Test files} +% +%    A few basic tests for Plain and LaTeX. +% +%    \begin{macrocode} +%<testplain>\input luatexbase-modutils.sty +%<testlatex>\RequirePackage{luatexbase-modutils} +%<*testplain,testlatex> +\catcode 64 11 +\luatexbase@directlua{ +  require "luatexbase.mcb" +  local function one(head,...) +      texio.write_nl("I'm number 1") +      return head, true +  end + +  local function two(head,...) +      texio.write_nl("I'm number 2") +      return head, true +  end + +  local function three(head,...) +      texio.write_nl("I'm number 3") +      return head, true +  end + +  callback.add("hpack_filter",one,"my example function one",1) +  callback.add("hpack_filter",two,"my example function two",2) +  callback.add("hpack_filter",three,"my example function three",1) + +  callback.remove("hpack_filter","my example function three") +} +%</testplain,testlatex> +%<testplain>\bye +%<testlatex>\stop +%    \end{macrocode} +% +% \Finale +\endinput | 
