diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /macros/latex/contrib/l3packages/xtemplate.dtx |
Initial commit
Diffstat (limited to 'macros/latex/contrib/l3packages/xtemplate.dtx')
-rw-r--r-- | macros/latex/contrib/l3packages/xtemplate.dtx | 2892 |
1 files changed, 2892 insertions, 0 deletions
diff --git a/macros/latex/contrib/l3packages/xtemplate.dtx b/macros/latex/contrib/l3packages/xtemplate.dtx new file mode 100644 index 0000000000..04aa51be36 --- /dev/null +++ b/macros/latex/contrib/l3packages/xtemplate.dtx @@ -0,0 +1,2892 @@ +% \iffalse meta-comment +% +%% File: xtemplate.dtx +% +% Copyright (C) 1999 Frank Mittelbach, Chris Rowley, David Carlisle +% (C) 2004-2010 Frank Mittelbach, The LaTeX3 Project +% (C) 2011-2019 The LaTeX3 Project +% +% It may be distributed and/or modified under the conditions of the +% LaTeX Project Public License (LPPL), either version 1.3c of this +% license or (at your option) any later version. The latest version +% of this license is in the file +% +% https://www.latex-project.org/lppl.txt +% +% This file is part of the "l3packages bundle" (The Work in LPPL) +% and all files in that bundle must be distributed together. +% +% ----------------------------------------------------------------------- +% +% The development version of the bundle can be found at +% +% https://github.com/latex3/latex3 +% +% for those people who are interested. +% +%<*driver|package> +% The version of expl3 required is tested as early as possible, as +% some really old versions do not define \ProvidesExplPackage. +\RequirePackage{expl3}[2018/02/21] +%<package>\@ifpackagelater{expl3}{2018/02/21} +%<package> {} +%<package> {% +%<package> \PackageError{xtemplate}{Support package l3kernel too old} +%<package> {% +%<package> Please install an up to date version of l3kernel\MessageBreak +%<package> using your TeX package manager or from CTAN.\MessageBreak +%<package> \MessageBreak +%<package> Loading xtemplate will abort!% +%<package> }% +%<package> \endinput +%<package> } +%</driver|package> +%<*driver> +\documentclass[full]{l3doc} +\usepackage{amstext} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +%</driver> +% \fi +% +% \title{^^A +% The \textsf{xtemplate} package\\ Prototype document functions^^A +% } +% +% \author{^^A +% The \LaTeX3 Project\thanks +% {^^A +% E-mail: +% \href{mailto:latex-team@latex-project.org} +% {latex-team@latex-project.org}^^A +% }^^A +% } +% +% \date{Released 2019-05-28} +% +% \maketitle +% +% \begin{documentation} +% +% 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{xtemplate} 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{xtemplate} 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 object types, +% templates, and instances, and they are discussed below in sections +% \ref{sec:objects}, \ref{sec:templates}, and~\ref{sec:instances}, +% respectively. +% +% \section {Objects, templates, and instances} +% +% By formally declaring documents to be composed of mark-up elements +% grouped into objects, 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{Object types} +% \label{sec:objects} +% +% An \emph{object type} (sometimes just \enquote{object}) 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 object, for example, might take +% three inputs: \enquote{title}, \enquote{short title}, and +% \enquote{label}. +% +% Any given document class will define which object types are to be +% used in the document, and any template of a given object type can be +% used to generate an instance for the object. (Of course, different +% templates will produce different typeset representations, but the +% underlying content will be the same.) +% +% \begin{function}{\DeclareObjectType} +% \begin{syntax} +% \cs{DeclareObjectType} \Arg{object type} \Arg{no. of args} +% \end{syntax} +% This function defines an \meta{object type} taking +% \meta{number of arguments}, where the \meta{object type} is an +% abstraction as discussed above. For example, +% \begin{verbatim} +% \DeclareObjectType{sectioning}{3} +% \end{verbatim} +% creates an object type \enquote{sectioning}, where each use of that +% object 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 object type. Templates that do the same +% thing, but in different ways, are grouped together by their object 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{object type} \Arg{template} \Arg{no.~of args} +% ~~\Arg{key list} +% \end{syntax} +% A \meta{template} interface is declared for a particular +% \meta{object type}, where the \meta{number of arguments} must +% agree with the object 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} \\ +% code +% & Generalised key type: use |#1| as the input to the key \\ +% 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 { object } { 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{object 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{object 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 object 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}) \\ +% code +% & \meta{code} using |#1| as input to the 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 is carried out by using the function +% \cs{AssignTemplateKeys}. Thus no keys are assigned if this is missing +% from the \meta{code} used. +% \end{function} +% +% \begin{function}{\EvaluateNow} +% \begin{syntax} +% \cs{EvaluteNow} \Arg{expression} +% \end{syntax} +% The standard method when creating an instance from a template is to +% evaluate the \meta{expression} when the instance is used. However, it may +% be desirable to calculate the value when declared, which can be +% forced using \cs{EvaluateNow}. Currently, this functionality is +% regarded as experimental: the team have not found an example where it +% is actually needed, and so it may be dropped \emph{if} no good +% examples are suggested! +% \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{object type} \Arg{instance} \Arg{template} \Arg{parameters} +% \end{syntax} +% This function uses a \meta{template} for an \meta{object 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 an object type for document sections +% (which might include chapters, parts, sections, \emph{etc}.), which +% is called \texttt{sectioning}. One possible template for this +% object 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{object type} \Arg{instance} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if the named \meta{instance} of a \meta{object type} exists, and +% then inserts the appropriate code into the input stream. +% \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{object type} \Arg{instance} \meta{arguments} +% \end{syntax} +% Uses an \meta{instance} of the \meta{object type}, which will require +% \meta{arguments} as determined by the number specified for the +% \meta{object 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{object type} \Arg{template} +% ~~\Arg{settings} \meta{arguments} +% \end{syntax} +% Uses the \meta{template} of the specified \meta{object type}, +% applying the \meta{settings} and absorbing \meta{arguments} as +% detailed by the \meta{object 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 { object } { template } { instance } +% { +% instance-key = +% \UseTemplate { object2 } { 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{object type} \Arg{template} \Arg{new defaults} +% \end{syntax} +% Edits the \meta{defaults} for a \meta{template} for an +% \meta{object 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{object type} \Arg{instance} \Arg{new values} +% \end{syntax} +% Edits the \meta{values} for an \meta{instance} for an +% \meta{object 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{When template parameters should be frozen} +% +% A class designer may be inheriting templates declared by someone else, +% either third-party code or the \LaTeX{} kernel itself. Sometimes these +% templates will be overly general for the purposes of the document. +% The user should be able to customise parts of the template instances, +% but otherwise be restricted to only those parameters allowed by the +% designer. +% +% \begin{function}{\DeclareRestrictedTemplate} +% \begin{syntax} +% \cs{DeclareRestrictedTemplate} +% ~~\Arg{object type} \Arg{parent template} \Arg{new template} +% ~~\Arg{parameters} +% \end{syntax} +% Creates a copy of the \meta{parent template} for the +% \meta{object type} called \meta{new template}. The key--value list +% of \meta{parameters} applies in the \meta{new template} and cannot +% be changed when creating an instance. +% \end{function} +% +% \section{Getting information about templates and instances} +% +% \begin{function}{\ShowInstanceValues} +% \begin{syntax} +% \cs{ShowInstanceValues} \Arg{object type} \Arg{instance} +% \end{syntax} +% Shows the \meta{values} for an \meta{instance} of the given +% \meta{object type} at the terminal. +% \end{function} +% +% \begin{function}{\ShowTemplateCode} +% \begin{syntax} +% \cs{ShowTemplateCode} \Arg{object type} \Arg{template} +% \end{syntax} +% Shows the \meta{code} of a \meta{template} for an \meta{object type} +% in the terminal. +% \end{function} +% +% \begin{function}{\ShowTemplateDefaults} +% \begin{syntax} +% \cs{ShowTemplateDefaults} \Arg{object type} \Arg{template} +% \end{syntax} +% Shows the \meta{default} values of a \meta{template} for an +% \meta{object type} in the terminal. +% \end{function} +% +% \begin{function}{\ShowTemplateInterface} +% \begin{syntax} +% \cs{ShowTemplateInterface} \Arg{object type} \Arg{template} +% \end{syntax} +% Shows the \meta{keys} and associated \meta{key types} of a +% \meta{template} for an \meta{object type} in the terminal. +% \end{function} +% +% \begin{function}{\ShowTemplateVariables} +% \begin{syntax} +% \cs{ShowTemplateVariables} \Arg{object type} \Arg{template} +% \end{syntax} +% Shows the \meta{variables} and associated \meta{keys} of a +% \meta{template} for an \meta{object 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 object 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} +% +% \section{Collections} +% +% The implementation of templates includes a concept termed +% \enquote{collections}. The idea is that by activating a collection, a +% set of instances can rapidly be set up. An example use case would be +% collections for \texttt{frontmatter}, \texttt{mainmatter} and +% \texttt{backmatter} in a book. This mechanism is currently implemented +% by the commands \cs{DeclareCollectionInstance}, \cs{EditCollectionInstance} +% and \cs{UseCollection}. However, while the idea of switchable instances is +% a useful one, the team feel that collections are not the correct way to +% achieve this, at least with the current approach. As such, the collection +% functions should be regarded as deprecated: they remain available to support +% existing code, but will be removed when a better mechanism is developed. +% +% +% \begin{function}{\ShowCollectionInstanceValues} +% \begin{syntax} +% \cs{ShowInstanceValues} \Arg{collection} \Arg{object type} \Arg{instance} +% \end{syntax} +% Shows the \meta{values} for an \meta{instance} within +% a \meta{collection} of the given \meta{object type} at the terminal. +% As for other collection commands, this should be regarded as deprecated. +% \end{function} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{xtemplate} Implementation} +% +% \begin{macrocode} +%<*package> +% \end{macrocode} +% +% \begin{macrocode} +%<@@=xtemplate> +% \end{macrocode} +% +% \begin{macrocode} +\ProvidesExplPackage{xtemplate}{2019-05-28}{} + {L3 Experimental prototype document functions} +% \end{macrocode} +% +% \subsection{Variables and constants} +% +% \begin{variable}{\c_@@_code_root_tl} +% \begin{variable}{\c_@@_defaults_root_tl} +% \begin{variable}{\c_@@_instances_root_tl} +% \begin{variable}{\c_@@_keytypes_root_tl} +% \begin{variable}{\c_@@_key_order_root_tl} +% \begin{variable}{\c_@@_restrict_root_tl} +% \begin{variable}{\c_@@_values_root_tl} +% \begin{variable}{\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_@@_restrict_root_tl { template~restrictions~>~ } +\tl_const:Nn \c_@@_values_root_tl { template~values~>~ } +\tl_const:Nn \c_@@_vars_root_tl { template~vars~>~ } +% \end{macrocode} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% \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. We suspend debugging to allow +% an assignment to a constant. +% \begin{macrocode} +\seq_const_from_clist:Nn \c_@@_keytypes_arg_seq + { choice , function , instance } +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_@@_object_type_prop} +% For storing types and the associated number of arguments. +% \begin{macrocode} +\prop_new:N \g_@@_object_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{variable}{\l_@@_collection_tl} +% The current instance collection name is stored here. +% \begin{macrocode} +\tl_new:N \l_@@_collection_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_collections_prop} +% Lists current collection in force, indexed by object type. +% \begin{macrocode} +\prop_new:N \l_@@_collections_prop +% \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. The internal implementation of property lists +% means that this is safe even with un-escaped |#| tokens. +% \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_@@_restrict_bool} +% A flag to indicate that a template is being restricted. +% \begin{macrocode} +\bool_new:N \l_@@_restrict_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_restrict_clist} +% A scratch list for restricting templates. +% \begin{macrocode} +\clist_new:N \l_@@_restrict_clist +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_key_name_tl} +% \begin{variable}{\l_@@_keytype_tl} +% \begin{variable}{\l_@@_keytype_arg_tl} +% \begin{variable}{\l_@@_value_tl} +% \begin{variable}{\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} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% +% \begin{variable}{\l_@@_keytypes_prop} +% \begin{variable}{\l_@@_key_order_seq} +% \begin{variable}{\l_@@_values_prop} +% \begin{variable}{\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} +% \end{variable} +% \end{variable} +% \end{variable} +% +% \begin{variable}{\l_@@_tmp_clist} +% \begin{variable}{\l_@@_tmp_dim} +% \begin{variable}{\l_@@_tmp_int} +% \begin{variable}{\l_@@_tmp_muskip} +% \begin{variable}{\l_@@_tmp_skip} +% For pre-processing the data stored by \pkg{xtemplate}, a number of +% scratch variables are needed. The assignments are made to these in the +% first instance, unless evaluation is delayed. +% \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 +% \end{macrocode} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% +% \begin{variable}{\l_@@_tmp_tl} +% A scratch variable for comparisons and so on. +% \begin{macrocode} +\tl_new:N \l_@@_tmp_tl +% \end{macrocode} +% \end{variable} +% +% \subsection{Variant of prop functions} +% +% \begin{macro}[TF]{\prop_get:NoN} +% In some cases, we need to expand the key, and get the corresponding +% value in a property list if it exists. +% \begin{macrocode} +\cs_generate_variant:Nn \prop_get:NnNTF { No } +\cs_generate_variant:Nn \prop_get:NnNT { No } +\cs_generate_variant:Nn \prop_get:NnNF { No } +% \end{macrocode} +% \end{macro} +% +% \subsection{Testing existence and validity} +% +% There are a number of checks needed for either the existence of +% a object 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_@@_object_type_prop {#1} \l_@@_tmp_tl + \int_compare:nNnTF {#2} = \l_@@_tmp_tl + {#3} + { + \msg_error:nnxxx { xtemplate } + { 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:nnxx { xtemplate } { no-template-code } + {#1} {#2} + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_execute_if_keytype_exist:nT} +% \begin{macro}{\@@_execute_if_keytype_exist:oT} +% 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:nnx { xtemplate } { unknown-keytype } {#1} } + } +\cs_generate_variant:Nn \@@_execute_if_keytype_exist:nT { o } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_execute_if_type_exist:nT} +% To check that a particular object type is valid. +% \begin{macrocode} +\cs_new_protected:Npn \@@_execute_if_type_exist:nT #1#2 + { + \prop_if_in:NnTF \g_@@_object_type_prop {#1} + {#2} + { \msg_error:nnx { xtemplate } { unknown-object-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:nnxx { xtemplate } { unknown-template } + {#1} {#2} + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[TF]{\@@_if_key_value:n, \@@_if_key_value:o} +% Tests for the first token in a string being \cs{KeyValue}, where +% \cs{EvaluateNow} is not important. +% \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: } + } +\cs_generate_variant:Nn \@@_if_key_value:nT { o } +\cs_generate_variant:Nn \@@_if_key_value:nF { o } +\cs_generate_variant:Nn \@@_if_key_value:nTF { o } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_if_eval_now:nTF} +% Tests for the first token in a string being \cs{EvaluateNow}. +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_eval_now:n #1 { TF } + { + \str_if_eq:noTF { \EvaluateNow } { \tl_head:w #1 \q_nil \q_stop } + { \prg_return_true: } + { \prg_return_false: } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[TF]{\@@_if_instance_exist:nnn} +% Testing for an instance is collection dependent. +% \begin{macrocode} +\prg_new_conditional:Npnn \@@_if_instance_exist:nnn #1#2#3 + { T, F, TF } + { + \cs_if_exist:cTF { \c_@@_instances_root_tl #1 / #2 / #3 } + { \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:n} +% \begin{macro}{\@@_store_keytypes:n} +% \begin{macro}{\@@_store_restrictions:n} +% \begin{macro}{\@@_store_values:n} +% \begin{macro}{\@@_store_vars:n} +% 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:n #1 + { + \prop_gclear_new:c { \c_@@_defaults_root_tl #1 } + \prop_gset_eq:cN { \c_@@_defaults_root_tl #1 } + \l_@@_values_prop + } +\cs_new_protected:Npn \@@_store_keytypes:n #1 + { + \prop_gclear_new:c { \c_@@_keytypes_root_tl #1 } + \prop_gset_eq:cN { \c_@@_keytypes_root_tl #1 } + \l_@@_keytypes_prop + \seq_gclear_new:c { \c_@@_key_order_root_tl #1 } + \seq_gset_eq:cN { \c_@@_key_order_root_tl #1 } + \l_@@_key_order_seq + } +\cs_new_protected:Npn \@@_store_values:n #1 + { + \prop_clear_new:c { \c_@@_values_root_tl #1 } + \prop_set_eq:cN { \c_@@_values_root_tl #1 } + \l_@@_values_prop + } +\cs_new_protected:Npn \@@_store_restrictions:n #1 + { + \clist_gclear_new:c { \c_@@_restrict_root_tl #1 } + \clist_gset_eq:cN { \c_@@_restrict_root_tl #1 } + \l_@@_restrict_clist + } +\cs_new_protected:Npn \@@_store_vars:n #1 + { + \prop_gclear_new:c { \c_@@_vars_root_tl #1 } + \prop_gset_eq:cN { \c_@@_vars_root_tl #1 } + \l_@@_vars_prop + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_recover_defaults:n} +% \begin{macro}{\@@_recover_keytypes:n} +% \begin{macro}{\@@_recover_restrictions:n} +% \begin{macro}{\@@_recover_values:n} +% \begin{macro}{\@@_recover_vars:n} +% 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:n #1 + { + \prop_if_exist:cTF + { \c_@@_defaults_root_tl #1 } + { + \prop_set_eq:Nc \l_@@_values_prop + { \c_@@_defaults_root_tl #1 } + } + { \prop_clear:N \l_@@_values_prop } + } +\cs_new_protected:Npn \@@_recover_keytypes:n #1 + { + \prop_if_exist:cTF + { \c_@@_keytypes_root_tl #1 } + { + \prop_set_eq:Nc \l_@@_keytypes_prop + { \c_@@_keytypes_root_tl #1 } + } + { \prop_clear:N \l_@@_keytypes_prop } + \seq_if_exist:cTF { \c_@@_key_order_root_tl #1 } + { + \seq_set_eq:Nc \l_@@_key_order_seq + { \c_@@_key_order_root_tl #1 } + } + { \seq_clear:N \l_@@_key_order_seq } + } +\cs_new_protected:Npn \@@_recover_restrictions:n #1 + { + \clist_if_exist:cTF + { \c_@@_restrict_root_tl #1 } + { + \clist_set_eq:Nc \l_@@_restrict_clist + { \c_@@_restrict_root_tl #1 } + } + { \clist_clear:N \l_@@_restrict_clist } + } +\cs_new_protected:Npn \@@_recover_values:n #1 + { + \prop_if_exist:cTF + { \c_@@_values_root_tl #1 } + { + \prop_set_eq:Nc \l_@@_values_prop + { \c_@@_values_root_tl #1 } + } + { \prop_clear:N \l_@@_values_prop } + } +\cs_new_protected:Npn \@@_recover_vars:n #1 + { + \prop_if_exist:cTF + { \c_@@_vars_root_tl #1 } + { + \prop_set_eq:Nc \l_@@_vars_prop + { \c_@@_vars_root_tl #1 } + } + { \prop_clear:N \l_@@_vars_prop } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Creating new object types} +% +% \begin{macro}{\@@_declare_object_type:nn} +% Although the object 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 +% object type. +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare_object_type:nn #1#2 + { + \int_set:Nn \l_@@_tmp_int {#2} + \int_compare:nTF { 0 <= \l_@@_tmp_int <= 9 } + { + \msg_info:nnxx { xtemplate } { declare-object-type } + {#1} { \exp_not:V \l_@@_tmp_int } + \prop_gput:NnV \g_@@_object_type_prop {#1} + \l_@@_tmp_int + } + { + \msg_error:nnxx { xtemplate } { bad-number-of-arguments } + {#1} { \exp_not:V \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 object 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:n { #1 / #2 } + \@@_store_keytypes:n { #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:oT \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:nnx { xtemplate } + { duplicate-key-interface } + { \l_@@_key_name_tl } + } + { \@@_parse_keys_elt_aux: } + } + } + } + } +\cs_new_protected:Npn \@@_parse_keys_elt_aux:n #1 + { + \str_if_eq:onT \l_@@_keytype_tl {#1} + { + \tl_if_empty:NT \l_@@_keytype_arg_tl + { + \msg_error:nnx { xtemplate } + { keytype-requires-argument } {#1} + \bool_set_true:N \l_@@_error_bool + \seq_map_break: + } + } + } +\cs_new:Npn \@@_parse_keys_elt_aux: + { + \tl_set:Nx \l_@@_tmp_tl + { + \l_@@_keytype_tl + \tl_if_empty:NF \l_@@_keytype_arg_tl + { { \l_@@_keytype_arg_tl } } + } + \prop_put:Noo \l_@@_keytypes_prop \l_@@_key_name_tl + \l_@@_tmp_tl + \seq_put_right:No \l_@@_key_order_seq \l_@@_key_name_tl + \str_if_eq:onT \l_@@_keytype_tl { choice } + { + \clist_if_in:NnT \l_@@_keytype_arg_tl { unknown } + { \msg_error:nn { xtemplate } { 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, spaces +% are removed and 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:Npx \@@_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_remove_all:Nn \exp_not:N \l_@@_tmp_tl { ~ } + \tl_replace_all:Nnn \exp_not:N \l_@@_tmp_tl { : } { \token_to_str:N : } + \tl_if_in:onTF \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 \q_stop + } + } + { + \exp_not:N \bool_set_true:N \exp_not:N \l_@@_error_bool + \msg_error:nnx { xtemplate } { missing-keytype } {#1} + } + } +\use:x + { + \cs_new_protected:Npn \exp_not:N \@@_split_keytype_aux:w + ##1 \token_to_str:N : ##2 \exp_not:N \q_stop + { + \tl_put_right:Nx \exp_not:N \l_@@_key_name_tl + { \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 \exp_not:N \q_stop + } + { + \exp_not:N \tl_if_empty:NTF \exp_not:N \l_@@_key_name_tl + { + \msg_error:nnx { xtemplate } { 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:o} +% \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:Nn \l_@@_keytype_tl {#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 \q_stop + { + \tl_if_empty:nT {####1} + { + \tl_set:Nn \l_@@_keytype_tl {##1} + \tl_set:Nn \l_@@_keytype_arg_tl {####2} + \seq_map_break: + } + } + \@@_split_keytype_arg_aux:w #1 \q_stop + } + } + \seq_map_function:NN \c_@@_keytypes_arg_seq + \@@_split_keytype_arg_aux:n + } +\cs_generate_variant:Nn \@@_split_keytype_arg:n { o } +\cs_new:Npn \@@_split_keytype_arg_aux:n #1 { } +\cs_new:Npn \@@_split_keytype_arg_aux:w #1 \q_stop { } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsubsection{Storing values} +% +% As \pkg{xtemplate} 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} +% Storing Boolean values requires a test for delayed evaluation, but +% is different to the various numerical variable types as there are +% only two possible values to store. So the code here tests the default +% switch and then records the meaning (either \texttt{true} or +% \texttt{false}). +% \begin{macrocode} +\cs_new_protected:Npn \@@_store_value_boolean:n #1 + { + \@@_if_eval_now:nTF {#1} + { + \bool_if:cTF { c_ #1 _bool } + { + \prop_put:Non \l_@@_values_prop \l_@@_key_name_tl + { true } + } + { + \prop_put:Non \l_@@_values_prop \l_@@_key_name_tl + { false } + } + } + { + \prop_put:Non \l_@@_values_prop \l_@@_key_name_tl {#1} + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_store_value_code:n} +% \begin{macro}{\@@_store_value_choice:n} +% \begin{macro}{\@@_store_value_commalist:n} +% \begin{macro}{\@@_store_value_function:n} +% \begin{macro}{\@@_store_value_instance:n} +% \begin{macro}{\@@_store_value_real:n} +% \begin{macro}{\@@_store_value_tokenlist: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_code:n #1 + { \prop_put:Non \l_@@_values_prop \l_@@_key_name_tl {#1} } +\cs_new_eq:NN \@@_store_value_choice:n \@@_store_value_code:n +\cs_new_eq:NN \@@_store_value_commalist:n \@@_store_value_code:n +\cs_new_eq:NN \@@_store_value_function:n \@@_store_value_code:n +\cs_new_eq:NN \@@_store_value_instance:n \@@_store_value_code:n +\cs_new_eq:NN \@@_store_value_real:n \@@_store_value_code:n +\cs_new_eq:NN \@@_store_value_tokenlist:n \@@_store_value_code:n +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_store_value_integer:n} +% \begin{macro}{\@@_store_value_length:n} +% \begin{macro}{\@@_store_value_muskip:n} +% \begin{macro}{\@@_store_value_skip:n} +% Storing the value of a number is in all cases more or less the same. +% If evaluation is taking place now, assignment is made to a scratch +% variable, and this result is then stored. On the other hand, if +% evaluation is delayed the current data is simply stored \enquote{as is}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_store_value_integer:n #1 + { + \@@_if_eval_now:nTF {#1} + { + \int_set:Nn \l_@@_tmp_int {#1} + \prop_put:NVV \l_@@_values_prop \l_@@_key_name_int + \l_@@_tmp_int + } + { + \prop_put:Non \l_@@_values_prop \l_@@_key_name_tl {#1} + } + } +\cs_new_protected:Npn \@@_store_value_length:n #1 + { + \@@_if_eval_now:nTF {#1} + { + \dim_set:Nn \l_@@_tmp_dim {#1} + \prop_put:NVV \l_@@_values_prop \l_@@_key_name_tl + \l_@@_tmp_dim + } + { + \prop_put:Non \l_@@_values_prop \l_@@_key_name_tl {#1} + } + } +\cs_new_protected:Npn \@@_store_value_muskip:n #1 + { + \@@_if_eval_now:nTF {#1} + { + \muskip_set:Nn \l_@@_tmp_muskip {#1} + \prop_put:NVV \l_@@_values_prop \l_@@_key_name_tl + \l_@@_tmp_muskip + } + { + \prop_put:Non \l_@@_values_prop \l_@@_key_name_tl {#1} + } + } +\cs_new_protected:Npn \@@_store_value_skip:n #1 + { + \@@_if_eval_now:nTF {#1} + { + \skip_set:Nn \l_@@_tmp_skip {#1} + \prop_put:NVV \l_@@_values_prop \l_@@_key_name_tl + \l_@@_tmp_skip + } + { + \prop_put:Non \l_@@_values_prop \l_@@_key_name_tl {#1} + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Implementation part of template declaration} +% +% \begin{macro}{\@@_declare_template_code:nnnnn} +% 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} + \cs_generate_from_arg_count:cNnn + { \c_@@_code_root_tl #1 / #2 } + \cs_gset_protected:Npn {#3} {#5} + } + } + } + } +% \end{macrocode} +% \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:n { #1 / #2 } + \@@_recover_keytypes:n { #1 / #2 } + \prop_clear:N \l_@@_vars_prop + \keyval_parse:NNn + \@@_parse_vars_elt:n \@@_parse_vars_elt:nn {#3} + \@@_store_vars:n { #1 / #2 } + \clist_clear:N \l_@@_restrict_clist + \@@_store_restrictions:n { #1 / #2 } + \prop_map_inline:Nn \l_@@_keytypes_prop + { + \msg_error:nnxxx { xtemplate } { 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:nnx { xtemplate } { key-no-variable } {#1} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_parse_vars_elt: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:nn #1#2 + { + \tl_set:Nx \l_@@_key_name_tl { \tl_to_str:n {#1} } + \tl_remove_all:Nn \l_@@_key_name_tl { ~ } + \prop_get:NoNTF + \l_@@_keytypes_prop + \l_@@_key_name_tl + \l_@@_keytype_tl + { + \@@_split_keytype_arg:o \l_@@_keytype_tl + \@@_parse_vars_elt_aux:n {#2} + \prop_remove:NV \l_@@_keytypes_prop \l_@@_key_name_tl + } + { \msg_error:nnx { xtemplate } { unknown-key } {#1} } + } +% \end{macrocode} +% \end{macro} +% \begin{macro}{\@@_parse_vars_elt_aux:n} +% \begin{macro}{\@@_parse_vars_elt_aux:w} +% There now needs to be some sanity checking on the variable name +% given. This does not apply for \texttt{choice} or +% \texttt{code} \enquote{variables}, but in all other cases the variable +% needs to exist. Also, the only prefix acceptable is \texttt{global}. So +% there are a few related checks to make. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_vars_elt_aux:n #1 + { + \str_if_eq:onTF \l_@@_keytype_tl { choice } + { \@@_implement_choices:n {#1} } + { + \str_if_eq:onTF \l_@@_keytype_tl { code } + { + \prop_put:Non \l_@@_vars_prop + \l_@@_key_name_tl {#1} + } + { + \tl_if_single:nTF {#1} + { + \cs_if_exist:NF #1 + { \@@_create_variable:N #1 } + \prop_put:Non \l_@@_vars_prop + \l_@@_key_name_tl {#1} + } + { + \tl_if_in:nnTF {#1} { global } + { \@@_parse_vars_elt_aux:w #1 \q_stop } + { + \msg_error:nnx { xtemplate } { bad-variable } + { \tl_to_str:n {#1} } + } + } + } + } + } +\cs_new_protected:Npn \@@_parse_vars_elt_aux:w #1 global #2 \q_stop + { + \tl_if_empty:nTF {#1} + { + \tl_if_single:nTF {#2} + { + \cs_if_exist:NF #2 + { \@@_create_variable:N #2 } + \prop_put:Non \l_@@_vars_prop + \l_@@_key_name_tl { #1 global #2 } + } + { + \msg_error:nnx { xtemplate } { bad-variable } + { \tl_to_str:n { #1 global #2 } } + } + } + { + \msg_error:nnx { xtemplate } { bad-variable } + { \tl_to_str:n { #1 global #2 } } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_create_variable:N} +% A shortcut to create non-declared variables. Some types need a name +% mapping, others can be used directly. +% \begin{macrocode} +\cs_new_protected:Npn \@@_create_variable:N #1 + { + \str_case:onF \l_@@_keytype_tl + { + { boolean } { \bool_new:N #1 } + { commalist } { \clist_new:N #1 } + { function } { \cs_new:Npn #1 { } } + { instance } { \cs_new_protected:Npn #1 { } } + { integer } { \int_new:N #1 } + { length } { \dim_new:N #1 } + { real } { \fp_new:N #1 } + { tokenlist } { \tl_new:N #1 } + } + { \use:c { \l_@@_keytype_tl _ new:N } #1 } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_implement_choices:n} +% \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:n #1 + { + \clist_set_eq:NN \l_@@_tmp_clist \l_@@_keytype_arg_tl + \prop_put:Non \l_@@_vars_prop \l_@@_key_name_tl { } + \keyval_parse:NNn + \@@_implement_choice_elt:n \@@_implement_choice_elt:nn + {#1} + \prop_get:NoNT \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:nnx { xtemplate } { 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:Nx \l_@@_tmp_tl + { \l_@@_key_name_tl \c_space_tl \l_@@_tmp_tl } + \prop_if_in:NoF \l_@@_vars_prop \l_@@_tmp_tl + { + \tl_set:Nx \l_@@_tmp_tl + { \l_@@_key_name_tl \c_space_tl \l_@@_tmp_tl } + \prop_if_in:NoF \l_@@_vars_prop \l_@@_tmp_tl + { + \prop_get:NoN \l_@@_keytypes_prop \l_@@_key_name_tl + \l_@@_tmp_tl + \@@_split_keytype_arg:o \l_@@_tmp_tl + \prop_get:NoN \l_@@_values_prop \l_@@_key_name_tl + \l_@@_tmp_tl + \msg_error:nnxxx { xtemplate } { unknown-default-choice } + { \l_@@_key_name_tl } { \l_@@_key_name_tl } + { \l_@@_keytype_arg_tl } + } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_implement_choice_elt:n} +% \begin{macro}{\@@_implement_choice_elt:nn} +% 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:n #1 + { + \clist_if_empty:NTF \l_@@_tmp_clist + { + \str_if_eq:nnF {#1} { unknown } + { + \prop_get:NoN \l_@@_keytypes_prop \l_@@_key_name_tl + \l_@@_tmp_tl + \@@_split_keytype_arg:o \l_@@_tmp_tl + \msg_error:nnxxx { xtemplate } { unknown-choice } + { \l_@@_key_name_tl } {#1} + { \l_@@_keytype_arg_tl } + } + } + { + \clist_if_in:NnTF \l_@@_tmp_clist {#1} + { \clist_remove_all:Nn \l_@@_tmp_clist {#1} } + { + \prop_get:NoN \l_@@_keytypes_prop \l_@@_key_name_tl + \l_@@_tmp_tl + \@@_split_keytype_arg:o \l_@@_tmp_tl + \msg_error:nnxxx { xtemplate } { unknown-choice } + { \l_@@_key_name_tl } {#1} + { \l_@@_keytype_arg_tl } + } + } + } +\cs_new_protected:Npn \@@_implement_choice_elt:nn #1#2 + { + \@@_implement_choice_elt:n {#1} + \tl_set:Nx \l_@@_tmp_tl + { \l_@@_key_name_tl \c_space_tl #1 } + \prop_put:Non \l_@@_vars_prop \l_@@_tmp_tl {#2} + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Editing template defaults} +% +% Template defaults can be edited either with no other changes or +% to prevent further editing, forming a \enquote{restricted template}. +% In the later case, a new template results, whereas simple editing +% does not produce a new template name. +% +% \begin{macro}{\@@_declare_restricted:nnnn} +% Creating a restricted template means copying the old template to the +% new one first. +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare_restricted:nnnn #1#2#3#4 + { + \@@_if_keys_exist:nnT {#1} {#2} + { + \@@_set_template_eq:nn { #1 / #3 } { #1 / #2 } + \bool_set_true:N \l_@@_restrict_bool + \@@_edit_defaults_aux:nnn {#1} {#3} {#4} + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_edit_defaults:nnn} +% \begin{macro}{\@@_edit_defaults_aux: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. The auxiliary function is used +% to allow code-sharing with the template-restriction system. +% \begin{macrocode} +\cs_new_protected:Npn \@@_edit_defaults:nnn + { + \bool_set_false:N \l_@@_restrict_bool + \@@_edit_defaults_aux:nnn + } +\cs_new_protected:Npn \@@_edit_defaults_aux:nnn #1#2#3 + { + \@@_if_keys_exist:nnT {#1} {#2} + { + \@@_recover_defaults:n { #1 / #2 } + \@@_recover_restrictions:n { #1 / #2 } + \@@_parse_values:nn { #1 / #2 } {#3} + \@@_store_defaults:n { #1 / #2 } + \@@_store_restrictions:n { #1 / #2 } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_parse_values:nn} +% 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:nn #1#2 + { + \@@_recover_keytypes:n {#1} + \clist_clear:N \l_@@_restrict_clist + \keyval_parse:NNn + \@@_parse_values_elt:n \@@_parse_values_elt:nn {#2} + } +% \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:nnx { xtemplate } { 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}. +% When a template is being restricted, the setting code will be +% skipped for restricted keys. +% \begin{macrocode} +\cs_new_protected:Npn \@@_parse_values_elt:nn #1#2 + { + \tl_set:Nx \l_@@_key_name_tl { \tl_to_str:n {#1} } + \tl_remove_all:Nn \l_@@_key_name_tl { ~ } + \prop_get:NoNTF \l_@@_keytypes_prop \l_@@_key_name_tl + \l_@@_tmp_tl + { + \bool_if:NTF \l_@@_restrict_bool + { + \clist_if_in:NoF \l_@@_restrict_clist + \l_@@_key_name_tl + { \@@_parse_values_elt_aux:n {#2} } + } + { \@@_parse_values_elt_aux:n {#2} } + } + { + \msg_error:nnx { xtemplate } { unknown-key } + { \l_@@_key_name_tl } + } + } +\cs_new_protected:Npn \@@_parse_values_elt_aux:n #1 + { + \clist_put_right:No \l_@@_restrict_clist \l_@@_key_name_tl + \@@_split_keytype_arg:o \l_@@_tmp_tl + \use:c { @@_store_value_ \l_@@_keytype_tl :n } {#1} + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_set_template_eq:nn} +% 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 \@@_set_template_eq:nn #1#2 + { + \@@_recover_defaults:n {#2} + \@@_store_defaults:n {#1} + \@@_recover_keytypes:n {#2} + \@@_store_keytypes:n {#1} + \@@_recover_vars:n {#2} + \@@_store_vars:n {#1} + \cs_gset_eq:cc { \c_@@_code_root_tl #1 } + { \c_@@_code_root_tl #2 } + } +% \end{macrocode} +% \end{macro} +% +%\subsection{Creating instances of templates} +% +% \begin{macro}{\@@_declare_instance:nnnnn} +% \begin{macro}{\@@_declare_instance_aux:nnnnn} +% 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. A final check is also made so that +% there is always an instance \enquote{outside} of any collection. +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare_instance:nnnnn #1#2#3#4#5 + { + \@@_execute_if_code_exist:nnT {#1} {#2} + { + \@@_recover_defaults:n { #1 / #2 } + \@@_recover_vars:n { #1 / #2 } + \@@_declare_instance_aux:nnnnn {#1} {#2} {#3} {#4} {#5} + } + } +\cs_new_protected:Npn \@@_declare_instance_aux:nnnnn #1#2#3#4#5 + { + \bool_set_false:N \l_@@_error_bool + \@@_parse_values:nn { #1 / #2 } {#5} + \bool_if:NF \l_@@_error_bool + { + \prop_put:Nnn \l_@@_values_prop { from~template } {#2} + \@@_store_values:n { #1 / #3 / #4 } + \@@_convert_to_assignments: + \cs_set_protected:cpx { \c_@@_instances_root_tl #1 / #3 / #4 } + { + \exp_not:N \@@_assignments_push:n + { \exp_not:o \l_@@_assignments_tl } + \exp_not:c { \c_@@_code_root_tl #1 / #2 } + } + \@@_if_instance_exist:nnnF {#1} { } {#4} + { + \cs_set_eq:cc + { \c_@@_instances_root_tl #1 / / #4 } + { \c_@@_instances_root_tl #1 / #3 / #4 } + } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_edit_instance:nnnn} +% \begin{macro}{\@@_edit_instance_aux:nnnnn} +% \begin{macro}{\@@_edit_instance_aux:nonnn} +% 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:nnnn #1#2#3 + { + \@@_if_instance_exist:nnnTF {#1} {#2} {#3} + { + \@@_recover_values:n { #1 / #2 / #3 } + \prop_get:NnN \l_@@_values_prop { from~template } + \l_@@_tmp_tl + \@@_edit_instance_aux:nonnn {#1} \l_@@_tmp_tl + {#2} {#3} + } + { + \msg_error:nnxx { xtemplate } { unknown-instance } + {#1} {#3} + } + } +\cs_new_protected:Npn \@@_edit_instance_aux:nnnnn #1#2 + { + \@@_recover_vars:n { #1 / #2 } + \@@_declare_instance_aux:nnnnn {#1} {#2} + } +\cs_generate_variant:Nn \@@_edit_instance_aux:nnnnn { no } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_convert_to_assignments:} +% \begin{macro}{\@@_convert_to_assignments_aux:n} +% \begin{macro}{\@@_convert_to_assignments_aux:nn} +% \begin{macro}{\@@_convert_to_assignments_aux:no} +% 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:no {#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:onF \l_@@_keytype_tl { choice } + { + \str_if_eq:onF \l_@@_keytype_tl { code } + { \@@_find_global: } + } + \tl_set:Nn \l_@@_key_name_tl {#1} + \use:c { @@_assign_ \l_@@_keytype_tl : } + } + { \msg_error:nnx { xtemplate } { unknown-attribute } {#1} } + } + } +\cs_generate_variant:Nn \@@_convert_to_assignments_aux:nn { no } +% \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 \q_stop + } + } +\cs_new_protected:Npn \@@_find_global_aux:w #1 global #2 \q_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:n { #1 / #2 } + \@@_recover_vars:n { #1 / #2 } + \@@_parse_values:nn { #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:oTF \l_@@_value_tl + { + \@@_key_to_value: + \tl_put_right:Nx \l_@@_assignments_tl + { + \exp_not:c { #1 _eq:NN } + \exp_not:o \l_@@_var_tl + \exp_not:o \l_@@_value_tl + } + } + { + \tl_put_right:Nx \l_@@_assignments_tl + { + \exp_not:c { #1 _ \l_@@_value_tl :N } + \exp_not:o \l_@@_var_tl + } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_assign_choice:} +% \begin{macro} +% {\@@_assign_choice_aux:n, \@@_assign_choice_aux:o} +% 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:xF + { \l_@@_key_name_tl \c_space_tl \l_@@_value_tl } + { + \@@_assign_choice_aux:xF + { \l_@@_key_name_tl \c_space_tl unknown } + { + \prop_get:NoN \l_@@_keytypes_prop \l_@@_key_name_tl + \l_@@_tmp_tl + \@@_split_keytype_arg:o \l_@@_tmp_tl + \msg_error:nnxxx { xtemplate } { unknown-choice } + { \l_@@_key_name_tl } { \l_@@_value_tl } + { \l_@@_keytype_arg_tl } + } + } + } +\cs_new_protected:Npn \@@_assign_choice_aux:nF #1 + { + \prop_get:NnNTF + \l_@@_vars_prop + {#1} + \l_@@_tmp_tl + { \tl_put_right:No \l_@@_assignments_tl \l_@@_tmp_tl } + } +\cs_generate_variant:Nn \@@_assign_choice_aux:nF { x } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_assign_code:} +% \begin{macro}{\@@_assign_code:n} +% Assigning general code to a key needs a scratch function to be created +% and run when \cs{AssignTemplateKeys} is called. So the appropriate +% definition then use is created in the token list variable. +% \begin{macrocode} +\cs_new_protected:Npn \@@_assign_code: + { + \tl_put_right:Nx \l_@@_assignments_tl + { + \cs_set_protected:Npn \@@_assign_code:n \exp_not:n {##1} + { \exp_not:o \l_@@_var_tl } + \@@_assign_code:n { \exp_not:o \l_@@_value_tl } + } + } +\cs_new_protected:Npn \@@_assign_code:n #1 { } +% \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:Nx \l_@@_assignments_tl + { + \cs_generate_from_arg_count:NNnn + \exp_not:o \l_@@_var_tl + \exp_not:N #1 + { \exp_not:o \l_@@_keytype_arg_tl } + { \exp_not:o \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:Nx \l_@@_assignments_tl + { + \exp_not:N #1 \exp_not:o \l_@@_var_tl + { + \@@_use_instance:nn + { \exp_not:o \l_@@_keytype_arg_tl } + { \exp_not:o \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 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:oTF \l_@@_value_tl + { + \@@_key_to_value: + \tl_put_right:Nx \l_@@_assignments_tl + { + #1 \exp_not:o \l_@@_var_tl + \exp_not:o \l_@@_value_tl + } + } + { + \tl_put_right:Nx \l_@@_assignments_tl + { + #2 \exp_not:o \l_@@_var_tl + { \exp_not:o \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:oT \l_@@_value_tl + { \@@_key_to_value: } + \tl_put_right:Nx \l_@@_assignments_tl + { + #1 \exp_not:o \l_@@_var_tl + { \exp_not:o \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:Nx \l_@@_tmp_tl { \tl_to_str:n {#1} } + \tl_remove_all:Nn \l_@@_key_name_tl { ~ } + \prop_get:NoNTF + \l_@@_vars_prop + \l_@@_tmp_tl + \l_@@_value_tl + { + \exp_after:wN \@@_key_to_value_auxii:w \l_@@_value_tl + \q_mark global \q_nil \q_stop + } + { + \msg_error:nnx { xtemplate } { unknown-attribute } + { \l_@@_tmp_tl } + } + } +\cs_new_protected:Npn \@@_key_to_value_auxii:w #1 global #2#3 \q_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. There is the possibility that a collection instance exists, +% so this is checked before trying the general instance. If nothing is +% found, an error is raised. One additional 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:nnxx { xtemplate } { type-mismatch } {#1} {#3} } +} +\cs_new_protected:Npn \@@_use_instance_aux:nn #1#2 + { + \@@_get_collection:n {#1} + \@@_if_instance_exist:nnnTF + {#1} { \l_@@_collection_tl } {#2} + { + \use:c + { + \c_@@_instances_root_tl #1 / + \l_@@_collection_tl / #2 + } + } + { + \@@_if_instance_exist:nnnTF {#1} { } {#2} + { \use:c { \c_@@_instances_root_tl #1 / / #2 } } + { + \msg_error:nnxx { xtemplate } { unknown-instance } + {#1} {#2} + } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_use_collection:nn} +% Switching to an instance collection is just a question of setting the +% appropriate list. +% \begin{macrocode} +\cs_new_protected:Npn \@@_use_collection:nn #1#2 + { \prop_put:Nnn \l_@@_collections_prop {#1} {#2} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_get_collection:n} +% Recovering the collection for a given type is pretty easy: just a read +% from the list. +% \begin{macrocode} +\cs_new_protected:Npn \@@_get_collection:n #1 + { + \prop_get:NnNF \l_@@_collections_prop {#1} + \l_@@_collection_tl + { \tl_clear:N \l_@@_collection_tl } + } +% \end{macrocode} +% \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:n { #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:n { #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:n { #1 / #2 } + \@@_show:Nnnn \l_@@_vars_prop + {#1} {#2} { variable~mapping } + } + } +\cs_new_protected:Npn \@@_show:Nnnn #1#2#3#4 + { + \msg_show:nnxxxx { xtemplate } { 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:nnn} +% Instance values are a little more complex, as there are the collection +% and template to consider. +% \begin{macrocode} +\cs_new_protected:Npn \@@_show_values:nnn #1#2#3 + { + \@@_if_instance_exist:nnnT {#1} {#2} {#3} + { + \@@_recover_values:n { #1 / #2 / #3 } + \msg_show:nnxxxx { xtemplate } { show-values } + { \tl_to_str:n {#1} } + { \tl_to_str:n {#2} } + { \tl_to_str:n {#3} } + { + \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 { xtemplate } { argument-number-mismatch } + { Object~type~'#1'~takes~#2~argument(s). } + { + Objects~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 { xtemplate } { bad-number-of-arguments } + { Bad~number~of~arguments~for~object~type~'#1'. } + { + An~object~may~accept~between~0~and~9~arguments.\\ + You~asked~to~use~#2~arguments:~this~is~not~supported. + } +\msg_new:nnnn { xtemplate } { 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 { xtemplate } { 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 { xtemplate } { 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 { xtemplate } { 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 { xtemplate } { 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 { xtemplate } { 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 { xtemplate } { 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 { xtemplate } { 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 { xtemplate } { key-not-implemented } + { Key~'#1'~has~no~implementation~\msg_line_context:. } + { + The~definition~of~key~implementations~for~template~'#2'~ + of~object~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 { xtemplate } { 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 { xtemplate } { 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 { xtemplate } { object-type-mismatch } + { Object~types~'#1'~and~'#2'~do~not~agree. } + { + You~are~trying~to~use~a~template~directly~with~\UseInstance + (or~a~similar~function),~but~the~object~types~do~not~match. + } +\msg_new:nnnn { xtemplate } { 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 { xtemplate } { 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 { xtemplate } { 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 { xtemplate } { 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 { xtemplate } { unknown-key } + { Unknown~template~key~'#1'. } + { + The~key~'#1'~was~not~declared~in~the~interface~ + for~the~current~template. + } +\msg_new:nnnn { xtemplate } { unknown-keytype } + { The~key-type~'#1'~is~unknown. } + { + Valid~key-types~are:\\ + -~boolean;\\ + -~choice;\\ + -~code;\\ + -~commalist;\\ + -~function;\\ + -~instance;\\ + -~integer;\\ + -~length;\\ + -~muskip;\\ + -~real;\\ + -~skip;\\ + -~tokenlist. + } +\msg_new:nnnn { xtemplate } { unknown-object-type } + { The~object~type~'#1'~is~unknown. } + { + An~object~type~needs~to~be~declared~with~\DeclareObjectType + prior~to~using~it. + } +\msg_new:nnnn { xtemplate } { unknown-template } + { The~template~'#2'~of~type~'#1'~is~unknown. } + { + No~interface~has~been~declared~for~a~template~ + '#2'~of~object~type~'#1'. + } +% \end{macrocode} +% +% Information messages only have text: more text should not be needed. +% \begin{macrocode} +\msg_new:nnn { xtemplate } { declare-object-type } + { Declaring~object~type~'#1'~taking~#2~argument(s)~\msg_line_context:. } +\msg_new:nnn { xtemplate } { declare-template-code } + { Declaring~code~for~template~'#2'~of~object~type'#1'~\msg_line_context:. } +\msg_new:nnn { xtemplate } { declare-template-interface } + { + Declaring~interface~for~template~'#2'~of~object~type~'#1'~ + \msg_line_context:. + } +\msg_new:nnn { xtemplate } { show-attribute } + { + The~template~'#2'~of~object~type~'#1'~has~ + \tl_if_empty:nTF {#4} { no~#3. } { #3 : #4 } + } +\msg_new:nnn { xtemplate } { show-values } + { + \tl_if_empty:nTF {#2} + { The~instance~'#3'~ } + { The~collection~ instance~'#3'~ (from~collection~'#2')~ } + of~object~type~'#1'~has~ + \tl_if_empty:nTF {#4} { no~values. } { values: #4 } + } +% \end{macrocode} +% +% \subsection{User functions} +% +% The user functions provided by \pkg{xtemplate} are pretty much direct +% copies of internal ones. However, by sticking to the \pkg{xparse} +% approach only the appropriate arguments are long. +% +% \begin{macro}{\DeclareObjectType} +% \begin{macro}{\DeclareTemplateInterface} +% \begin{macro}{\DeclareTemplateCode} +% \begin{macro}{\DeclareRestrictedTemplate} +% \begin{macro}{\EditTemplateDefaults} +% \begin{macro}{\DeclareInstance} +% \begin{macro}{\DeclareCollectionInstance} +% \begin{macro}{\EditInstance} +% \begin{macro}{\EditCollectionInstance} +% \begin{macro}{\UseTemplate} +% \begin{macro}{\UseInstance} +% \begin{macro}{\UseCollection} +% All simple translations, with the appropriate long/short argument +% filtering. +% \begin{macrocode} +\cs_new_protected:Npn \DeclareObjectType #1#2 + { \@@_declare_object_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 \DeclareRestrictedTemplate #1#2#3#4 + { \@@_declare_restricted:nnnn {#1} {#2} {#3} {#4} } +\cs_new_protected:Npn \DeclareInstance #1#2#3#4 + { \@@_declare_instance:nnnnn {#1} {#3} { } {#2} {#4} } +\cs_new_protected:Npn \DeclareCollectionInstance #1#2#3#4#5 + { \@@_declare_instance:nnnnn {#2} {#4} {#1} {#3} {#5} } +\cs_new_protected:Npn \EditTemplateDefaults #1#2#3 + { \@@_edit_defaults:nnn {#1} {#2} {#3} } +\cs_new_protected:Npn \EditInstance #1#2#3 + { \@@_edit_instance:nnnn {#1} { } {#2} {#3} } +\cs_new_protected:Npn \EditCollectionInstance #1#2#3#4 + { \@@_edit_instance:nnnn {#2} {#1} {#3} {#4} } +\cs_new_protected:Npn \UseTemplate #1#2#3 + { \@@_use_template:nnn {#1} {#2} {#3} } +\cs_new_protected:Npn \UseInstance #1#2 + { \@@_use_instance:nn {#1} {#2} } +\cs_new_protected:Npn \UseCollection #1#2 + { \@@_use_collection: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} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ShowTemplateCode} +% \begin{macro}{\ShowTemplateDefaults} +% \begin{macro}{\ShowTemplateInterface} +% \begin{macro}{\ShowTemplateVariables} +% \begin{macro}{\ShowInstanceValues} +% \begin{macro}{\ShowCollectionInstanceValues} +% 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:nnn {#1} { } {#2} } +\cs_new_protected:Npn \ShowCollectionInstanceValues #1#2#3 + { \@@_show_values:nnn {#1} {#2} {#3} } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\IfInstanceExistT, \IfInstanceExistF, \IfInstanceExistTF} +% More direct translation: only the base instance is checked for. +% \begin{macrocode} +\cs_new:Npn \IfInstanceExistTF #1#2 + { \@@_if_instance_exist:nnnTF {#1} { } {#2} } +\cs_new:Npn \IfInstanceExistT #1#2 + { \@@_if_instance_exist:nnnT {#1} { } {#2} } +\cs_new:Npn \IfInstanceExistF #1#2 + { \@@_if_instance_exist:nnnF {#1} { } {#2} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\EvaluateNow} +% \begin{macro}{\KeyValue} +% These are both do nothing functions. Both simply dump their arguments +% when executed: this should not happen with \cs{KeyValue}. +% \begin{macrocode} +\cs_new_protected:Npn \EvaluateNow #1 {#1} +\cs_new_protected:Npn \KeyValue #1 {#1} +% \end{macrocode} +% \end{macro} +% \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{macrocode} +\cs_new_eq:NN \ShowTemplateKeytypes \ShowTemplateInterface +% \end{macrocode} +% +% \begin{macrocode} +%</package> +% \end{macrocode} +% +%\end{implementation} +% +%\PrintIndex |