% \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-2020 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] %\@ifpackagelater{expl3}{2018/02/21} % {} % {% % \PackageError{xtemplate}{Support package l3kernel too old} % {% % Please install an up to date version of l3kernel\MessageBreak % using your TeX package manager or from CTAN.\MessageBreak % \MessageBreak % Loading xtemplate will abort!% % }% % \endinput % } % %<*driver> \documentclass[full]{l3doc} \usepackage{amstext} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \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 2020-03-03} % % \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 } { } % } % \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}{2020-03-03}{} {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 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 \\ ~~''\\ ~or~\\ ~~'global~'.\\ 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:\\ \ \ ~:~#1~{~~} \\ but~LaTeX~did~not~find~an~. } \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:\\ \ \ ~:~\\ LaTeX~could~not~find~a~~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:\\ \ \ ~=~ } \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~:~\\ but~LaTeX~could~not~find~a~. } \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} % % \end{macrocode} % %\end{implementation} % %\PrintIndex