summaryrefslogtreecommitdiff
path: root/macros/latex-dev/base/lttemplates.dtx
diff options
context:
space:
mode:
Diffstat (limited to 'macros/latex-dev/base/lttemplates.dtx')
-rw-r--r--macros/latex-dev/base/lttemplates.dtx2698
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