diff options
Diffstat (limited to 'macros/latex-dev/base/lttemplates.dtx')
-rw-r--r-- | macros/latex-dev/base/lttemplates.dtx | 2698 |
1 files changed, 2698 insertions, 0 deletions
diff --git a/macros/latex-dev/base/lttemplates.dtx b/macros/latex-dev/base/lttemplates.dtx new file mode 100644 index 0000000000..844fb6fd34 --- /dev/null +++ b/macros/latex-dev/base/lttemplates.dtx @@ -0,0 +1,2698 @@ +% \iffalse meta-comment +% +% Copyright (C) 1999 Frank Mittelbach, Chris Rowley, David Carlisle +% Copyright (C) 2004-2010 Frank Mittelbach, The LaTeX Project +% Copyright (C) 2011-2024 +% The LaTeX Project and any individual authors listed elsewhere +% in this file. +% +% This file is part of the LaTeX base system. +% ------------------------------------------- +% +% It may be distributed and/or modified under the +% conditions of the LaTeX Project Public License, either version 1.3c +% of this license or (at your option) any later version. +% The latest version of this license is in +% https://www.latex-project.org/lppl.txt +% and version 1.3c or later is part of all distributions of LaTeX +% version 2008 or later. +% +% This file has the LPPL maintenance status "maintained". +% +% The list of all files belonging to the LaTeX base distribution is +% given in the file `manifest.txt'. See also `legal.txt' for additional +% information. +% +% The list of derived (unpacked) files belonging to the distribution +% and covered by LPPL is defined by the unpacking scripts (with +% extension .ins) which are part of the distribution. +% +% \fi +% +% \iffalse +% +%%% From File: lttemplates.dtx +% +%<*driver> +% \fi +\ProvidesFile{lttemplates.dtx} + [2024-02-11 v1.0a LaTeX Kernel (Prototype document functions)] +% \iffalse +\documentclass{l3doc} +\GetFileInfo{lttemplates.dtx} +\begin{document} + \DocInput{lttemplates.dtx} +\end{document} +%</driver> +% \fi +% +% \title{The \texttt{lttemplates.dtx} code\thanks{This file has version +% \fileversion\ dated \filedate, \copyright\ \LaTeX\ +% Project.}} +% \author{^^A +% Frank Mittelbach, Chris Rowley, David Carlisle, \LaTeX{} Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \maketitle +% +% \section{Introduction} +% +% There are three broad \enquote{layers} between putting down ideas into +% a source file and ending up with a typeset document. These layers of +% document writing are +% \begin{enumerate} +% \item authoring of the text with mark-up; +% \item document layout design; +% \item implementation (with \TeX{} programming) of the design. +% \end{enumerate} +% We write the text as an author, and we see the visual output of the design +% after the document is generated; the \TeX{} implementation in the middle is +% the glue between the two. +% +% \LaTeX{}'s greatest success has been to standardise a system of mark-up that +% balances the trade-off between ease of reading and ease of writing to suit +% almost all forms of technical writing. It's +% other original strength was a good background in typographical design; while +% the standard \LaTeXe{} classes look somewhat dated now in terms of their +% visual design, their typography is generally sound (barring the occasional +% minor faults). +% +% However, \LaTeXe{} has always lacked a standard approach to customising +% the visual design of a document. Changing the looks of the standard classes +% involved either: +% \begin{itemize} +% \item Creating a new version of the implementation code of the class and +% editing it. +% \item Loading one of the many packages to customise certain elements of +% the standard classes. +% \item Loading a completely different document class, such as +% \textsf{KOMA-Script} or \textsf{memoir}, that allows easy customisation. +% \end{itemize} +% All three of these approaches have their drawbacks and learning curves. +% +% The idea behind \pkg{lttemplates} is to cleanly separate the three layers +% introduced at the beginning of this section, so that document authors who +% are not programmers can easily change the design of their documents. +% \pkg{lttemplates} also makes it easier for \LaTeX{} programmers to provide +% their own customisations on top of a pre-existing class. +% +% \section{What is a document?} +% +% Besides the textual content of the words themselves, the source file +% of a document contains mark-up elements that add structure to the +% document. These elements include sectional divisions, figure/table +% captions, lists of various sorts, theorems/proofs, and so on. +% The list will be different for every document that can be written. +% +% Each element can be represented logically without worrying about the +% formatting, with mark-up such as \cs{section}, \cs{caption}, +% |\begin{enumerate}| and so on. The output of each one of these +% document elements will be a typeset representation of the information +% marked up, and the visual arrangement and design of these elements +% can vary widely in producing a variety of desired outcomes. +% +% For each type of document element, there may be design variations that +% contain the same sort of information but present it in slightly +% different ways. For example, the difference between a numbered and an +% unnumbered section, \cs{section} and |\section*|, or the difference +% between an itemised list or an enumerated list. +% +% There are three distinct layers in the definition of +% \enquote{a document} at this level +% \begin{enumerate} +% \item semantic elements such as the ideas of sections and lists; +% \item a set of design solutions for representing these elements +% visually; +% \item specific variations for these designs that represent the +% elements in the document. +% \end{enumerate} +% In the parlance of the template system, these are called types, +% templates, and instances, and they are discussed below in sections +% \ref{sec:types}, \ref{sec:templates}, and~\ref{sec:instances}, +% respectively. +% +% \section {Types, templates, and instances} +% +% By formally declaring documents to be composed of mark-up elements +% grouped into types, which are interpreted and typeset with a set of +% templates, each of which has one or more instances with which to +% compose each and every semantic unit of the text, we can cleanly +% separate the components of document construction. +% +% All of the structures provided by the template system are global, +% and do not respect \TeX{} grouping. +% +% \section{Template types} +% \label{sec:types} +% +% An \emph{template type} (sometimes just \enquote{type}) is an +% abstract idea of a document element that takes a fixed number of +% arguments corresponding to the information from the document author +% that it is representing. A sectioning type, for example, might take +% three inputs: \enquote{title}, \enquote{short title}, and +% \enquote{label}. +% +% Any given document class will define which types are to be +% used in the document, and any template of a given type can be +% used to generate an instance for the type. (Of course, different +% templates will produce different typeset representations, but the +% underlying content will be the same.) +% +% \begin{function}{\NewTemplateType} +% \begin{syntax} +% \cs{NewTemplateType} \Arg{template type} \Arg{no. of args} +% \end{syntax} +% This function defines an \meta{template type} taking +% \meta{number of arguments}, where the \meta{type} is an +% abstraction as discussed above. For example, +% \begin{verbatim} +% \NewTemplateType{sectioning}{3} +% \end{verbatim} +% creates a type \enquote{sectioning}, where each use of that +% type will need three arguments. +% \end{function} +% +% \section{Templates} +% \label{sec:templates} +% +% A \emph{template} is a generalised design solution for representing +% the information of a specified type. Templates that do the same +% thing, but in different ways, are grouped together by their type +% and given separate names. There are two important parts to a template: +% \begin{itemize} +% \item the parameters it takes to vary the design it is producing; +% \item the implementation of the design. +% \end{itemize} +% As a document author or designer does not care about the +% implementation but rather only the interface to the template, these two +% aspects of the template definition are split into two independent +% declarations, \cs{DeclareTemplateInterface} and +% \cs{DeclareTemplateCode}. +% +% \begin{function}{\DeclareTemplateInterface} +% \begin{syntax} +% \cs{DeclareTemplateInterface} +% ~~\Arg{type} \Arg{template} \Arg{no.~of args} +% ~~\Arg{key list} +% \end{syntax} +% A \meta{template} interface is declared for a particular +% \meta{type}, where the \meta{number of arguments} must +% agree with the type declaration. The interface itself is +% defined by the \meta{key list}, which is itself a key--value list +% taking a specialized format: +% \begin{quotation} +% \obeylines +% \noindent +% \meta{key1}~":"~\meta{key type1}~"," +% \meta{key2}~":"~\meta{key type2}~"," +% \meta{key3}~":"~\meta{key type3}~"="~\meta{default3}~"," +% \meta{key4}~":"~\meta{key type4}~"="~\meta{default4}~"," +% \ldots +% \end{quotation} +% Each \meta{key} name should consist of \textsc{ascii} characters, +% with the exception of |,|, |=| and \verb*| |. The recommended form +% for key names is to use lower case letters, with dashes to separate +% out different parts. Spaces are ignored in key names, so they can be +% included or missed out at will. Each \meta{key} must have a +% \meta{key type}, which defined the type of input that the \meta{key} +% requires. A full list of key types is given in +% Table~\ref{tab:key-types}. Each key may have a \meta{default} +% value, which will be used in by the template if the \meta{key} is +% not set explicitly. The \meta{default} should be of the correct +% form to be accepted by the \meta{key type} of the \meta{key}: this +% is not checked by the code. +% \end{function} +% +% \begin{table} +% \centering +% \begin{tabular}{>{\ttfamily}ll} +% \toprule +% \multicolumn{1}{l}{Key-type} & Description of input \\ +% \midrule +% boolean & \texttt{true} or \texttt{false} \\ +% choice\Arg{choices} +% & A list of pre-defined \meta{choices} \\ +% commalist & A comma-separated list \\ +% function\Arg{$N$} +% & A function definition with $N$ arguments +% ($N$ from $0$ to $9$) \\ +% instance\Arg{name} +% & An instance of type \meta{name} \\ +% integer & An integer or integer expression \\ +% length & A fixed length \\ +% muskip & A math length with shrink and stretch components \\ +% real & A real (floating point) value \\ +% skip & A length with shrink and stretch components \\ +% tokenlist & A token list: any text or commands \\ +% \bottomrule +% \end{tabular} +% \caption{Key-types for defining template interfaces with +% \cs{DeclareTemplateInterface}.} +% \label{tab:key-types} +% \end{table} +% +% \begin{function}{\KeyValue} +% \begin{syntax} +% \cs{KeyValue} \Arg{key name} +% \end{syntax} +% There are occasions where the default (or value) for one key +% should be taken from another. The \cs{KeyValue} function can be +% used to transfer this information without needing to know the +% internal implementation of the key: +% \begin{verbatim} +% \DeclareTemplateInterface { type } { template } { no. of args } +% { +% key-name-1 : key-type = value , +% key-name-2 : key-type = \KeyValue { key-name-1 }, +% ... +% } +% \end{verbatim} +% \end{function} +% +% \begin{function}{\DeclareTemplateCode} +% \begin{syntax} +% \cs{DeclareTemplateCode} +% ~~\Arg{type} \Arg{template} \Arg{no.~of args} +% ~~\Arg{key bindings} \Arg{code} +% \end{syntax} +% The relationship between a templates keys and the internal +% implementation is created using the \cs{DeclareTemplateCode} +% function. As with \cs{DeclareTemplateInterface}, the +% \meta{template} name is given along with the \meta{type} +% and \meta{number of arguments} required. The \meta{key bindings} +% argument is a key--value list which specifies the relationship +% between each \meta{key} of the template interface with an +% underlying\meta{variable}. +% +% \begin{quotation} +% \obeylines +% \noindent +% \meta{key1}~"="~\meta{variable1}, +% \meta{key2}~"="~\meta{variable2}, +% \meta{key3}~"="~global~\meta{variable3}, +% \meta{key4}~"="~global~\meta{variable4}, +% \ldots +% \end{quotation} +% With the exception of the choice, code and function key types, +% the \meta{variable} here should be the name of an existing +% \LaTeX3 register. As illustrated, the key word \enquote{global} +% may be included in the listing to indicate that the \meta{variable} +% should be assigned globally. A full list of variable bindings is +% given in Table~\ref{tab:key-vars}. +% +% The \meta{code} argument of \cs{DeclareTemplateCode} is used +% as the replacement text for the template when it is used, either +% directly or as an instance. This may therefore accept arguments +% |#1|, |#2|, \emph{etc}.~as detailed by the \meta{number of arguments} +% taken by the type. +% \end{function} +% +% \begin{table} +% \centering +% \begin{tabular}{>{\ttfamily}ll} +% \toprule +% \multicolumn{1}{l}{Key-type} & Description of binding \\ +% \midrule +% boolean & Boolean variable, \emph{e.g}.~\cs{l_tmpa_bool} \\ +% choice +% & List of choice implementations +% (see Section~\ref{sec:choices-key}) \\ +% commalist & Comma list, \emph{e.g}.~\cs{l_tmpa_clist} \\ +% function +% & Function taking $N$ arguments, \emph{e.g}.~\cs{use_i:nn} \\ +% instance \\ +% integer & Integer variable, \emph{e.g}.~\cs{l_tmpa_int} \\ +% length & Dimension variable, \emph{e.g}.~\cs{l_tmpa_dim} \\ +% muskip & Muskip variable, \emph{e.g}.~\cs{l_tmpa_muskip} \\ +% real & Floating-point variable, \emph{e.g}.~\cs{l_tmpa_fp} \\ +% skip & Skip variable, \emph{e.g}.~\cs{l_tmpa_skip} \\ +% tokenlist & Token list variable, \emph{e.g}.~\cs{l_tmpa_tl} \\ +% \bottomrule +% \end{tabular} +% \caption{Bindings required for different key types when defining +% template implementations with \cs{DeclareTemplateCode}. Apart +% from \texttt{code}, \texttt{choice} and \texttt{function} +% all of these accept the key word \texttt{global} to carry +% out a global assignment.} +% \label{tab:key-vars} +% \end{table} +% +% \begin{function}{\AssignTemplateKeys} +% \begin{syntax} +% \cs{AssignTemplateKeys} +% \end{syntax} +% In the final argument of \cs{DeclareTemplateCode} the assignment of +% keys defined by the template may be delayed by including the command +% \cs{AssignTemplateKeys}. If this is \emph{not} present, keys are assigned +% immediately before the template code. If \cs{AssignTemplateKeys} is +% present, assignment is delayed until this point. Note that the +% command must be \emph{directly} present in the code, not placed +% within a nested command/macro. +% \end{function} +% +% \begin{function}{\DeclareTemplateCopy} +% \begin{syntax} +% \cs{DeclareTemplateCopy} +% ~~\Arg{type} \Arg{template2} \Arg{template1} +% \end{syntax} +% Copies \meta{template1} of \meta{type} to a new name \meta{template2}: +% the copy can then be edited independent of teh original. +% \end{function} +% +% \section{Multiple choices} +% \label{sec:choices-key} +% +% The \texttt{choice} key type implements multiple choice input. At the +% interface level, only the list of valid choices is needed: +% \begin{verbatim} +% \DeclareTemplateInterface { foo } { bar } { 0 } +% { key-name : choice { A, B, C } } +% \end{verbatim} +% where the choices are given as a comma-list (which must therefore +% be wrapped in braces). A default value can also be given: +% \begin{verbatim} +% \DeclareTemplateInterface { foo } { bar } { 0 } +% { key-name : choice { A, B, C } = A } +% \end{verbatim} +% +% At the implementation level, each choice is associated with code, +% using a nested key--value list. +% \begin{verbatim} +% \DeclareTemplateCode { foo } { bar } { 0 } +% { +% key-name = +% { +% A = Code-A , +% B = Code-B , +% C = Code-C +% } +% } +% { ... } +% \end{verbatim} +% The two choice lists should match, but in the implementation a +% special \texttt{unknown} choice is also available. This can be used +% to ignore values and implement an \enquote{else} branch: +% \begin{verbatim} +% \DeclareTemplateCode { foo } { bar } { 0 } +% { +% key-name = +% { +% A = Code-A , +% B = Code-B , +% C = Code-C , +% unknown = Else-code +% } +% } +% { ... } +% \end{verbatim} +% The \texttt{unknown} entry must be the last one given, and should +% \emph{not} be listed in the interface part of the template. +% +% For keys which accept the values \texttt{true} and \texttt{false} +% both the boolean and choice key types can be used. As template +% interfaces are intended to prompt clarity at the design level, the +% boolean key type should be favoured, with the choice type reserved +% for keys which take arbitrary values. +% +% \section{Instances} +% \label{sec:instances} +% +% After a template is defined it still needs to be put to use. The +% parameters that it expects need to be defined before it can be used in +% a document. Every time a template has parameters given to it, an +% \emph{instance} is created, and this is the code that ends up in the +% document to perform the typesetting of whatever pieces of information +% are input into it. +% +% For example, a template might say \enquote{here is a section with or +% without a number that might be centred or left aligned and print its +% contents in a certain font of a certain size, with a bit of a gap +% before and after it} whereas an instance declares \enquote{this is a +% section with a number, which is centred and set in $12\,\text{pt}$ +% italic with a $10\,\text{pt}$ skip before and a +% $12\,\text{pt}$ skip after it}. Therefore, an instance is just a +% frozen version of a template with specific settings as chosen by the +% designer. +% +% \begin{function}{\DeclareInstance} +% \begin{syntax} +% \cs{DeclareInstance} +% ~~\Arg{type} \Arg{instance} \Arg{template} \Arg{parameters} +% \end{syntax} +% This function uses a \meta{template} for an \meta{type} +% to create an \meta{instance}. The \meta{instance} will be set +% up using the \meta{parameters}, which will set some of the +% \meta{keys} in the \meta{template}. +% +% As a practical example, consider a type for document sections +% (which might include chapters, parts, sections, \emph{etc}.), which +% is called \texttt{sectioning}. One possible template for this +% type might be called \texttt{basic}, and one instance of this +% template would be a numbered section. The instance declaration might +% read: +% \begin{verbatim} +% \DeclareInstance { sectioning } { section-num } { basic } +% { +% numbered = true , +% justification = center , +% font =\normalsize\itshape , +% before-skip = 10pt , +% after-skip = 12pt , +% } +% \end{verbatim} +% Of course, the key names here are entirely imaginary, but illustrate +% the general idea of fixing some settings. +% \end{function} +% +% \begin{function}{\IfInstanceExistT, \IfInstanceExistF, \IfInstanceExistTF} +% \begin{syntax} +% \cs{IfInstanceExistTF} \Arg{type} \Arg{instance} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if the named \meta{instance} of a \meta{type} exists, and +% then inserts the appropriate code into the input stream. +% \end{function} +% +% \begin{function}{\DeclareInstanceCopy} +% \begin{syntax} +% \cs{DeclareInstanceCopy} +% ~~\Arg{type} \Arg{instance2} \Arg{instance1} +% \end{syntax} +% Copies the \meta{values} for \meta{instance1} for an +% \meta{type} to \meta{instance2}. +% \end{function} +% +% \section{Document interface} +% +% After the instances have been chosen, document commands must be +% declared to use those instances in the document. \cs{UseInstance} +% calls instances directly, and this command should be used internally +% in document-level mark-up. +% +% \begin{function}{\UseInstance} +% \begin{syntax} +% \cs{UseInstance} +% ~~\Arg{type} \Arg{instance} \meta{arguments} +% \end{syntax} +% Uses an \meta{instance} of the \meta{type}, which will require +% \meta{arguments} as determined by the number specified for the +% \meta{type}. The \meta{instance} must have been declared +% before it can be used, otherwise an error is raised. +% \end{function} +% +% \begin{function}{\UseTemplate} +% \begin{syntax} +% \cs{UseTemplate} \Arg{type} \Arg{template} +% ~~\Arg{settings} \meta{arguments} +% \end{syntax} +% Uses the \meta{template} of the specified \meta{type}, +% applying the \meta{settings} and absorbing \meta{arguments} as +% detailed by the \meta{type} declaration. This in effect +% is the same as creating an instance using \cs{DeclareInstance} +% and immediately using it with \cs{UseInstance}, but without the +% instance having any further existence. It is therefore useful where +% a template needs to be used once. +% +% This function can also be used as the argument to \texttt{instance} +% key types: +% \begin{verbatim} +% \DeclareInstance { type } { template } { instance } +% { +% instance-key = +% \UseTemplate { type2 } { template2 } { <settings> } +% } +% \end{verbatim} +% \end{function} +% +% \section{Changing existing definitions} +% +% Template parameters may be assigned specific defaults for instances +% to use if the instance declaration doesn't explicit set those +% parameters. In some cases, the document designer will wish to edit +% these defaults to allow them to \enquote{cascade} to the instances. +% The alternative would be to set each parameter identically for each +% instance declaration, a tedious and error-prone process. +% +% \begin{function}{\EditTemplateDefaults} +% \begin{syntax} +% \cs{EditTemplateDefaults} +% ~~\Arg{type} \Arg{template} \Arg{new defaults} +% \end{syntax} +% Edits the \meta{defaults} for a \meta{template} for an +% \meta{type}. The \meta{new defaults}, given as a key--value +% list, replace the existing defaults for the \meta{template}. This +% means that the change will apply to instances declared after the +% editing, but that instances which have already been created are +% unaffected. +% \end{function} +% +% \begin{function}{\EditInstance} +% \begin{syntax} +% \cs{EditInstance} +% ~~\Arg{type} \Arg{instance} \Arg{new values} +% \end{syntax} +% Edits the \meta{values} for an \meta{instance} for an +% \meta{type}. The \meta{new values}, given as a key--value +% list, replace the existing values for the \meta{instance}. This +% function is complementary to \cs{EditTemplateDefaults}: +% \cs{EditInstance} changes a single instance while leaving the +% template untouched. +% \end{function} +% +% \section{\emph{Ad hoc} adjustment of templates} +% +% \begin{function}{\SetTemplateKeys} +% \begin{syntax} +% \cs{SetTemplateKeys} \Arg{type} \Arg{template} \Arg{keyvals} +% \end{syntax} +% At point of use it may be useful to apply changed to individual instances. +% This is supported as each template key is made available for adjustment +% using \cs{SetTemplateKeys}. +% \end{function} +% +% For example, after +% \begin{verbatim} +% \NewTypeType{MyObj}{0} +% \DeclareTemplateInterface{MyObj}{TemplateA}{0} +% { +% akey: tokenlist , +% bkey: function{2} +% } +% \DeclareTemplateCode{MyObj}{TemplateA}{0} +% { +% akey = SomeTokens , +% bkey = \func:nn , +% } +% \end{verbatim} +% the template keys could be adjusted in an \emph{ad hoc} fashion using +% \begin{verbatim} +% \SetTemplateKeys{MyObj}{TemplateA} +% { +% akey = OtherTokens , +% bkey = \AltFunc:nn +% } +% \end{verbatim} +% +% \section{Getting information about templates and instances} +% +% \begin{function}{\ShowInstanceValues} +% \begin{syntax} +% \cs{ShowInstanceValues} \Arg{type} \Arg{instance} +% \end{syntax} +% Shows the \meta{values} for an \meta{instance} of the given +% \meta{type} at the terminal. +% \end{function} +% +% \begin{function}{\ShowTemplateCode} +% \begin{syntax} +% \cs{ShowTemplateCode} \Arg{type} \Arg{template} +% \end{syntax} +% Shows the \meta{code} of a \meta{template} for an \meta{type} +% in the terminal. +% \end{function} +% +% \begin{function}{\ShowTemplateDefaults} +% \begin{syntax} +% \cs{ShowTemplateDefaults} \Arg{type} \Arg{template} +% \end{syntax} +% Shows the \meta{default} values of a \meta{template} for an +% \meta{type} in the terminal. +% \end{function} +% +% \begin{function}{\ShowTemplateInterface} +% \begin{syntax} +% \cs{ShowTemplateInterface} \Arg{type} \Arg{template} +% \end{syntax} +% Shows the \meta{keys} and associated \meta{key types} of a +% \meta{template} for an \meta{type} in the terminal. +% \end{function} +% +% \begin{function}{\ShowTemplateVariables} +% \begin{syntax} +% \cs{ShowTemplateVariables} \Arg{type} \Arg{template} +% \end{syntax} +% Shows the \meta{variables} and associated \meta{keys} of a +% \meta{template} for an \meta{type} in the terminal. Note that +% \texttt{code} and \texttt{choice} keys do not map directly to variables +% but to arbitrary code. For \texttt{choice} keys, each valid choice +% is shown as a separate entry in the list, with the key name and choice +% separated by a space, for example +% \begin{verbatim} +% Template 'example' of type 'example' has variable mapping: +% > demo unknown => \def \demo {?} +% > demo c => \def \demo {c} +% > demo b => \def \demo {b} +% > demo a => \def \demo {a}. +% \end{verbatim} +% would be shown for a choice key \texttt{demo} with valid choices +% \texttt{a}, \texttt{b} and \texttt{c}, plus code for an \texttt{unknown} +% branch. +% \end{function} +% +% \MaybeStop{\setlength\IndexMin{200pt}\PrintIndex} +% +% \section{The implementation} +% +% \begin{macrocode} +%<@@=template> +% \end{macrocode} +% +% \begin{macrocode} +%<*2ekernel> +\message{templates,} +%</2ekernel> +% \end{macrocode} +% +% \begin{macrocode} +%<*2ekernel|latexrelease> +% \end{macrocode} +% +% \begin{macrocode} +\ExplSyntaxOn +% \end{macrocode} +% +% \begin{macrocode} +%<latexrelease>\NewModuleRelease{2024/06/01}{lttemplates} +%<latexrelease> {Prototype~document~commands}% +% \end{macrocode} +% +% \subsection{Variables and constants} +% +% \begin{variable} +% { +% \c_@@_code_root_tl , +% \c_@@_defaults_root_tl , +% \c_@@_instances_root_tl , +% \c_@@_keytypes_root_tl , +% \c_@@_key_order_root_tl , +% \c_@@_restrict_root_tl , +% \c_@@_values_root_tl , +% \c_@@_vars_root_tl +% } +% So that literal values are kept to a minimum. +% \begin{macrocode} +\tl_const:Nn \c_@@_code_root_tl { template~code~>~ } +\tl_const:Nn \c_@@_defaults_root_tl { template~defaults~>~ } +\tl_const:Nn \c_@@_instances_root_tl { template~instance~>~ } +\tl_const:Nn \c_@@_keytypes_root_tl { template~key~types~>~ } +\tl_const:Nn \c_@@_key_order_root_tl { template~key~order~>~ } +\tl_const:Nn \c_@@_values_root_tl { template~values~>~ } +\tl_const:Nn \c_@@_vars_root_tl { template~vars~>~ } +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\c_@@_keytypes_arg_seq} +% A list of keytypes which also need additional data (an argument), +% used to parse the keytype correctly. +% \begin{macrocode} +\seq_const_from_clist:Nn \c_@@_keytypes_arg_seq + { choice , function , instance } +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_@@_type_prop} +% For storing types and the associated number of arguments. +% \begin{macrocode} +\prop_new:N \g_@@_type_prop +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_assignments_tl} +% When creating an instance, the assigned values are collected here. +% \begin{macrocode} +\tl_new:N \l_@@_assignments_tl +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\l_@@_default_tl} +% The default value for a key is recovered here from the property list +% in which it is stored. +% \begin{macrocode} +\tl_new:N \l_@@_default_tl +% \end{macrocode} +%\ end{macro} +% +% \begin{variable}{\l_@@_error_bool} +% A flag for errors to be carried forward. +% \begin{macrocode} +\bool_new:N \l_@@_error_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_global_bool} +% Used to indicate that assignments should be global. +% \begin{macrocode} +\bool_new:N \l_@@_global_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% { +% \l_@@_key_name_tl , +% \l_@@_keytype_tl , +% \l_@@_keytype_arg_tl , +% \l_@@_value_tl , +% \l_@@_var_tl +% } +% When defining each key in a template, the name and type of the key +% need to be separated and stored. Any argument needed by the +% keytype is also stored separately. +% \begin{macrocode} +\tl_new:N \l_@@_key_name_tl +\tl_new:N \l_@@_keytype_tl +\tl_new:N \l_@@_keytype_arg_tl +\tl_new:N \l_@@_value_tl +\tl_new:N \l_@@_var_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% { +% \l_@@_keytypes_prop , +% \l_@@_key_order_seq , +% \l_@@_values_prop , +% \l_@@_vars_prop +% } +% To avoid needing too many difficult-to-follow csname assignments, +% various scratch token registers are used to build up data, which is +% then transferred +% \begin{macrocode} +\prop_new:N \l_@@_keytypes_prop +\seq_new:N \l_@@_key_order_seq +\prop_new:N \l_@@_values_prop +\prop_new:N \l_@@_vars_prop +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% { +% \l_@@_tmp_clist , +% \l_@@_tmp_dim , +% \l_@@_tmp_int , +% \l_@@_tmp_muskip , +% \l_@@_tmp_skip , +% \l_@@_tmp_tl +% } +% Scratch space. +% \begin{macrocode} +\clist_new:N \l_@@_tmp_clist +\dim_new:N \l_@@_tmp_dim +\int_new:N \l_@@_tmp_int +\muskip_new:N \l_@@_tmp_muskip +\skip_new:N \l_@@_tmp_skip +\tl_new:N \l_@@_tmp_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\s_@@_mark, \s_@@_stop} +% Internal scan marks. +% \begin{macrocode} +\scan_new:N \s_@@_mark +\scan_new:N \s_@@_stop +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\q_@@_nil} +% Internal quarks. +% \begin{macrocode} +\quark_new:N \q_@@_nil +% \end{macrocode} +% \end{variable} +% +% \begin{macro}[pTF]{\@@_quark_if_nil:n} +% Branching quark conditional. +% \begin{macrocode} +\__kernel_quark_new_conditional:Nn \@@_quark_if_nil:N { F } +% \end{macrocode} +% \end{macro} +% +% \subsection{Testing existence and validity} +% +% There are a number of checks needed for either the existence of +% a type, template or instance. There are also some for the +% validity of a particular call. All of these are collected up here. +% +% \begin{macro}{\@@_execute_if_arg_agree:nnT} +% A test agreement between the number of arguments for the template +% type and that specified when creating a template. This is not done as a +% separate conditional for efficiency and better error message +% \begin{macrocode} +\cs_new_protected:Npn \@@_execute_if_arg_agree:nnT #1#2#3 + { + \prop_get:NnN \g_@@_type_prop {#1} \l_@@_tmp_tl + \int_compare:nNnTF {#2} = \l_@@_tmp_tl + {#3} + { + \msg_error:nneee { template } { argument-number-mismatch } + {#1} { \l_@@_tmp_tl } {#2} + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_execute_if_code_exist:nnT} +% A template is only fully declared if the code has been set up, +% which can be checked by looking for the template function itself. +% \begin{macrocode} +\cs_new_protected:Npn \@@_execute_if_code_exist:nnT #1#2#3 + { + \cs_if_exist:cTF { \c_@@_code_root_tl #1 / #2 } + {#3} + { \msg_error:nnnn { template } { no-template-code } {#1} {#2} } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% {\@@_execute_if_keytype_exist:nT, \@@_execute_if_keytype_exist:VT} +% The test for valid keytypes looks for a function to set up the key, +% which is part of the \enquote{code} side of the template definition. +% This avoids having different lists for the two parts of the process. +% \begin{macrocode} +\cs_new_protected:Npn \@@_execute_if_keytype_exist:nT #1#2 + { + \cs_if_exist:cTF { @@_store_value_ #1 :n } + {#2} + { \msg_error:nnn { template } { unknown-keytype } {#1} } + } +\cs_generate_variant:Nn \@@_execute_if_keytype_exist:nT { V } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_execute_if_type_exist:nT} +% To check that a particular type is valid. +% \begin{macrocode} +\cs_new_protected:Npn \@@_execute_if_type_exist:nT #1#2 + { + \prop_if_in:NnTF \g_@@_type_prop {#1} + {#2} + { \msg_error:nnn { template } { unknown-type } {#1} } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_execute_if_keys_exist:nnT} +% To check that the keys for a template have been set up before trying +% to create any code, a simple check for the correctly-named keytype +% property list. +% \begin{macrocode} +\cs_new_protected:Npn \@@_if_keys_exist:nnT #1#2#3 + { + \cs_if_exist:cTF { \c_@@_keytypes_root_tl #1 / #2 } + {#3} + { \msg_error:nnnn { template } { unknown-template } {#1} {#2} } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[TF]{\@@_if_key_value:n, \@@_if_key_value:V} +% Tests for the first token in a string being \cs{KeyValue}. +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_key_value:n #1 { T , F , TF } + { + \str_if_eq:noTF { \KeyValue } { \tl_head:w #1 \q_nil \q_stop } + \prg_return_true: + \prg_return_false: + } +\prg_generate_conditional_variant:Nnn \@@_if_key_value:n { V } { T , F , TF } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[TF]{\@@_if_instance_exist:nn} +% Testing for an instance +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_instance_exist:nn #1#2 { T, F, TF } + { + \cs_if_exist:cTF { \c_@@_instances_root_tl #1 / #2 } + \prg_return_true: + \prg_return_false: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_if_use_template:nTF} +% Tests for the first token in a string being \cs{UseTemplate}. +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_use_template:n #1 { TF } + { + \str_if_eq:noTF { \UseTemplate } { \tl_head:w #1 \q_nil \q_stop } + \prg_return_true: + \prg_return_false: +} +% \end{macrocode} +% \end{macro} +% +% \subsection{Saving and recovering property lists} +% +% The various property lists for templates have to be shuffled in +% and out of storage. +% +% \begin{macro} +% { +% \@@_store_defaults:nn , +% \@@_store_keytypes:nn , +% \@@_store_values:nn , +% \@@_store_vars:nn +% } +% The defaults and keytypes are transferred from the scratch property +% lists to the \enquote{proper} lists for the template being created. +% \begin{macrocode} +\cs_new_protected:Npn \@@_store_defaults:nn #1#2 + { + \debug_suspend: + \prop_gclear_new:c { \c_@@_defaults_root_tl #1 / #2 } + \prop_gset_eq:cN { \c_@@_defaults_root_tl #1 / #2 } + \l_@@_values_prop + \debug_resume: + } +\cs_new_protected:Npn \@@_store_keytypes:nn #1#2 + { + \debug_suspend: + \prop_if_exist:cTF { \c_@@_keytypes_root_tl #1 / #2 } + { + \msg_info:nnnn { template } { declare-template-interface } {#1} {#2} + \prop_gclear:c { \c_@@_keytypes_root_tl #1 / #2 } + } + { \prop_new:c { \c_@@_keytypes_root_tl #1 / #2 } } + \prop_gset_eq:cN { \c_@@_keytypes_root_tl #1 / #2 } + \l_@@_keytypes_prop + \seq_gclear_new:c { \c_@@_key_order_root_tl #1 / #2 } + \seq_gset_eq:cN { \c_@@_key_order_root_tl #1 / #2 } + \l_@@_key_order_seq + \debug_resume: + } +\cs_new_protected:Npn \@@_store_values:nn #1#2 + { + \debug_suspend: + \prop_clear_new:c { \c_@@_values_root_tl #1 / #2 } + \prop_set_eq:cN { \c_@@_values_root_tl #1 / #2 } + \l_@@_values_prop + \debug_resume: + } +\cs_new_protected:Npn \@@_store_vars:nn #1#2 + { + \debug_suspend: + \prop_gclear_new:c { \c_@@_vars_root_tl #1 / #2 } + \prop_gset_eq:cN { \c_@@_vars_root_tl #1 / #2 } + \l_@@_vars_prop + \debug_resume: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \@@_recover_defaults:nn , +% \@@_recover_keytypes:nn , +% \@@_recover_values:nn , +% \@@_recover_vars:nn +% } +% Recovering the stored data for a template is rather less complex +% than storing it. All that happens is the data is transferred from +% the permanent to the scratch storage. However, we need to check the +% scratch storage does exist. +% \begin{macrocode} +\cs_new_protected:Npn \@@_recover_defaults:nn #1#2 + { + \prop_if_exist:cTF + { \c_@@_defaults_root_tl #1 / #2 } + { + \prop_set_eq:Nc \l_@@_values_prop + { \c_@@_defaults_root_tl #1 / #2 } + } + { \prop_clear:N \l_@@_values_prop } + } +\cs_new_protected:Npn \@@_recover_keytypes:nn #1#2 + { + \prop_if_exist:cTF + { \c_@@_keytypes_root_tl #1 / #2 } + { + \prop_set_eq:Nc \l_@@_keytypes_prop + { \c_@@_keytypes_root_tl #1 / #2 } + } + { \prop_clear:N \l_@@_keytypes_prop } + \seq_if_exist:cTF { \c_@@_key_order_root_tl #1 / #2 } + { + \seq_set_eq:Nc \l_@@_key_order_seq + { \c_@@_key_order_root_tl #1 / #2 } + } + { \seq_clear:N \l_@@_key_order_seq } + } +\cs_new_protected:Npn \@@_recover_values:nn #1#2 + { + \prop_if_exist:cTF + { \c_@@_values_root_tl #1 / #2 } + { + \prop_set_eq:Nc \l_@@_values_prop + { \c_@@_values_root_tl #1 / #2 } + } + { \prop_clear:N \l_@@_values_prop } + } +\cs_new_protected:Npn \@@_recover_vars:nn #1#2 + { + \prop_if_exist:cTF + { \c_@@_vars_root_tl #1 / #2 } + { + \prop_set_eq:Nc \l_@@_vars_prop + { \c_@@_vars_root_tl #1 / #2 } + } + { \prop_clear:N \l_@@_vars_prop } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Creating new template types} +% +% \begin{macro}{\@@_define_type:nn, \@@_declare_type:nn} +% Although the type is the \enquote{top level} of the template +% system, it is actually very easy to implement. All that happens is that +% the number of arguments required is recorded, indexed by the name of the +% type. +% \begin{macrocode} +\cs_new_protected:Npn \@@_define_type:nn #1#2 + { + \prop_if_in:NnTF \g_@@_type_prop {#1} + { \msg_error:nnn { template } { type-already-defined } {#1} } + { \@@_declare_type:nn {#1} {#2} } + } +\cs_new_protected:Npn \@@_declare_type:nn #1#2 + { + \int_set:Nn \l_@@_tmp_int {#2} + \int_compare:nTF { 0 <= \l_@@_tmp_int <= 9 } + { + \msg_info:nnnV { template } { declare-type } + {#1} \l_@@_tmp_int + \prop_gput:NnV \g_@@_type_prop {#1} + \l_@@_tmp_int + } + { + \msg_error:nnnV { template } { bad-number-of-arguments } + {#1} \l_@@_tmp_int + } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Design part of template declaration} +% +% The \enquote{design} part of a template declaration defines the general +% behaviour of each key, and possibly a default value. However, it does +% not include the implementation. This means that what happens here is +% the two properties are saved to appropriate lists, which can then +% be used later to recover the information when implementing the keys. +% +% \begin{macro}{\@@_declare_template_keys:nnnn} +% The main function for the \enquote{design} part of creating a template +% starts by checking that the type exists and that the number of +% arguments required agree. If that is all fine, then the two storage +% areas for defaults and keytypes are initialised. The mechanism is then +% set up for the \pkg{l3keys} module to actually parse the keys. +% Finally, the code hands of to the storage routine to save the parsed +% information properly. +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare_template_keys:nnnn #1#2#3#4 + { + \@@_execute_if_type_exist:nT {#1} + { + \@@_execute_if_arg_agree:nnT {#1} {#3} + { + \prop_clear:N \l_@@_values_prop + \prop_clear:N \l_@@_keytypes_prop + \seq_clear:N \l_@@_key_order_seq + \keyval_parse:NNn + \@@_parse_keys_elt:n \@@_parse_keys_elt:nn {#4} + \@@_store_defaults:nn {#1} {#2} + \@@_store_keytypes:nn {#1} {#2} + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_parse_keys_elt:n} +% \begin{macro}{\@@_parse_keys_elt_aux:n} +% \begin{macro}{\@@_parse_keys_elt_aux:} +% Processing the key part of the key--value pair is always carried out +% using this function, even if a value was found. First, the key name +% is separated from the keytype, and if necessary the keytype is +% separated into two parts. This information is then used to check that +% the keytype is valid, before storing the keytype (plus argument if +% necessary) as a property of the key name. The key name is also stored +% (in braces) in the token list to record the order the keys are defined +% in. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_keys_elt:n #1 + { + \@@_split_keytype:n {#1} + \bool_if:NF \l_@@_error_bool + { + \@@_execute_if_keytype_exist:VT \l_@@_keytype_tl + { + \seq_map_function:NN \c_@@_keytypes_arg_seq + \@@_parse_keys_elt_aux:n + \bool_if:NF \l_@@_error_bool + { + \seq_if_in:NoTF \l_@@_key_order_seq + \l_@@_key_name_tl + { + \msg_error:nnV { template } { duplicate-key-interface } + \l_@@_key_name_tl + } + { \@@_parse_keys_elt_aux: } + } + } + } + } +\cs_new_protected:Npn \@@_parse_keys_elt_aux:n #1 + { + \str_if_eq:VnT \l_@@_keytype_tl {#1} + { + \tl_if_empty:NT \l_@@_keytype_arg_tl + { + \msg_error:nnn { template } { keytype-requires-argument } {#1} + \bool_set_true:N \l_@@_error_bool + \seq_map_break: + } + } + } +\cs_new_protected:Npn \@@_parse_keys_elt_aux: + { + \tl_set:Ne \l_@@_tmp_tl + { + \l_@@_keytype_tl + \tl_if_empty:NF \l_@@_keytype_arg_tl + { { \l_@@_keytype_arg_tl } } + } + \prop_put:NVV \l_@@_keytypes_prop \l_@@_key_name_tl + \l_@@_tmp_tl + \seq_put_right:NV \l_@@_key_order_seq \l_@@_key_name_tl + \str_if_eq:VnT \l_@@_keytype_tl { choice } + { + \clist_if_in:NnT \l_@@_keytype_arg_tl { unknown } + { \msg_error:nn { template } { choice-unknown-reserved } } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_parse_keys_elt:nn} +% For keys which have a default, the keytype and key name are first +% separated out by the \cs{@@_parse_keys_elt:n} +% routine, before storing the default value in the scratch property list. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_keys_elt:nn #1#2 + { + \@@_parse_keys_elt:n {#1} + \use:c { @@_store_value_ \l_@@_keytype_tl :n } {#2} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_split_keytype:n} +% \begin{macro}{\@@_split_keytype_aux:w} +% The keytype and key name should be separated by |:|. As the +% definition might be given inside or outside of a code block, +% the category code of colons is standardised. After +% that, the standard delimited argument method is used to separate the +% two parts. +% \begin{macrocode} +\cs_new_protected:Npe \@@_split_keytype:n #1 + { + \exp_not:N \bool_set_false:N \exp_not:N \l_@@_error_bool + \tl_set:Nn \exp_not:N \l_@@_tmp_tl {#1} + \tl_replace_all:Nnn \exp_not:N \l_@@_tmp_tl { : } { \token_to_str:N : } + \tl_if_in:VnTF \exp_not:N \l_@@_tmp_tl { \token_to_str:N : } + { + \exp_not:n + { + \tl_clear:N \l_@@_key_name_tl + \exp_after:wN \@@_split_keytype_aux:w + \l_@@_tmp_tl \s_@@_stop + } + } + { + \exp_not:N \bool_set_true:N \exp_not:N \l_@@_error_bool + \msg_error:nnn { template } { missing-keytype } {#1} + } + } +\use:e + { + \cs_new_protected:Npn \exp_not:N \@@_split_keytype_aux:w + #1 \token_to_str:N : #2 \s_@@_stop + { + \tl_put_right:Ne \exp_not:N \l_@@_key_name_tl + { + \exp_not:N \tl_trim_spaces:e + { \exp_not:N \tl_to_str:n {#1} } + } + \tl_if_in:nnTF {#2} { \token_to_str:N : } + { + \tl_put_right:Nn \exp_not:N \l_@@_key_name_tl + { \token_to_str:N : } + \exp_not:N \@@_split_keytype_aux:w #2 \s_@@_stop + } + { + \exp_not:N \tl_if_empty:NTF \exp_not:N \l_@@_key_name_tl + { + \msg_error:nnn { template } { empty-key-name } + { \token_to_str:N : #2 } + } + { \exp_not:N \@@_split_keytype_arg:n {#2} } + } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_split_keytype_arg:n, \@@_split_keytype_arg:V} +% \begin{macro}{\@@_split_keytype_arg_aux:n} +% \begin{macro}{\@@_split_keytype_arg_aux:w} +% The second stage of sorting out the keytype is to check for an +% argument. As there is no convenient delimiting token to look for, +% a check is made instead for each possible text value for the keytype. +% To keep things faster, this only involves the keytypes that need an +% argument. If a match is made, then a check is also needed to see that +% it is at the start of the keytype information. All being well, the +% split can then be applied. Any non-matching keytypes are assumed to +% be \enquote{correct} as given, and are left alone (this is checked by +% other code). +% \begin{macrocode} +\cs_new_protected:Npn \@@_split_keytype_arg:n #1 + { + \tl_set:Ne \l_@@_keytype_tl { \tl_trim_spaces:n {#1} } + \tl_clear:N \l_@@_keytype_arg_tl + \cs_set_protected:Npn \@@_split_keytype_arg_aux:n ##1 + { + \tl_if_in:nnT {#1} {##1} + { + \cs_set:Npn \@@_split_keytype_arg_aux:w + ####1 ##1 ####2 \s_@@_stop + { + \tl_if_blank:nT {####1} + { + \tl_set:Ne \l_@@_keytype_tl + { \tl_trim_spaces:n {##1} } + \tl_if_blank:nF {####2} + { + \tl_set:Ne \l_@@_keytype_arg_tl + { \use:n ####2 } + } + \seq_map_break: + } + } + \@@_split_keytype_arg_aux:w #1 \s_@@_stop + } + } + \seq_map_function:NN \c_@@_keytypes_arg_seq + \@@_split_keytype_arg_aux:n + } +\cs_generate_variant:Nn \@@_split_keytype_arg:n { V } +\cs_new:Npn \@@_split_keytype_arg_aux:n #1 { } +\cs_new:Npn \@@_split_keytype_arg_aux:w #1 \s_@@_stop { } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsubsection{Storing values} +% +% As \pkg{lttemplates} pre-processes key values for efficiency reasons, +% there is a need to convert the values given as defaults into +% \enquote{ready to use} data. The same general idea is true when an instance +% is declared. However, assignments are not made until an instance is +% used, and so there has to be some intermediate storage. Furthermore, +% the ability to delay evaluation of results is needed. To achieve these +% aims, a series of \enquote{process and store} functions are defined here. +% +% All of the information about the key (the key name and the keytype) +% is already stored as variables. The same property list is always used +% to store the data, meaning that the only argument required is the +% value to be processed and potentially stored. +% +% \begin{macro}{\@@_store_value_boolean:n} +% \begin{macrocode} +\cs_new_protected:Npn \@@_store_value_boolean:n #1 + { \prop_put:Non \l_@@_values_prop \l_@@_key_name_tl {#1} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_store_value:n, \@@_store_value_choice:n, +% \@@_store_value_function:n, \@@_store_value_instance:n} +% With no need to worry about delayed evaluation, these keytypes all +% just store the input directly. +% \begin{macrocode} +\cs_new_protected:Npn \@@_store_value:n #1 + { \prop_put:Non \l_@@_values_prop \l_@@_key_name_tl {#1} } +\cs_new_eq:NN \@@_store_value_choice:n \@@_store_value:n +\cs_new_eq:NN \@@_store_value_function:n \@@_store_value:n +\cs_new_eq:NN \@@_store_value_instance:n \@@_store_value:n +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_store_value_aux:Nn, \@@_store_value_integer:n, +% \@@_store_value_length:n, \@@_store_value_muskip:n, +% \@@_store_value_real:n, \@@_store_value_skip:n, +% \@@_store_value_tokenlist:n, \@@_store_value_commalist:n} +% Storing values in \cs{l_@@_values_prop} is in most cases the same. +% \begin{macrocode} +\cs_new_protected:Npn \@@_store_value_aux:Nn #1#2 + { \prop_put:Non \l_@@_values_prop \l_@@_key_name_tl {#2} } +\cs_new_protected:Npn \@@_store_value_integer:n + { \@@_store_value_aux:Nn \int_eval:n } +\cs_new_protected:Npn \@@_store_value_length:n + { \@@_store_value_aux:Nn \dim_eval:n } +\cs_new_protected:Npn \@@_store_value_muskip:n + { \@@_store_value_aux:Nn \muskip_eval:n } +\cs_new_protected:Npn \@@_store_value_real:n + { \@@_store_value_aux:Nn \fp_eval:n } +\cs_new_protected:Npn \@@_store_value_skip:n + { \@@_store_value_aux:Nn \skip_eval:n } +\cs_new_protected:Npn \@@_store_value_tokenlist:n + { \@@_store_value_aux:Nn \use:n } +\cs_new_eq:NN \@@_store_value_commalist:n \@@_store_value_tokenlist:n +% \end{macrocode} +% \end{macro} +% +% \subsection{Implementation part of template declaration} +% +% \begin{macro}{\@@_declare_template_code:nnnnn} +% \begin{macro}{\@@_declare_template_code:nnnn} +% The main function for implementing a template starts with a couple of +% simple checks to make sure that there are no obvious mistakes: the +% number of arguments must agree and the template keys must have been +% declared. +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare_template_code:nnnnn #1#2#3#4#5 + { + \@@_execute_if_type_exist:nT {#1} + { + \@@_execute_if_arg_agree:nnT {#1} {#3} + { + \@@_if_keys_exist:nnT {#1} {#2} + { + \@@_store_key_implementation:nnn {#1} {#2} {#4} + \regex_match:nnTF { \c { AssignTemplateKeys } } {#5} + { \@@_declare_template_code:nnnn {#1} {#2} {#3} {#5} } + { + \@@_declare_template_code:nnnn + {#1} {#2} {#3} { \AssignTemplateKeys #5 } + } + } + } + } + } +\cs_new_protected:Npn \@@_declare_template_code:nnnn #1#2#3#4 + { + \cs_if_exist:cT { \c_@@_code_root_tl #1 / #2 } + { \msg_info:nnnn { template } { declare-template-code } {#1} {#2} } + \cs_generate_from_arg_count:cNnn + { \c_@@_code_root_tl #1 / #2 } + \cs_gset_protected:Npn {#3} {#4} + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_store_key_implementation:nnn} +% Actually storing the implementation part of a template is quite easy +% as it only requires the list of keys given to be turned into a +% property list. There is also some error-checking to do, hence the need +% to have the list of defined keytypes available. In certain cases +% (when choices are involved) parsing the key results in changes to the +% default values. That is why they are loaded and then saved again. +% \begin{macrocode} +\cs_new_protected:Npn \@@_store_key_implementation:nnn #1#2#3 + { + \@@_recover_defaults:nn {#1} {#2} + \@@_recover_keytypes:nn {#1} {#2} + \prop_clear:N \l_@@_vars_prop + \keyval_parse:nnn + { \@@_parse_vars_elt:n } { \@@_parse_vars_elt:nnn { #1 / #2 } } {#3} + \@@_store_vars:nn {#1} {#2} + \prop_map_inline:Nn \l_@@_keytypes_prop + { \msg_error:nnnnn { template } { key-not-implemented } {##1} {#2} {#1} } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_parse_vars_elt:n} +% At the implementation stage, every key must have a value given. So +% this is an error function. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_vars_elt:n #1 + { \msg_error:nnn { template } { key-no-variable } {#1} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_parse_vars_elt:nnn} +% \begin{macro}{\@@_parse_vars_elt_aux:nn} +% \begin{macro}{\@@_parse_vars_elt_aux:nw} +% \begin{macro}{\@@_parse_vars_elt_aux:nnn, \@@_parse_vars_elt_aux:nne} +% \begin{macro}{\@@_parse_vars_elt_key:nn} +% The actual storage part here is very simple: the storage bin name +% is placed into the property list. At the same time, a comparison is +% made with the keytypes defined earlier: if there is a mismatch then +% an error is raised. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_vars_elt:nnn #1#2#3 + { + \tl_set:Ne \l_@@_key_name_tl + { \tl_trim_spaces:e { \tl_to_str:n {#2} } } + \prop_get:NVNTF \l_@@_keytypes_prop + \l_@@_key_name_tl + \l_@@_keytype_tl + { + \@@_split_keytype_arg:V \l_@@_keytype_tl + \@@_parse_vars_elt_aux:nn {#1} {#3} + \prop_remove:NV \l_@@_keytypes_prop \l_@@_key_name_tl + } + { \msg_error:nnn { template } { unknown-key } {#2} } + } +% \end{macrocode} +% Split off any leading \texttt{global} and they look for the way to +% implement. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_vars_elt_aux:nn #1#2 + { + \@@_parse_vars_elt_aux:nw {#1} #2 global global \s_@@_stop + } +\cs_new_protected:Npn \@@_parse_vars_elt_aux:nw + #1#2 global #3 global #4 \s_@@_stop + { + \tl_if_blank:nTF {#4} + { \@@_parse_vars_elt_aux:nnn {#1} { } {#2} } + { + \tl_if_blank:nTF {#2} + { + \@@_parse_vars_elt_aux:nne + {#1} { global } { \tl_trim_spaces:n {#3} } + } + { \msg_error:nnn { template } { bad-variable } { #2 global #3 } } + } + } +\cs_new_protected:Npn \@@_parse_vars_elt_aux:nnn #1#2#3 + { + \str_case:VnF \l_@@_keytype_tl + { + { choice } { \@@_implement_choices:nn {#1} {#3} } + { function } + { + \cs_if_exist:NF #3 + { \cs_new:Npn #3 { } } + \@@_parse_vars_elt_key:nn {#1} + { + .code:n = + { + \cs_generate_from_arg_count:NNnn + \exp_not:N #3 + \exp_not:c + { cs_ \str_if_eq:nnT {#1} { global } { g } set:Npn } + { \exp_not:V \l_@@_keytype_arg_tl } + {##1} + } + } + \prop_put:NVn \l_@@_vars_prop + \l_@@_key_name_tl {#2#3} + } + { instance } + { + \@@_parse_vars_elt_key:nn {#1} + { + .code:n = + { + \exp_not:c + { cs_ \str_if_eq:nnT {#1} { global } { g } set:Npn } + \exp_not:N #3 { \UseInstance {##1} } + } + } + \prop_put:NVn \l_@@_vars_prop + \l_@@_key_name_tl {#2#3} + } + } + { + \tl_if_single:nTF {#3} + { + \cs_if_exist:NF #3 + { \use:c { \@@_map_var_type: _new:N } #3 } + \@@_parse_vars_elt_key:nn {#1} + { + . \@@_map_var_type: + _ \str_if_eq:nnT {#1} { global } { g } set:N + = \exp_not:N #3 + } + \prop_put:NVn \l_@@_vars_prop + \l_@@_key_name_tl {#2#3} + } + { \msg_error:nnn { template } { bad-variable } {#2#3} } + } + } +\cs_generate_variant:Nn \@@_parse_vars_elt_aux:nnn { nne } +\cs_new_protected:Npn \@@_parse_vars_elt_key:nn #1#2 + { + \keys_define:ne { template / #1 } + { \l_@@_key_name_tl #2 } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_map_var_type:} +% Turn a \enquote{friendly} variable type into an \texttt{expl3} one. +% \begin{macrocode} +\cs_new:Npn \@@_map_var_type: + { + \str_case:Vn \l_@@_keytype_tl + { + { boolean } { bool } + { commalist } { clist } + { integer } { int } + { length } { dim } + { muskip } { muskip } + { real } { fp } + { skip } { skip } + { tokenlist } { tl } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_implement_choices:nn} +% \begin{macro}{\@@_implement_choices_default:} +% Implementing choices requires a second key--value loop. So after a +% little set-up, the standard parser is called. +% \begin{macrocode} +\cs_new_protected:Npn \@@_implement_choices:nn #1#2 + { + \clist_set:NV \l_@@_tmp_clist \l_@@_keytype_arg_tl + \prop_put:NVn \l_@@_vars_prop \l_@@_key_name_tl { } + \keys_define:ne { template / #1 } { \l_@@_key_name_tl .choice: } + \keyval_parse:nnn + { \@@_implement_choice_elt:n } + { \@@_implement_choice_elt:nnn {#1} } + {#2} + \prop_get:NVNT \l_@@_values_prop \l_@@_key_name_tl + \l_@@_tmp_tl + { \@@_implement_choices_default: } + \clist_if_empty:NF \l_@@_tmp_clist + { + \clist_map_inline:Nn \l_@@_tmp_clist + { \msg_error:nnn { template } { choice-not-implemented } {##1} } + } + } +% \end{macrocode} +% A sanity check for the default value, so that an error is raised +% now and not when converting to assignments. +% \begin{macrocode} +\cs_new_protected:Npn \@@_implement_choices_default: + { + \tl_set:Ne \l_@@_tmp_tl + { \l_@@_key_name_tl \c_space_tl \l_@@_tmp_tl } + \prop_if_in:NVF \l_@@_vars_prop \l_@@_tmp_tl + { + \tl_set:Ne \l_@@_tmp_tl + { \l_@@_key_name_tl \c_space_tl \l_@@_tmp_tl } + \prop_if_in:NVF \l_@@_vars_prop \l_@@_tmp_tl + { + \prop_get:NVN \l_@@_keytypes_prop \l_@@_key_name_tl + \l_@@_tmp_tl + \@@_split_keytype_arg:V \l_@@_tmp_tl + \prop_get:NVN \l_@@_values_prop \l_@@_key_name_tl + \l_@@_tmp_tl + \msg_error:nnVV { template } { unknown-default-choice } + \l_@@_key_name_tl + \l_@@_key_name_tl + } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_implement_choice_elt:nnn, \@@_implement_choice_elt_aux:nnn} +% \begin{macro}{\@@_implement_choice_elt_aux:n} +% \begin{macro}{\@@_implement_choice_elt:n} +% The actual storage of the implementation of a choice is mainly about +% error checking. The code here ensures that all choices have to have +% been declared, apart from the special \texttt{unknown} choice, which +% must come last. The code for each choice is stored along with the +% key name in the variables property list. +% \begin{macrocode} +\cs_new_protected:Npn \@@_implement_choice_elt:nnn #1#2#3 + { + \clist_if_empty:NTF \l_@@_tmp_clist + { + \str_if_eq:nnTF {#2} { unknown } + { \@@_implement_choice_elt_aux:nnn {#1} {#2} {#3} } + { \@@_implement_choice_elt_aux:n {#2} } + } + { + \clist_if_in:NnTF \l_@@_tmp_clist {#2} + { + \clist_remove_all:Nn \l_@@_tmp_clist {#2} + \@@_implement_choice_elt_aux:nnn {#1} {#2} {#3} + } + { \@@_implement_choice_elt_aux:n {#2} } + } + } +\cs_new_protected:Npn \@@_implement_choice_elt_aux:n #1 + { + \prop_get:NVN \l_@@_keytypes_prop \l_@@_key_name_tl + \l_@@_tmp_tl + \@@_split_keytype_arg:V \l_@@_tmp_tl + \msg_error:nnVn { template } { unknown-choice } \l_@@_key_name_tl {#1} + } +\cs_new_protected:Npn \@@_implement_choice_elt_aux:nnn #1#2#3 + { + \keys_define:ne { template / #1 } + { \l_@@_key_name_tl / #2 .code:n = { \exp_not:n {#3} } } + \tl_set:Ne \l_@@_tmp_tl + { \l_@@_key_name_tl \c_space_tl #2 } + \prop_put:NVn \l_@@_vars_prop \l_@@_tmp_tl {#3} + } +\cs_new_protected:Npn \@@_implement_choice_elt:n #1 + { + \msg_error:nnVn { template } { choice-requires-code } + \l_@@_key_name_tl {#1} + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Editing template defaults} +% +% \begin{macro}{\@@_edit_defaults:nnn} +% Editing the template defaults means getting the values back out +% of the store, then parsing the list of new values before putting +% the updated list back into storage. +% \begin{macrocode} +\cs_new_protected:Npn \@@_edit_defaults:nnn #1#2#3 + { + \@@_if_keys_exist:nnT {#1} {#2} + { + \@@_recover_defaults:nn {#1} {#2} + \@@_parse_values:nnn {#1} {#2} {#3} + \@@_store_defaults:nn {#1} {#2} + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_parse_values:nnn} +% The routine to parse values is the same for both editing a +% template and setting up an instance. So the code here does only the +% minimum necessary for reading the values. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_values:nnn #1#2#3 + { + \@@_recover_keytypes:nn {#1} {#2} + \keyval_parse:NNn + \@@_parse_values_elt:n \@@_parse_values_elt:nn {#3} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_parse_values_elt:n} +% Every key needs a value, so this is just an error routine. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_values_elt:n #1 + { + \bool_set_true:N \l_@@_error_bool + \msg_error:nnn { template } { key-no-value } {#1} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_parse_values_elt:nn} +% \begin{macro}{\@@_parse_values_elt_aux:n} +% To store the value, find the keytype then call the saving function. +% These need the current key name saved as \cs{l_@@_key_name_tl}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_values_elt:nn #1#2 + { + \tl_set:Ne \l_@@_key_name_tl + { \tl_trim_spaces:e { \tl_to_str:n {#1} } } + \prop_get:NVNTF \l_@@_keytypes_prop \l_@@_key_name_tl + \l_@@_tmp_tl + { \@@_parse_values_elt_aux:n {#2} } + { \msg_error:nnV { template } { unknown-key } \l_@@_key_name_tl } + } +\cs_new_protected:Npn \@@_parse_values_elt_aux:n #1 + { + \@@_split_keytype_arg:V \l_@@_tmp_tl + \use:c { @@_store_value_ \l_@@_keytype_tl :n } {#1} + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_template_set_eq:nnn} +% To copy a template, each of the lists plus the code has to be copied +% across. To keep this independent of the list storage system, it is +% all done with two-part shuffles. +% \begin{macrocode} +\cs_new_protected:Npn \@@_template_set_eq:nnn #1#2#3 + { + \@@_recover_defaults:nn {#1} {#3} + \@@_store_defaults:nn {#1} {#2} + \@@_recover_keytypes:nn {#1} {#3} + \@@_store_keytypes:nn {#1} {#2} + \@@_recover_vars:nn {#1} {#3} + \@@_store_vars:nn {#1} {#2} + \cs_if_exist:cT { \c_@@_code_root_tl #1 / #2 } + { \msg_info:nnnn { template } { declare-template-code } {#1} {#2} } + \cs_gset_eq:cc { \c_@@_code_root_tl #1 / #2 } + { \c_@@_code_root_tl #1 / #3 } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Creating instances of templates} +% +% \begin{macro} +% { +% \@@_declare_instance:nnnn, +% \@@_declare_instance_aux:nnnn +% } +% Making an instance has two distinct parts. First, the keys given are +% parsed to transfer the values into the structured data format used +% internally. This allows the default and given values to be combined +% with no repetition. In the second step, the structured data is +% converted to pre-defined variable assignments, and these are stored +% in the function for the instance. +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare_instance:nnnn #1#2#3#4 + { + \@@_execute_if_code_exist:nnT {#1} {#2} + { + \@@_recover_defaults:nn {#1} {#2} + \@@_recover_vars:nn {#1} {#2} + \@@_declare_instance_aux:nnnn {#1} {#2} {#3} {#4} + } + } +\cs_new_protected:Npn \@@_declare_instance_aux:nnnn #1#2#3#4 + { + \bool_set_false:N \l_@@_error_bool + \@@_parse_values:nnn {#1} {#2} {#4} + \bool_if:NF \l_@@_error_bool + { + \prop_put:Nnn \l_@@_values_prop { from~template } {#2} + \@@_store_values:nn {#1} {#3} + \@@_convert_to_assignments: + \cs_if_exist:cT { \c_@@_instances_root_tl #1 / #3 } + { \msg_info:nnnn { template } { declare-instance } {#3} {#1} } + \cs_set_protected:cpe { \c_@@_instances_root_tl #1 / #3 } + { + \exp_not:N \@@_assignments_push:n + { \exp_not:V \l_@@_assignments_tl } + \exp_not:c { \c_@@_code_root_tl #1 / #2 } + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_instance_set_eq:nnn} +% Copy--paste an instance. +% \begin{macrocode} +\cs_new_protected:Npn \@@_instance_set_eq:nnn #1#2#3 + { + \@@_if_instance_exist:nnTF {#1} {#3} + { + \@@_recover_values:nn {#1} {#3} + \@@_store_values:nn {#1} {#2} + \cs_if_exist:cT { \c_@@_instances_root_tl #1 / #2 } + { \msg_info:nnnn { template } { declare-instance } {#2} {#1} } + \cs_set_eq:cc { \c_@@_instances_root_tl #1 / #2 } + { \c_@@_instances_root_tl #1 / #3 } + } + { \msg_error:nnnn { template } { unknown-instance } {#1} {#3} } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_edit_instance:nnn} +% \begin{macro} +% {\@@_edit_instance_aux:nnnnn, \@@_edit_instance_aux:nVnnn} +% Editing an instance is almost identical to declaring one. The only +% variation is the source of the values to use. When editing, they are +% recovered from the previous instance run. +% \begin{macrocode} +\cs_new_protected:Npn \@@_edit_instance:nnn #1#2#3 + { + \@@_if_instance_exist:nnTF {#1} {#2} + { + \@@_recover_values:nn {#1} {#2} + \prop_get:NnN \l_@@_values_prop { from~template } + \l_@@_tmp_tl + \@@_edit_instance_aux:nVnn + {#1} \l_@@_tmp_tl {#2} {#3} + } + { \msg_error:nnnn { template } { unknown-instance } {#1} {#2} } + } +\cs_new_protected:Npn \@@_edit_instance_aux:nnnn #1#2#3#4 + { + \@@_recover_vars:nn {#1} {#2} + \@@_declare_instance_aux:nnnn {#1} {#2} {#3} {#4} + } +\cs_generate_variant:Nn \@@_edit_instance_aux:nnnn { nV } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_convert_to_assignments:} +% \begin{macro}{\@@_convert_to_assignments_aux:n} +% \begin{macro} +% {\@@_convert_to_assignments_aux:nn, \@@_convert_to_assignments_aux:nV} +% The idea on converting to a set of assignments is to loop over each +% key, so that the loop order follows the declaration order of the keys. +% This is done using a sequence as property lists are not +% \enquote{ordered}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_convert_to_assignments: + { + \tl_clear:N \l_@@_assignments_tl + \seq_map_function:NN \l_@@_key_order_seq + \@@_convert_to_assignments_aux:n + } +\cs_new_protected:Npn \@@_convert_to_assignments_aux:n #1 + { + \prop_get:NnN \l_@@_keytypes_prop {#1} \l_@@_tmp_tl + \@@_convert_to_assignments_aux:nV {#1} \l_@@_tmp_tl + } +% \end{macrocode} +% The second auxiliary function actually does the work. The +% arguments here are the key name (|#1|) and the keytype (|#2|). +% From those, the value to assign and the name of the appropriate +% variable are recovered. A bit of work is then needed to sort out +% keytypes with arguments (for example instances), and to look for +% global assignments. Once that is done, a hand-off can be made to the +% handler for the relevant keytype. +% \begin{macrocode} +\cs_new_protected:Npn \@@_convert_to_assignments_aux:nn #1#2 + { + \prop_get:NnNT \l_@@_values_prop {#1} \l_@@_value_tl + { + \prop_get:NnNTF \l_@@_vars_prop {#1} \l_@@_var_tl + { + \@@_split_keytype_arg:n {#2} + \str_if_eq:VnF \l_@@_keytype_tl { choice } + { + \str_if_eq:VnF \l_@@_keytype_tl { code } + { \@@_find_global: } + } + \tl_set:Nn \l_@@_key_name_tl {#1} + \use:c { @@_assign_ \l_@@_keytype_tl : } + } + { \msg_error:nnn { template } { unknown-attribute } {#1} } + } + } +\cs_generate_variant:Nn \@@_convert_to_assignments_aux:nn { nV } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_find_global:} +% \begin{macro}{\@@_find_global_aux:w} +% Global assignments should have the phrase |global| at the front. +% This is pretty easy to find: no other error checking, though. +% \begin{macrocode} +\cs_new_protected:Npn \@@_find_global: + { + \bool_set_false:N \l_@@_global_bool + \tl_if_in:onT \l_@@_var_tl { global } + { + \exp_after:wN \@@_find_global_aux:w \l_@@_var_tl \s_@@_stop + } + } +\cs_new_protected:Npn \@@_find_global_aux:w #1 global #2 \s_@@_stop + { + \tl_set:Nn \l_@@_var_tl {#2} + \bool_set_true:N \l_@@_global_bool + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Using templates directly} +% +% \begin{macro}{\@@_use_template:nnn} +% Directly use a template with a particular parameter setting. +% This is also picked up if used in a nested fashion inside a parameter +% list. The idea is essentially the same as creating an instance, +% just with no saving of the result. +% \begin{macrocode} +\cs_new_protected:Npn \@@_use_template:nnn #1#2#3 + { + \@@_execute_if_code_exist:nnT {#1} {#2} + { + \@@_recover_defaults:nn {#1} {#2} + \@@_recover_vars:nn {#1} {#2} + \@@_parse_values:nnn {#1} {#2} {#3} + \@@_convert_to_assignments: + \use:c { \c_@@_code_root_tl #1 / #2 } + } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Assigning values to variables} +% +% \begin{macro}{\@@_assign_boolean:} +% \begin{macro}{\@@_assign_boolean_aux:n} +% Setting a Boolean value is slightly different to everything else +% as the value can be used to work out which \texttt{set} function to +% call. As long as there is no need to recover things from another +% variable, everything is pretty easy. If there is, then we need to allow +% for the fact that the recovered value here will \emph{not} be expandable, +% so needs to be converted to something that is. +% \begin{macrocode} +\cs_new_protected:Npn \@@_assign_boolean: + { + \bool_if:NTF \l_@@_global_bool + { \@@_assign_boolean_aux:n { bool_gset } } + { \@@_assign_boolean_aux:n { bool_set } } + } +\cs_new_protected:Npn \@@_assign_boolean_aux:n #1 + { + \@@_if_key_value:VTF \l_@@_value_tl + { + \@@_key_to_value: + \tl_put_right:Ne \l_@@_assignments_tl + { + \exp_not:c { #1 _eq:NN } + \exp_not:V \l_@@_var_tl + \exp_not:V \l_@@_value_tl + } + } + { + \tl_put_right:Ne \l_@@_assignments_tl + { + \exp_not:c { #1 _ \l_@@_value_tl :N } + \exp_not:V \l_@@_var_tl + } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_assign_choice:} +% \begin{macro} +% {\@@_assign_choice_aux:nF, \@@_assign_choice_aux:eF} +% The idea here is to find either the choice as-given or else the +% special |unknown| choice, and to copy the appropriate code across. +% \begin{macrocode} +\cs_new_protected:Npn \@@_assign_choice: + { + \@@_assign_choice_aux:eF + { \l_@@_key_name_tl \c_space_tl \l_@@_value_tl } + { + \@@_assign_choice_aux:eF + { \l_@@_key_name_tl \c_space_tl unknown } + { + \prop_get:NVN \l_@@_keytypes_prop \l_@@_key_name_tl + \l_@@_tmp_tl + \@@_split_keytype_arg:V \l_@@_tmp_tl + \msg_error:nnVV { template } { unknown-choice } + \l_@@_key_name_tl + \l_@@_value_tl + } + } + } +\cs_new_protected:Npn \@@_assign_choice_aux:nF #1 + { + \prop_get:NnNTF \l_@@_vars_prop {#1} \l_@@_tmp_tl + { \tl_put_right:NV \l_@@_assignments_tl \l_@@_tmp_tl } + } +\cs_generate_variant:Nn \@@_assign_choice_aux:nF { e } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_assign_function:} +% \begin{macro}{\@@_assign_function_aux:N} +% This looks a bit messy but is only actually one function. +% \begin{macrocode} +\cs_new_protected:Npn \@@_assign_function: + { + \bool_if:NTF \l_@@_global_bool + { \@@_assign_function_aux:N \cs_gset:Npn } + { \@@_assign_function_aux:N \cs_set:Npn } + } +\cs_new_protected:Npn \@@_assign_function_aux:N #1 + { + \tl_put_right:Ne \l_@@_assignments_tl + { + \cs_generate_from_arg_count:NNnn + \exp_not:V \l_@@_var_tl + \exp_not:N #1 + { \exp_not:V \l_@@_keytype_arg_tl } + { \exp_not:V \l_@@_value_tl } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_assign_instance:} +% \begin{macro}{\@@_assign_instance_aux:N} +% Using an instance means adding the appropriate function creation to +% the tl. No checks are made at this stage, so if the instance is +% not valid then errors will arise later. +% \begin{macrocode} +\cs_new_protected:Npn \@@_assign_instance: + { + \bool_if:NTF \l_@@_global_bool + { \@@_assign_instance_aux:N \cs_gset_protected:Npn } + { \@@_assign_instance_aux:N \cs_set_protected:Npn } + } +\cs_new_protected:Npn \@@_assign_instance_aux:N #1 + { + \tl_put_right:Ne \l_@@_assignments_tl + { + \exp_not:N #1 \exp_not:V \l_@@_var_tl + { + \@@_use_instance:nn + { \exp_not:V \l_@@_keytype_arg_tl } + { \exp_not:V \l_@@_value_tl } + } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_assign_integer:} +% \begin{macro}{\@@_assign_length:} +% \begin{macro}{\@@_assign_muskip:} +% \begin{macro}{\@@_assign_real:} +% \begin{macro}{\@@_assign_skip:} +% All of the calculated assignments use the same underlying code, with +% only the low-level assignment function changing. +% \begin{macrocode} +\cs_new_protected:Npn \@@_assign_integer: + { + \bool_if:NTF \l_@@_global_bool + { \@@_assign_variable:N \int_gset:Nn } + { \@@_assign_variable:N \int_set:Nn } + } +\cs_new_protected:Npn \@@_assign_length: + { + \bool_if:NTF \l_@@_global_bool + { \@@_assign_variable:N \dim_gset:Nn } + { \@@_assign_variable:N \dim_set:Nn } +} +\cs_new_protected:Npn \@@_assign_muskip: + { + \bool_if:NTF \l_@@_global_bool + { \@@_assign_variable:N \muskip_gset:Nn } + { \@@_assign_variable:N \muskip_set:Nn } + } +\cs_new_protected:Npn \@@_assign_real: + { + \bool_if:NTF \l_@@_global_bool + { \@@_assign_variable:N \fp_gset:Nn } + { \@@_assign_variable:N \fp_set:Nn } + } +\cs_new_protected:Npn \@@_assign_skip: + { + \bool_if:NTF \l_@@_global_bool + { \@@_assign_variable:N \skip_gset:Nn } + { \@@_assign_variable:N \skip_set:Nn } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_assign_tokenlist:} +% \begin{macro}{\@@_assign_tokenlist_aux:NN} +% Life would be easy here if it were not for \cs{KeyValue}. To deal +% correctly with that, we need to allow for the recovery a stored value +% at point of use. +% \begin{macrocode} +\cs_new_protected:Npn \@@_assign_tokenlist: + { + \bool_if:NTF \l_@@_global_bool + { \@@_assign_tokenlist_aux:NN \tl_gset:NV \tl_gset:Nn } + { \@@_assign_tokenlist_aux:NN \tl_set:NV \tl_set:Nn } + } +\cs_new_protected:Npn \@@_assign_tokenlist_aux:NN #1#2 + { + \@@_if_key_value:VTF \l_@@_value_tl + { + \@@_key_to_value: + \tl_put_right:Ne \l_@@_assignments_tl + { + #1 \exp_not:V \l_@@_var_tl + { \exp_not:V \l_@@_value_tl } + } + } + { + \tl_put_right:Ne \l_@@_assignments_tl + { + #2 \exp_not:V \l_@@_var_tl + { \exp_not:V \l_@@_value_tl } + } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_assign_commalist:} +% Very similar for commas lists, so some code is shared. +% \begin{macrocode} +\cs_new_protected:Npn \@@_assign_commalist: + { + \bool_if:NTF \l_@@_global_bool + { \@@_assign_tokenlist_aux:NN \clist_gset:NV \clist_gset:Nn } + { \@@_assign_tokenlist_aux:NN \clist_set:NV \clist_set:Nn } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_assign_variable:N} +% A general-purpose function for all of the numerical assignments. +% As long as the value is not coming from another variable, the stored +% value is simply transferred for output. +% \begin{macrocode} +\cs_new_protected:Npn \@@_assign_variable:N #1 + { + \@@_if_key_value:VT \l_@@_value_tl + { \@@_key_to_value: } + \tl_put_right:Ne \l_@@_assignments_tl + { + #1 \exp_not:V \l_@@_var_tl + { \exp_not:V \l_@@_value_tl } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_key_to_value:} +% \begin{macro}{\@@_key_to_value_auxi:w} +% \begin{macro}{\@@_key_to_value_auxii:w} +% The idea here is to recover the attribute value of another key. To +% do that, the marker is removed and a look up takes place. If this +% is successful, then the name of the variable of the attribute is +% returned. This assumes that the value will be used in context where +% it will be converted to a value, for example when setting a number. +% There is also a need to check in case the copied value happens to be +% \texttt{global}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_key_to_value: + { \exp_after:wN \@@_key_to_value_auxi:w \l_@@_value_tl } +\cs_new_protected:Npn \@@_key_to_value_auxi:w \KeyValue #1 + { + \tl_set:Ne \l_@@_tmp_tl { \tl_trim_spaces:e { \tl_to_str:n {#1} } } + \prop_get:NVNTF \l_@@_vars_prop \l_@@_tmp_tl + \l_@@_value_tl + { + \exp_after:wN \@@_key_to_value_auxii:w \l_@@_value_tl + \s_@@_mark global \q_@@_nil \s_@@_stop + } + { \msg_error:nnV { template } { unknown-attribute } \l_@@_tmp_tl } + } +\cs_new_protected:Npn \@@_key_to_value_auxii:w #1 global #2#3 \s_@@_stop + { + \@@_quark_if_nil:NF #2 + { \tl_set:Nn \l_@@_value_tl {#2} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Using instances} +% +% \begin{macro}{\@@_use_instance:nn} +% \begin{macro}{\@@_use_instance_aux:nNnnn} +% \begin{macro}{\@@_use_instance_aux:nn} +% Using an instance is just a question of finding the appropriate function. +% If nothing is found, an error is raised. One complication is that +% if the first token of argument |#2| is \cs{UseTemplate} then that +% is also valid. There is an error-test to make sure that the +% types agree, and if so the template is used directly. +% \begin{macrocode} +\cs_new_protected:Npn \@@_use_instance:nn #1#2 + { + \@@_if_use_template:nTF {#2} + { \@@_use_instance_aux:nNnnn {#1} #2 } + { \@@_use_instance_aux:nn {#1} {#2} } + } +\cs_new_protected:Npn \@@_use_instance_aux:nNnnn #1#2#3#4#5 + { + \str_if_eq:nnTF {#1} {#3} + { \@@_use_template:nnn {#3} {#4} {#5} } + { \msg_error:nnnn { template } { type-mismatch } {#1} {#3} } +} +\cs_new_protected:Npn \@@_use_instance_aux:nn #1#2 + { + \@@_if_instance_exist:nnTF {#1} {#2} + { \use:c { \c_@@_instances_root_tl #1 / #2 } } + { \msg_error:nnnn { template } { unknown-instance } {#1} {#2} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +%\subsection{Assignment manipulation} +% +% A few functions to transfer assignments about, as this is needed by +% \cs{AssignTemplateKeys}. +% +% \begin{macro}{\@@_assignments_pop:} +% To actually use the assignments. +% \begin{macrocode} +\cs_new:Npn \@@_assignments_pop: { \l_@@_assignments_tl } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_assignments_push:n} +% Here, the assignments are stored for later use. +% \begin{macrocode} +\cs_new_protected:Npn \@@_assignments_push:n #1 + { \tl_set:Nn \l_@@_assignments_tl {#1} } +% \end{macrocode} +% \end{macro} +% +% \subsection{Showing templates and instances} +% +% \begin{macro}{\@@_show_code:nn} +% Showing the code for a template is just a translation of +% \cs{cs_show:c}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_show_code:nn #1#2 + { \cs_show:c { \c_@@_code_root_tl #1 / #2 } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \@@_show_defaults:nn, +% \@@_show_keytypes:nn, +% \@@_show_vars:nn +% } +% \begin{macro}{\@@_show:Nnnn} +% A modified version of the property-list printing code, such that +% the output refers to templates and instances rather than to the +% underlying structures. +% \begin{macrocode} +\cs_new_protected:Npn \@@_show_defaults:nn #1#2 + { + \@@_if_keys_exist:nnT {#1} {#2} + { + \@@_recover_defaults:nn {#1} {#2} + \@@_show:Nnnn \l_@@_values_prop + {#1} {#2} { default~values } + } + } +\cs_new_protected:Npn \@@_show_keytypes:nn #1#2 + { + \@@_if_keys_exist:nnT {#1} {#2} + { + \@@_recover_keytypes:nn {#1} {#2} + \@@_show:Nnnn \l_@@_keytypes_prop + {#1} {#2} { interface } + } + } +\cs_new_protected:Npn \@@_show_vars:nn #1#2 + { + \@@_execute_if_code_exist:nnT {#1} {#2} + { + \@@_recover_vars:nn {#1} {#2} + \@@_show:Nnnn \l_@@_vars_prop + {#1} {#2} { variable~mapping } + } + } +\cs_new_protected:Npn \@@_show:Nnnn #1#2#3#4 + { + \msg_show:nneeee { template } { show-attribute } + { \tl_to_str:n {#2} } + { \tl_to_str:n {#3} } + { \tl_to_str:n {#4} } + { \prop_map_function:NN #1 \msg_show_item_unbraced:nn } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_show_values:nn} +% Instance values are a little more complex, as is the template to consider. +% \begin{macrocode} +\cs_new_protected:Npn \@@_show_values:nn #1#2 + { + \@@_if_instance_exist:nnT {#1} {#2} + { + \@@_recover_values:nn {#1} {#2} + \msg_show:nneee { template } { show-values } + { \tl_to_str:n {#1} } + { \tl_to_str:n {#2} } + { + \prop_map_function:NN \l_@@_values_prop + \msg_show_item_unbraced:nn + } + } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Messages} +% +% The text for error messages: short and long text for all of them. +% \begin{macrocode} +\msg_new:nnnn { template } { argument-number-mismatch } + { Template~type~'#1'~takes~#2~argument(s). } + { + Templates~of~type~'#1'~require~#2~argument(s).\\ + You~have~tried~to~make~a~template~for~'#1'~ + with~#3~argument(s),~which~is~not~possible:~ + the~number~of~arguments~must~agree. + } +\msg_new:nnnn { template } { bad-number-of-arguments } + { Bad~number~of~arguments~for~template~type~'#1'. } + { + A~template~may~accept~between~0~and~9~arguments.\\ + You~asked~to~use~#2~arguments:~this~is~not~supported. + } +\msg_new:nnnn { template } { bad-variable } + { Incorrect~variable~description~'#1'. } + { + The~argument~'#1'~is~not~of~the~form \\ + ~~'<variable>'\\ + ~or~\\ + ~~'global~<variable>'.\\ + It~must~be~given~in~one~of~these~formats~to~be~used~in~a~template. + } +\msg_new:nnnn { template } { choice-not-implemented } + { The~choice~'#1'~has~no~implementation. } + { + Each~choice~listed~in~the~interface~for~a~template~must~ + have~an~implementation. + } +\msg_new:nnnn { template } { choice-no-code } + { The~choice~'#1'~requires~implementation~details. } + { + When~creating~template~code~using~\DeclareTemplateCode,~ + each~choice~name~must~have~an~associated~implementation.\\ + This~should~be~given~after~a~'='~sign:~LaTeX~did~not~find~one. + } +\msg_new:nnnn { template } { choice-requires-code } + { The~choice~'#2'~for~key~'#1'~requires~an~implementation. } + { + You~should~have~put:\\ + \ \ #1~:~choice~{~#2 = <code> ~} \\ + but~LaTeX~did~not~find~any~<code>. + } +\msg_new:nnnn { template } { duplicate-key-interface } + { Key~'#1'~appears~twice~in~interface~definition~\msg_line_context:. } + { + Each~key~can~only~have~one~interface~declared~in~a~template.\\ + LaTeX~found~two~interfaces~for~'#1'. + } +\msg_new:nnnn { template } { keytype-requires-argument } + { The~key~type~'#1'~requires~an~argument~\msg_line_context:. } + { + You~should~have~put:\\ + \ \ <key-name>~:~#1~{~<argument>~} \\ + but~LaTeX~did~not~find~an~<argument>. + } +\msg_new:nnnn { template } { invalid-keytype } + { The~key~'#1'~is~missing~a~key-type~\msg_line_context:. } + { + Each~key~in~a~template~requires~a~key-type,~given~in~the~form:\\ + \ \ <key>~:~<key-type>\\ + LaTeX~could~not~find~a~<key-type>~in~your~input. + } +\msg_new:nnnn { template } { key-no-value } + { The~key~'#1'~has~no~value~\msg_line_context:. } + { + When~creating~an~instance~of~a~template~ + every~key~listed~must~include~a~value:\\ + \ \ <key>~=~<value> + } +\msg_new:nnnn { template } { key-no-variable } + { The~key~'#1'~requires~implementation~details~\msg_line_context:. } + { + When~creating~template~code~using~\DeclareTemplateCode,~ + each~key~name~must~have~an~associated~implementation.\\ + This~should~be~given~after~a~'='~sign:~LaTeX~did~not~find~one. + } +\msg_new:nnnn { template } { key-not-implemented } + { Key~'#1'~has~no~implementation~\msg_line_context:. } + { + The~definition~of~key~implementations~for~template~'#2'~ + of~template~type~'#3'~does~not~include~any~details~for~key~'#1'.\\ + The~key~was~declared~in~the~interface~definition,~ + and~so~an~implementation~is~required. + } +\msg_new:nnnn { template } { missing-keytype } + { The~key~'#1'~is~missing~a~key-type~\msg_line_context:. } + { + Key~interface~definitions~should~be~of~the~form\\ + \ \ #1~:~<key-type>\\ + but~LaTeX~could~not~find~a~<key-type>. + } +\msg_new:nnnn { template } { no-template-code } + { + The~template~'#2'~of~type~'#1'~is~unknown~ + or~has~no~implementation. + } + { + There~is~no~code~available~for~the~template~name~given.\\ + This~should~be~given~using~\DeclareTemplateCode. + } +\msg_new:nnnn { template } { type-already-defined } + { Template~type~'#1'~already~defined. } + { + You~have~used~\NewTemplateType~ + with~a~template~type~that~has~already~been~defined. + } +\msg_new:nnnn { template } { type-mismatch } + { Template~types~'#1'~and~'#2'~do~not~agree. } + { + You~are~trying~to~use~a~template~directly~with~\UseInstance + (or~a~similar~function),~but~the~template~types~do~not~match. + } +\msg_new:nnnn { template } { unknown-attribute } + { The~template~attribute~'#1'~is~unknown. } + { + There~is~a~definition~in~the~current~template~reading\\ + \ \ \token_to_str:N \KeyValue {~#1~} \\ + but~there~is~no~key~called~'#1'. + } +\msg_new:nnnn { template } { unknown-choice } + { The~choice~'#2'~was~not~declared~for~key~'#1'. } + { + The~key~'#1'~takes~a~fixed~list~of~choices~ + and~this~list~does~not~include~'#2'. + } +\msg_new:nnnn { template } { unknown-default-choice } + { The~default~choice~'#2'~was~not~declared~for~key~'#1'. } + { + The~key~'#1'~takes~a~fixed~list~of~choices~ + and~this~list~does~not~include~'#2'. + } +\msg_new:nnnn { template } { unknown-instance } + { The~instance~'#2'~of~type~'#1'~is~unknown. } + { + You~have~asked~to~use~an~instance~'#2',~ + but~this~has~not~been~created. + } +\msg_new:nnnn { template } { unknown-key } + { Unknown~template~key~'#1'. } + { + The~key~'#1'~was~not~declared~in~the~interface~ + for~the~current~template. + } +\msg_new:nnnn { template } { unknown-keytype } + { The~key-type~'#1'~is~unknown. } + { + Valid~key-types~are:\\ + -~boolean;\\ + -~choice;\\ + -~commalist;\\ + -~function;\\ + -~instance;\\ + -~integer;\\ + -~length;\\ + -~muskip;\\ + -~real;\\ + -~skip;\\ + -~tokenlist. + } +\msg_new:nnnn { template } { unknown-type } + { The~template~type~'#1'~is~unknown. } + { + A~template~type~needs~to~be~defined~with~\NewTemplateType + prior~to~using~it. + } +\msg_new:nnnn { template } { unknown-template } + { The~template~'#2'~of~type~'#1'~is~unknown. } + { + No~interface~has~been~declared~for~a~template~ + '#2'~of~template~type~'#1'. + } +% \end{macrocode} +% +% Information messages only have text: more text should not be needed. +% \begin{macrocode} +\msg_new:nnn { template } { declare-instance } + { Declaring~instance~~'#1'~of~type~#2~\msg_line_context:. } +\msg_new:nnn { template } { declare-template-code } + { Declaring~code~for~template~'#2'~of~template~type~'#1'~\msg_line_context:. } +\msg_new:nnn { template } { declare-template-interface } + { + Declaring~interface~for~template~'#2'~of~template~type~'#1'~ + \msg_line_context:. + } +\msg_new:nnn { template } { declare-type } + { Declaring~template~type~'#1'~taking~#2~argument(s)~\msg_line_context:. } +\msg_new:nnn { template } { show-attribute } + { + The~template~'#2'~of~type~'#1'~has~ + \tl_if_empty:nTF {#4} { no~#3. } { #3 : #4 } + } +\msg_new:nnn { template } { show-values } + { + The~instance~'#2'~of~type~'#1'~has~ + \tl_if_empty:nTF {#3} { no~values. } { values: #3 } + } +% \end{macrocode} +% +% Also add \pkg{template} to the \pkg{LaTeX} messages. +% \begin{macrocode} +\prop_gput:Nnn \g_msg_module_type_prop { template } { LaTeX } +% \end{macrocode} +% +% \subsection{User functions} +% +% \begin{macro}{\NewTemplateType} +% \begin{macro}{\DeclareTemplateInterface} +% \begin{macro}{\DeclareTemplateCode} +% \begin{macro}{\DeclareTemplateCopy} +% \begin{macro}{\EditTemplateDefaults} +% \begin{macro}{\UseTemplate} +% \begin{macro}{\DeclareInstance} +% \begin{macro}{\DeclareInstanceCopy} +% \begin{macro}{\EditInstance} +% \begin{macro}{\UseInstance} +% All simple translations. +% \begin{macrocode} +\cs_new_protected:Npn \NewTemplateType #1#2 + { \@@_define_type:nn {#1} {#2} } +\cs_new_protected:Npn \DeclareTemplateInterface #1#2#3#4 + { \@@_declare_template_keys:nnnn {#1} {#2} {#3} {#4} } +\cs_new_protected:Npn \DeclareTemplateCode #1#2#3#4#5 + { \@@_declare_template_code:nnnnn {#1} {#2} {#3} {#4} {#5} } +\cs_new_protected:Npn \DeclareTemplateCopy #1#2#3 + { \@@_template_set_eq:nnn {#1} {#2} {#3} } +\cs_new_protected:Npn \EditTemplateDefaults #1#2#3 + { \@@_edit_defaults:nnn {#1} {#2} {#3} } +\cs_new_protected:Npn \UseTemplate #1#2#3 + { \@@_use_template:nnn {#1} {#2} {#3} } +\cs_new_protected:Npn \DeclareInstance #1#2#3#4 + { \@@_declare_instance:nnnn {#1} {#3} {#2} {#4} } +\cs_new_protected:Npn \DeclareInstanceCopy #1#2#3 + { \@@_instance_set_eq:nnn {#1} {#2} {#3} } +\cs_new_protected:Npn \EditInstance #1#2#3 + { \@@_edit_instance:nnn {#1} {#2} {#3} } +\cs_new_protected:Npn \UseInstance #1#2 + { \@@_use_instance:nn {#1} {#2} } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ShowTemplateCode} +% \begin{macro}{\ShowTemplateDefaults} +% \begin{macro}{\ShowTemplateInterface} +% \begin{macro}{\ShowTemplateVariables} +% \begin{macro}{\ShowInstanceValues} +% The show functions are again just translation. +% \begin{macrocode} +\cs_new_protected:Npn \ShowTemplateCode #1#2 + { \@@_show_code:nn {#1} {#2} } +\cs_new_protected:Npn \ShowTemplateDefaults #1#2 + { \@@_show_defaults:nn {#1} {#2} } +\cs_new_protected:Npn \ShowTemplateInterface #1#2 + { \@@_show_keytypes:nn {#1} {#2} } +\cs_new_protected:Npn \ShowTemplateVariables #1#2 + { \@@_show_vars:nn {#1} {#2} } +\cs_new_protected:Npn \ShowInstanceValues #1#2 + { \@@_show_values:nn {#1} {#2} } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\KeyValue} +% Simply dump the argument when executed: this should not happen. +% \begin{macrocode} +\cs_new_protected:Npn \KeyValue #1 {#1} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\AssignTemplateKeys} +% A short call to use a token register by proxy. +% \begin{macrocode} +\cs_new_protected:Npn \AssignTemplateKeys { \@@_assignments_pop: } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\SetTemplateKeys} +% A friendly wrapper +% \begin{macrocode} +\cs_new_protected:Npn \SetTemplateKeys #1#2#3 + { \keys_set_known:nnN { template / #1 / #2 } {#3} \l_@@_tmp_clist } +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +%<latexrelease>\IncludeInRelease{0000/00/00}{lttemplates}% +%<latexrelease> {Prototype~document~commands}% +%<latexrelease> +%<latexrelease>\EndModuleRelease +% \end{macrocode} +% +% \begin{macrocode} +\ExplSyntaxOff +% \end{macrocode} +% +% \begin{macrocode} +%</2ekernel|latexrelease> +% \end{macrocode} +% +% We need to stop DocStrip treating |@@| in a special way at this point. +% \begin{macrocode} +%<@@=> +% \end{macrocode} +% +% \Finale
\ No newline at end of file |