From e0c6872cf40896c7be36b11dcc744620f10adf1d Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Mon, 2 Sep 2019 13:46:59 +0900 Subject: Initial commit --- macros/latex/contrib/l3packages/CHANGELOG.md | 60 + macros/latex/contrib/l3packages/README.md | 127 + macros/latex/contrib/l3packages/l3keys2e-demo.tex | 43 + macros/latex/contrib/l3packages/l3keys2e.dtx | 338 ++ macros/latex/contrib/l3packages/l3keys2e.ins | 47 + macros/latex/contrib/l3packages/l3keys2e.pdf | Bin 0 -> 327677 bytes macros/latex/contrib/l3packages/xfp.dtx | 194 + macros/latex/contrib/l3packages/xfp.ins | 47 + macros/latex/contrib/l3packages/xfp.pdf | Bin 0 -> 310471 bytes macros/latex/contrib/l3packages/xfrac.dtx | 1141 +++++ macros/latex/contrib/l3packages/xfrac.ins | 48 + macros/latex/contrib/l3packages/xfrac.pdf | Bin 0 -> 481873 bytes macros/latex/contrib/l3packages/xparse.dtx | 4886 +++++++++++++++++++++ macros/latex/contrib/l3packages/xparse.ins | 47 + macros/latex/contrib/l3packages/xparse.pdf | Bin 0 -> 501765 bytes macros/latex/contrib/l3packages/xtemplate.dtx | 2892 ++++++++++++ macros/latex/contrib/l3packages/xtemplate.ins | 58 + macros/latex/contrib/l3packages/xtemplate.pdf | Bin 0 -> 416969 bytes 18 files changed, 9928 insertions(+) create mode 100644 macros/latex/contrib/l3packages/CHANGELOG.md create mode 100644 macros/latex/contrib/l3packages/README.md create mode 100644 macros/latex/contrib/l3packages/l3keys2e-demo.tex create mode 100644 macros/latex/contrib/l3packages/l3keys2e.dtx create mode 100644 macros/latex/contrib/l3packages/l3keys2e.ins create mode 100644 macros/latex/contrib/l3packages/l3keys2e.pdf create mode 100644 macros/latex/contrib/l3packages/xfp.dtx create mode 100644 macros/latex/contrib/l3packages/xfp.ins create mode 100644 macros/latex/contrib/l3packages/xfp.pdf create mode 100644 macros/latex/contrib/l3packages/xfrac.dtx create mode 100644 macros/latex/contrib/l3packages/xfrac.ins create mode 100644 macros/latex/contrib/l3packages/xfrac.pdf create mode 100644 macros/latex/contrib/l3packages/xparse.dtx create mode 100644 macros/latex/contrib/l3packages/xparse.ins create mode 100644 macros/latex/contrib/l3packages/xparse.pdf create mode 100644 macros/latex/contrib/l3packages/xtemplate.dtx create mode 100644 macros/latex/contrib/l3packages/xtemplate.ins create mode 100644 macros/latex/contrib/l3packages/xtemplate.pdf (limited to 'macros/latex/contrib/l3packages') diff --git a/macros/latex/contrib/l3packages/CHANGELOG.md b/macros/latex/contrib/l3packages/CHANGELOG.md new file mode 100644 index 0000000000..9f4f537301 --- /dev/null +++ b/macros/latex/contrib/l3packages/CHANGELOG.md @@ -0,0 +1,60 @@ +# Changelog +All notable changes to the `l3packages` bundle since the start of 2018 +will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +this project uses date-based 'snapshot' version identifiers. + +## [Unreleased] + +## [2019-05-28] + +- Remove stray spaces in processor information + +## [2019-05-03] + +### Added + +- `xparse`: Support for `trace` package + +## [2019-03-05] + +### Added +- `xparse`: b-type argument to grab body of environments + +### Changed +- `xparse`: make \IfBooleanTF safer +- `xparse`: clearer error messages, especially for environments +- `xparse`: when defining an environment, trim spaces at ends of its name + +## [2018-09-24] + +### Changed +- `xparse`: put spaces back when a trailing optional arg is absent (fixes #466) + +## [2018-08-23] + +### Added +- `CHANGELOG.md` (fixes #460) + +## [2018-05-12] + +### Changed +- Track changes in primitive naming in `l3kernel` + +## [2018-04-30] + +### Changed +- `xparse`: allow spaces before trailing optional arguments, + with new "!" modifier to control behavior +- Switch to ISO date format +- Improve cross-module use of internal functions + +[Unreleased]: https://github.com/latex3/latex3/compare/2019-05-28...HEAD +[2019-05-28]: https://github.com/latex3/latex3/compare/2019-05-03...2019-05-28 +[2019-05-03]: https://github.com/latex3/latex3/compare/2019-03-05...2019-05-03 +[2019-03-05]: https://github.com/latex3/latex3/compare/2019-09-24...2019-03-05 +[2018-09-24]: https://github.com/latex3/latex3/compare/2018-08-23...2018-09-24 +[2018-08-23]: https://github.com/latex3/latex3/compare/2018-05-12...2018-08-23 +[2018-05-12]: https://github.com/latex3/latex3/compare/2018-04-30...2018-05-12 +[2018-04-30]: https://github.com/latex3/latex3/compare/2017-12-16...2018-04-30 diff --git a/macros/latex/contrib/l3packages/README.md b/macros/latex/contrib/l3packages/README.md new file mode 100644 index 0000000000..413bc2a8a6 --- /dev/null +++ b/macros/latex/contrib/l3packages/README.md @@ -0,0 +1,127 @@ +LaTeX3 High-Level Concepts +========================== + +Release 2019-05-28 + +Overview +-------- + +The `l3packages` collection is contains implementations for aspects of the +LaTeX3 kernel, dealing with higher-level ideas such as the Designer Interface. +The packages here are considered broadly stable (The LaTeX3 Project does not +expect the interfaces to alter radically). These packages are build on LaTeX2e +conventions at the interface level, and so may not migrate in the current form +to a stand-alone LaTeX3 format. + +All of the material in the collection requires the LaTeX3 base layer package +[`l3kernel`](http://ctan.org/pkg/l3kernel). The two packages must be installed +in matching versions: if you update `l3packages`, make sure that `l3kernel` is +updated at the same time. + +Currently included in the CTAN release of `l3packages` are the following +bundles: +* `l3keys2e` +* `xfp` +* `xfrac` +* `xparse` +* `xtemplate` + +`l3keys2e` +---------- + +The `l3keys2e` package allows keys defined using `l3keys` to be used as package +and class options with LaTeX2e. This is tied to the method the existing kernel +uses for processing options, and so it is likely that a stand-alone LaTeX3 +kernel will use a very different approach. + +`xfp` +----- + +The `xfp` package provides a document-level interface for the LaTeX3 +FPU. As such, it is a wrapper around the core `\fp_eval:n` function +but does not require code syntax. It provides the expandable command +`\fpeval`, which can be used inside for example `\edef` or contexts +where TeX requires a number. + +`xfrac` +------- + +The `xfrac` package uses the interface defined by `xtemplate` to provide +flexible split-level fractions _via_ the `\sfrac` macro. This is both a +demonstration of the power of the template concept and also a useful addition +to the available functionality in LaTeX2e. + +`xparse` +------- + +The `xparse` package provides a high-level interface for declaring document +commands, e.g., a uniform way to define commands taking optional arguments, +optional stars (and others), mandatory arguments and more. + +`xtemplate` +----------- + +The `xtemplate` package provides an interface for defining generic +functions using a key=val syntax. This is designed to be +"self-documenting", with the key definitions providing information +on how they are to be used. + +Discussion +---------- + +Discussion concerning the approach, suggestions for improvements, +changes, additions, _etc._ should be addressed to the list +[LaTeX-L](https://listserv.uni-heidelberg.de/cgi-bin/wa?A0=LATEX-L). + +You can subscribe to this list by sending mail to + + listserv@urz.uni-heidelberg.de + +with the body containing + + subscribe LATEX-L + +Issues +------ + +The issue tracker for LaTeX3 is currently located +[on GitHub](https://github.com/latex3/latex3/issues). + +Please report specific issues with LaTeX3 code there; more general +discussion should be directed to the [LaTeX-L list](#Discussion). + +The LaTeX3 Project +------------------ + +Development of LaTeX3 is carried out by +[The LaTeX3 Project](http://www.latex-project.org/latex3.html). Currently, +the team members are + +* Johannes Braams +* David Carlisle +* Robin Fairbairns +* Morten Høgholm +* Bruno Le Floch +* Thomas Lotze +* Frank Mittelbach +* Will Robertson +* Chris Rowley +* Rainer Schöpf +* Joseph Wright + +Former members of The LaTeX3 Project team were + +* Michael Downes +* Denys Duchier +* Alan Jeffrey +* Martin Schröder + +The development team can be contacted +by e-mail: ; for general LaTeX3 discussion +the [LaTeX-L list](#Discussion) should be used. + +----- + +

Copyright (C) 1998-2011,2015-2019 The LaTeX3 Project
+http://latex-project.org/
+All rights reserved.

diff --git a/macros/latex/contrib/l3packages/l3keys2e-demo.tex b/macros/latex/contrib/l3packages/l3keys2e-demo.tex new file mode 100644 index 0000000000..fda8fa40ff --- /dev/null +++ b/macros/latex/contrib/l3packages/l3keys2e-demo.tex @@ -0,0 +1,43 @@ +% This file demonstrates the use of the l3keys2e module to allow +% LaTeX3 keyvals (created using l3keys) to be used as LaTeX2e +% package or class options. +% + +% First, create a class +\begin{filecontents}{l3keys2e-class.cls} + \LoadClass{article} + \RequirePackage{l3keys2e} + \ProvidesExplClass{l3keys2e-class}{0000/00/00}{0.0}{test} + \keys_define:nn { test1 } { + option1 .code:n = {\wlog{You~gave~`#1'~for~option1}} + } + \ProcessKeysOptions { test1 } +\end{filecontents} + +% Load the class with some options. +% The class itself recognises `option1', leaving `option2' and +% `option3' as global optons which are not yet used. +\documentclass[option1=check,option2=more stuff,option3=unused] + {l3keys2e-class} + +%Now create a package +\begin{filecontents}{l3keys2e-package.sty} + \RequirePackage{l3keys2e} + \ProvidesExplPackage{l3keys2e-package}{0000/00/00}{0.0}{test} + \keys_define:nn { test2 } { + option1 .code:n = {\wlog{You~gave~`#1'~for~option1~(again)}}, + option2 .code:n = {\wlog{You~gave~`#1'~for~option2}}, + option4 .code:n = {\wlog{You~gave~`#1'~for~option4}}, + } + \ProcessKeysOptions { test2 } +\end{filecontents} + +% Load the package. +\usepackage[option4={extra stuff},option5=undefined!] + {l3keys2e-package} + +\begin{document} +% The log should show that option3 is an unused global option, and that +% option5 is not known by the l3keys2e-demo package. Everything else +% should simply be listed in the log. +\end{document} diff --git a/macros/latex/contrib/l3packages/l3keys2e.dtx b/macros/latex/contrib/l3packages/l3keys2e.dtx new file mode 100644 index 0000000000..92f3e82a37 --- /dev/null +++ b/macros/latex/contrib/l3packages/l3keys2e.dtx @@ -0,0 +1,338 @@ +% \iffalse meta-comment +% +%% File: l3keys2e.dtx +% +% Copyright (C) 2009,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] +%\@ifpackagelater{expl3}{2018/02/21} +% {} +% {% +% \PackageError{l3keys2e}{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 l3keys2e will abort!% +% }% +% \endinput +% } +% +%<*driver> +\documentclass[full]{l3doc} +\usepackage{amstext} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +% +% \fi +% +% \title{^^A +% The \textsf{l3keys2e} package\\ +% \LaTeXe{} option processing using \LaTeX3 keys^^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} +% +% The key--value method for optional arguments is very popular, as it +% allows the class or package author to define a large number of +% options with a simple interface. The \pkg{expl3} bundle of \LaTeX3 +% base code includes the module \pkg{l3keys} for defining keys, but +% to use these when loading \LaTeXe\ packages and classes requires +% extra support. That support is provided by this small package, which +% is intended to enable \LaTeXe\ packages to benefit from the power of +% the \LaTeX3 key--value system. +% +% \subsection{Creating and using keyval options} +% +% As with any key--value input, using key--value pairs as package or +% class options has two parts. The first stage is to define one or +% more keys, using the \cs{keys_define:nn} function. For example, an +% option which simply stores a value would be created using: +% \begin{verbatim} +% \keys_define:nn { module } +% { option .tl_set:N = \l_module_variable_tl } +% \end{verbatim} +% On its own, this will not make the key an option for the package or +% class containing the definition. The second stage is therefore to +% process the current options, searching for applicable keys. +% +% \begin{function}{\ProcessKeysOptions} +% \begin{syntax} +% \cs{ProcessKeysOptions} \Arg{module} +% \end{syntax} +% The \cs{ProcessKeysOptions} function is used to check the current +% option list against the keys defined for \Arg{module}. Global (class) +% options and local (package) options are checked when this function +% is called in a package. Each option which does match a key name is +% then used to attempt to set the appropriate key using +% \cs{keys_set:nn}. For example, the option defined earlier would be +% processed by the line +% \begin{verbatim} +% \ProcessKeysOptions { module } +% \end{verbatim} +% \end{function} +% +% \begin{function}{\ProcessKeysPackageOptions} +% \begin{syntax} +% \cs{ProcessKeysPackageOptions} \Arg{module} +% \end{syntax} +% This function works in a similar manner to \cs{ProcessKeysOptions}. +% When used in a \LaTeXe{} package, \cs{ProcessKeysPackageOptions} +% will not examine any class options available. In contrast, +% \cs{ProcessKeysOptions} does parse class options (in common with the +% \LaTeXe{} kernel function \cs{ProcessOptions}). +% \end{function} +% +%\end{documentation} +% +%\begin{implementation} +% +%\subsection{\pkg{l3keys2e} Implementation} +% +% \begin{macrocode} +%<*package> +% \end{macrocode} +% +% \begin{macrocode} +%<@@=keys> +% \end{macrocode} +% +% \begin{macrocode} +\ProvidesExplPackage{l3keys2e}{2019-05-28}{} + {LaTeX2e option processing using LaTeX3 keys} +% \end{macrocode} +% +% Non-standard variants. +% \begin{macrocode} +\cs_generate_variant:Nn \clist_put_right:Nn { Nv } +\cs_generate_variant:Nn \keys_if_exist:nnT { nx } +\cs_generate_variant:Nn \keys_if_exist:nnTF { nx } +% \end{macrocode} +% +% \begin{macro}{\l_@@_latexe_options_clist} +% A single list is used for all options, into which they are collected +% before processing. +% \begin{macrocode} +\clist_new:N \l_@@_latexe_options_clist +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\l_@@_process_class_bool} +% A flag to indicate that class options should be processed for +% packages. +% \begin{macrocode} +\bool_new:N \l_@@_process_class_bool +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_latexe_options:n} +% The main function calls functions to collect up the global and local +% options into \cs{l_@@_latexe_options_clist} before calling the +% underlying functions to actually do the processing. So that a suitable +% message is produced if the option is unknown, the special +% \texttt{unknown} key is set if it does not already exist for the +% current module. +% \begin{macrocode} +\cs_new_protected:Npn \@@_latexe_options:n #1 + { + \clist_clear:N \l_@@_latexe_options_clist + \@@_latexe_options_global:n {#1} + \@@_latexe_options_local: + \keys_if_exist:nnF {#1} { unknown } + { + \keys_define:nn {#1} + { + unknown .code:n = + { + \msg_error:nnxx { keyvalue } { option-unknown } + { \l_keys_key_tl } { \@currname } + } + } + } + \clist_map_inline:Nn \l_@@_latexe_options_clist + { + \tl_set:Nn \CurrentOption {##1} + \keys_set:nn {#1} {##1} + } + \AtEndOfPackage { \cs_set_eq:NN \@unprocessedoptions \scan_stop: } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_latexe_options_global:n} +% Global (class) options are handled differently for \LaTeXe{} packages +% and classes. Hence this function is essentially a check on the current +% file type. The initial test is needed as \LaTeXe{} allows variables to +% be equal to \cs{scan_stop:}, which is forbidden in \LaTeX3 code. +% \begin{macrocode} +\cs_new_protected:Npn \@@_latexe_options_global:n #1 + { + \cs_if_eq:NNF \@classoptionslist \scan_stop: + { + \cs_if_eq:NNTF \@currext \@clsextension + { \@@_latexe_options_class:n {#1} } + { + \bool_if:NT \l_@@_process_class_bool + { \@@_latexe_options_package:n {#1} } + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_latexe_options_class:n} +% For classes, each option (stripped of any content after |=|) +% is checked for existence as a key. If found, the option is added to +% the combined list for processing. On the other hand, unused options +% are stored up in \cs{@unusedoptionlist}. Before any of that, though, +% there is a simple check to see if there is an |unknown| key. If there +% is, then \emph{everything} will match and the mapping can be skipped. +% \begin{macrocode} +\cs_new_protected:Npn \@@_latexe_options_class:n #1 + { + \cs_if_free:cF { opt@ \@currname . \@currext } + { + \keys_if_exist:nnTF {#1} { unknown } + { + \clist_put_right:Nv \l_@@_latexe_options_clist + { opt@ \@currname . \@currext } + } + { + \clist_map_inline:cn { opt@ \@currname . \@currext } + { + \keys_if_exist:nxTF {#1} { \@@_latexe_remove_equals:n {##1} } + { \clist_put_right:Nn \l_@@_latexe_options_clist {##1} } + { \clist_put_right:Nn \@unusedoptionlist {##1} } + } + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_latexe_options_package:n} +% For global options when processing a package, the tasks are slightly +% different from those for a class. The check is the same, but here +% there is nothing to do if the option is not applicable. Each valid +% option also needs to be removed from \cs{@unusedoptionlist}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_latexe_options_package:n #1 + { + \clist_map_inline:Nn \@classoptionslist + { + \keys_if_exist:nxT {#1} { \@@_latexe_remove_equals:n {##1} } + { + \clist_put_right:Nn \l_@@_latexe_options_clist {##1} + \clist_remove_all:Nn \@unusedoptionlist {##1} + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_latexe_options_local:} +% If local options are found, the are added to the processing list. +% \LaTeXe{} stores options for each file in a macro which may or may not +% exist, hence the need to use \cs{cs_if_exist:c}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_latexe_options_local: + { + \cs_if_eq:NNF \@currext \@clsextension + { + \cs_if_exist:cT { opt@ \@currname . \@currext } + { + \exp_args:NNc \clist_put_right:NV \l_@@_latexe_options_clist + { opt@ \@currname . \@currext } + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_latexe_remove_equals:n} +% \begin{macro}{\@@_latexe_remove_equals:w} +% As the name suggests, this is a simple function to remove an equals +% sign from the input. This is all wrapped up in an \texttt{n} function +% so that there will always be a sign available. +% \begin{macrocode} +\cs_new:Npn \@@_latexe_remove_equals:n #1 + { \@@_latexe_remove_equals:w #1 = \q_stop } +\cs_new:Npn \@@_latexe_remove_equals:w #1 = #2 \q_stop { \exp_not:n {#1} } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\ProcessKeysOptions} +% \begin{macro}{\ProcessKeysPackageOptions} +% The user macro are simply wrappers around the internal process. In +% contrast to other similar packages, the module name is always required +% here. +% \begin{macrocode} +\cs_new_protected:Npn \ProcessKeysOptions #1 + { + \bool_set_true:N \l_@@_process_class_bool + \@@_latexe_options:n {#1} + } +\cs_new_protected:Npn \ProcessKeysPackageOptions #1 + { + \bool_set_false:N \l_@@_process_class_bool + \@@_latexe_options:n {#1} + } +\@onlypreamble \ProcessKeysOptions +\@onlypreamble \ProcessKeysPackageOptions +% \end{macrocode} +%\end{macro} +%\end{macro} +% +% One message to give. +% \begin{macrocode} +\msg_new:nnnn { keyvalue } { option-unknown } + { Unknown~option~'#1'~for~package~#2. } + { + LaTeX~has~been~asked~to~set~an~option~called~'#1'~ + but~the~#2~package~has~not~created~an~option~with~this~name. + } +% \end{macrocode} +% +% \begin{macrocode} +% +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex diff --git a/macros/latex/contrib/l3packages/l3keys2e.ins b/macros/latex/contrib/l3packages/l3keys2e.ins new file mode 100644 index 0000000000..df9e88b7e9 --- /dev/null +++ b/macros/latex/contrib/l3packages/l3keys2e.ins @@ -0,0 +1,47 @@ +\iffalse meta-comment + +File: l3keys2e.ins + +Copyright (C) 2009,2011,2012,2016,2017,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 + + http://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 released version of this bundle is available from CTAN. + +\fi + +\input l3docstrip.tex +\askforoverwritefalse + +\preamble + +Copyright (C) 2009-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: + + http://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. + +\endpreamble +% stop docstrip adding \endinput +\postamble +\endpostamble + +\keepsilent + +\generate{\file{l3keys2e.sty} {\from{l3keys2e.dtx} {package}}} + +\endbatchfile diff --git a/macros/latex/contrib/l3packages/l3keys2e.pdf b/macros/latex/contrib/l3packages/l3keys2e.pdf new file mode 100644 index 0000000000..aee2995d1f Binary files /dev/null and b/macros/latex/contrib/l3packages/l3keys2e.pdf differ diff --git a/macros/latex/contrib/l3packages/xfp.dtx b/macros/latex/contrib/l3packages/xfp.dtx new file mode 100644 index 0000000000..1b6cbd3680 --- /dev/null +++ b/macros/latex/contrib/l3packages/xfp.dtx @@ -0,0 +1,194 @@ +% \iffalse meta-comment +% +%% File: xfp.dtx +% +% Copyright (C) 2017-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] +%\@ifpackagelater{expl3}{2018/02/21} +% {} +% {% +% \PackageError{xfp}{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 xfp will abort!% +% }% +% \endinput +% } +%\RequirePackage{xparse} +% +%<*driver> +\documentclass[full]{l3doc} +\usepackage{amstext} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +% +% \fi +% +% \providecommand\nan{\texttt{NaN}} +% +% \title{^^A +% The \textsf{xfp} package\\Floating Point Unit^^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} +% +% This package provides a \LaTeXe{} document-level interface to the \LaTeX{}3 +% floating point unit (part of \pkg{expl3}). It also provides a parallel +% integer expression interface for convenience. +% +% \begin{function}[EXP]{\fpeval} +% The expandable command \cs{fpeval} takes as its argument a floating +% point expression and produces a result using the normal rules of +% mathematics. As this command is expandable it can be used where \TeX{} +% requires a number and for example within a low-level \cs{edef} operation +% to give a purely numerical result. +% \end{function} +% +% Briefly, the floating point expressions may comprise: +% \begin{itemize} +% \item Basic arithmetic: addition $x+y$, subtraction $x-y$, +% multiplication $x*y$, division $x/y$, square root~$\sqrt{x}$, +% and parentheses. +% \item Comparison operators: $x\mathop{\mathtt{<}}y$, +% $x\mathop{\mathtt{<=}}y$, $x\mathop{\mathtt{>?}}y$, +% $x\mathop{\mathtt{!=}}y$ \emph{etc.} +% \item Boolean logic: sign $\operatorname{sign} x$, +% negation $\mathop{!}x$, conjunction +% $x\mathop{\&\&}y$, disjunction $x\mathop{\vert\vert}y$, ternary +% operator $x\mathop{?}y\mathop{:}z$. +% \item Exponentials: $\exp x$, $\ln x$, $x^y$. +% \item Integer factorial: $\operatorname{fact} x$. +% \item Trigonometry: $\sin x$, $\cos x$, $\tan x$, $\cot x$, $\sec +% x$, $\csc x$ expecting their arguments in radians, and +% $\operatorname{sind} x$, $\operatorname{cosd} x$, +% $\operatorname{tand} x$, $\operatorname{cotd} x$, +% $\operatorname{secd} x$, $\operatorname{cscd} x$ expecting their +% arguments in degrees. +% \item Inverse trigonometric functions: $\operatorname{asin} x$, +% $\operatorname{acos} x$, $\operatorname{atan} x$, +% $\operatorname{acot} x$, $\operatorname{asec} x$, +% $\operatorname{acsc} x$ giving a result in radians, and +% $\operatorname{asind} x$, $\operatorname{acosd} x$, +% $\operatorname{atand} x$, $\operatorname{acotd} x$, +% $\operatorname{asecd} x$, $\operatorname{acscd} x$ giving a result +% in degrees. +% \item Extrema: $\max(x_{1},x_{2},\ldots)$, $\min(x_{1},x_{2},\ldots)$, +% $\operatorname{abs}(x)$. +% \item Rounding functions, controlled by two optional +% values, $n$ (number of places, $0$ by default) and +% $t$ (behavior on a tie, $\nan$ by default): +% \begin{itemize} +% \item $\operatorname{trunc}(x,n)$ rounds towards zero, +% \item $\operatorname{floor}(x,n)$ rounds towards~$-\infty$, +% \item $\operatorname{ceil}(x,n)$ rounds towards~$+\infty$, +% \item $\operatorname{round}(x,n,t)$ rounds to the closest value, with +% ties rounded to an even value by default, towards zero if $t=0$, +% towards $+\infty$ if $t>0$ and towards $-\infty$ if $t<0$. +% \end{itemize} +% \item Random numbers: $\mathop{rand}()$, $\mathop{randint}(m,n)$. +% \item Constants: \texttt{pi}, \texttt{deg} (one degree in radians). +% \item Dimensions, automatically expressed in points, \emph{e.g.}, +% \texttt{pc} is~$12$. +% \item Automatic conversion (no need for \cs{number}) of +% integer, dimension, and skip variables to floating points numbers, +% expressing dimensions in points and ignoring the stretch and +% shrink components of skips. +% \item Tuples: $(x_1,\ldots{},x_n)$ that can be added together, +% multiplied or divided by a floating point number, and nested. +% \end{itemize} +% +% An example of use could be the following. +% \begin{verbatim} +% \LaTeX{} can now compute: $ \frac{\sin (3.5)}{2} + 2\cdot 10^{-3} +% = \fpeval{sin(3.5)/2 + 2e-3} $. +% \end{verbatim} +% +% \begin{function}[EXP]{\inteval} +% The expandable command \cs{inteval} takes as its argument an integer +% expression and produces a result using the normal rules of +% mathematics. The operations recognised are |+|, |-|, |*| and |/| plus +% parentheses. Division occurs with \emph{rounding}, and ties are rounded +% away from zero. As this command is +% expandable it can be used where \TeX{} requires a number and for example +% within a low-level \cs{edef} operation to give a purely numerical result. +% \end{function} +% +% An example of use could be the following. +% \begin{verbatim} +% \LaTeX{} can now compute: The sum of the numbers is $\inteval{1 + 2 + 3}$. +% \end{verbatim} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{xfp} implementation} +% +% \begin{macrocode} +%<*package> +% \end{macrocode} +% +% \begin{macrocode} +\ProvidesExplPackage{xfp}{2019-05-28}{} + {L3 Floating point unit} +% \end{macrocode} +% +% \begin{macro}{\fpeval} +% A document level wrapper around the code level function. +% \begin{macrocode} +\NewExpandableDocumentCommand \fpeval { m } { \fp_eval:n {#1} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\inteval} +% A second one, this time around an \eTeX{} primitive. +% \begin{macrocode} +\NewExpandableDocumentCommand \inteval { m } { \int_eval:n {#1} } +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +% +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex diff --git a/macros/latex/contrib/l3packages/xfp.ins b/macros/latex/contrib/l3packages/xfp.ins new file mode 100644 index 0000000000..6a2a26d125 --- /dev/null +++ b/macros/latex/contrib/l3packages/xfp.ins @@ -0,0 +1,47 @@ +\iffalse meta-comment + +File: xfp.ins + +Copyright (C) 2017,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 + + http://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 released version of this bundle is available from CTAN. + +\fi + +\input l3docstrip.tex +\askforoverwritefalse + +\preamble + +Copyright (C) 2017-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: + + http://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. + +\endpreamble +% stop docstrip adding \endinput +\postamble +\endpostamble + +\keepsilent + +\generate{\file{xfp.sty}{\from{xfp.dtx}{package}}} + +\endbatchfile diff --git a/macros/latex/contrib/l3packages/xfp.pdf b/macros/latex/contrib/l3packages/xfp.pdf new file mode 100644 index 0000000000..3f595b37c9 Binary files /dev/null and b/macros/latex/contrib/l3packages/xfp.pdf differ diff --git a/macros/latex/contrib/l3packages/xfrac.dtx b/macros/latex/contrib/l3packages/xfrac.dtx new file mode 100644 index 0000000000..fa1739eee5 --- /dev/null +++ b/macros/latex/contrib/l3packages/xfrac.dtx @@ -0,0 +1,1141 @@ +% \iffalse meta-comment +% +%% File: xfrac.dtx +% +% Copyright (C) 2004,2008-2010 Morten Hoegholm +% (C) 2011,2012,2014-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] +%\@ifpackagelater{expl3}{2018/02/21} +% {} +% {% +% \PackageError{xfrac}{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 xfrac will abort!% +% }% +% \endinput +% } +\RequirePackage{amstext,graphicx,l3keys2e,textcomp,xparse,xtemplate} +% +%<*driver> +\documentclass[full]{l3doc} +\usepackage{nicefrac,xfrac} ^^A Need nicefrac for demo purposes +\providecommand*\key[1]{\textbf{#1}} +\newcommand*\switch[2]{{\fontfamily{#1}\selectfont #2}} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +% +% \fi +% +% \title{^^A +% The \textsf{xfrac} package\\ Split-level fractions^^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} +% +% The \pkg{xfrac} package defines a document command \cs{sfrac} +% with the following syntax: +% \begin{quote} +% \cs{sfrac}\oarg{instance}\marg{num}\oarg{sep}\marg{denom} +% \end{quote} +% Let's show a few examples: +% \begin{verbatim} +% \sfrac{1}{2}, $\sfrac{1}{2}$, +% $\mathbf{3\times\sfrac{1}{2}}$ +% \quad \fontfamily{ppl}\selectfont Palatino: \sfrac{1}{2} +% \quad \fontfamily{ptm}\selectfont Times: \sfrac{1}{2} +% \end{verbatim} +% \begin{quote} +% \sfrac{1}{2}, $\sfrac{1}{2}$, $\mathbf{3\times\sfrac{1}{2}}$ +% \quad \fontfamily{ppl}\selectfont Palatino: \sfrac{1}{2} +% \quad \fontfamily{ptm}\selectfont Times: \sfrac{1}{2} +% \end{quote} +% You'll notice something interesting: not only does the \cs{sfrac} +% command work as it should in math mode, it also gets the job done +% for other fonts as well. +% +% \section{A Bit of History} +% +% \subsection{The Past} +% +% One of the first exercises in \emph{The \TeX{}Book} is to design a +% macro for split level fractions. The solution presented is fairly +% simple, using a \emph{virgule} (a slash) for separating the two +% components. It looks okay because the text font and math font of +% Computer Modern look almost identical. +% +% The proper symbol to use instead of the virgule is a \emph{solidus} +% which does not exist in Computer Modern. It is however available in +% the European Computer Modern fonts, but I'll get back to that. +% +% \subsection{The Present} +% +% The most common way to produce split level fractions within \LaTeXe{} +% is by means of the \pkg{nicefrac} package. Part of the reason it +% has found widespread use is due to the strange design of the +% built-in text fractions of the EC fonts, which look like this: +% \textonehalf{}. The package is very simple to use but there are a few +% issues: +% \begin{itemize} +% \item It uses the virgule instead of the solidus. +% \item Font size of numerator and denominator is bigger than in the +% built-in symbol. Compare Palatino: \switch{ppl}{\nicefrac{1}{2}} +% \emph{vs.}~\switch{ppl}{\textonehalf}. +% \item It doesn't correct for fonts using text figures such as in the +% \pkg{eco} package. Compare \switch{cmor}{\nicefrac{1}{2}} and +% \switch{cmor}{\nicefrac{8}{9}}. +% \item In math mode, it doesn't always pick up the correct math +% alphabet. +% \end{itemize} +% In short: \pkg{nicefrac} doesn't attempt to be the answer to +% everything and so this is not a criticism of the package. It works +% quite well for Computer Modern which was pretty much what was widely +% available at the time it was developed. Users these days, however, +% have a choice of many fonts when they write their documents. +% +% \subsection{The Future} +% +% Fonts are wildly different; one macro that works fine for Computer +% Modern obviously doesn't work well at all in Palatino. For one we +% have to make the separator symbol configurable, and we need to +% take care of several details as well: font scaling of the +% numerator/denominator pair (ND), font selection of ND, \emph{etc.} If we +% are to have a single package for this in the future we have to define a +% totally generic interface for the fraction commands and then adjust +% parameters depending on the current font. What you see in this +% prototype implementation of \pkg{xfrac} is just that. +% +% \section{Advanced User Interface} +% +% \subsection{Text mode} +% +% The usual problem in text mode has a name: Computer Modern. The +% solidi of all the Computer Modern fonts leave a lot to be desired, +% although things are potentially looking better as the Latin Modern +% fonts are becoming more stable and widespread. As long as the +% default fonts are Computer Modern variants we must however work +% around this. One idea that comes to mind is to see what happens +% when you use a solidus from another font instead. Let's try with +% Times: +% \begin{quote} +% \DeclareInstance{xfrac}{cmr2}{text} +% {slash-symbol-font = ptm} +% \enquote{You take \sfrac[cmr2]{1}{2} cup of sugar, \ldots} +% \end{quote} +% That looks quite good actually, so it was probably very difficult +% to obtain that result. Nope, it was extremely easy---if you happen +% to know about \emph{instances}: +% \begin{verbatim} +% \DeclareInstance{xfrac}{cmr}{text} +% {slash-symbol-font = ptm} +% \end{verbatim} +% So we define an instance with the name |cmr| from the template +% |text| which in turn is of object type |xfrac|. You'll notice +% the |cmr| is also the name of the font family for Computer Modern +% Roman and the reasoning behind is that every font family should +% have it's own settings, and if a document command is to work well +% in that scheme, letting it use the name of the current font family +% seems like a good idea. Thus the \cs{sfrac} command checks to see +% whether an instance with same name as the current font family +% exists and uses it if the test is true; otherwise the default +% setting is used. Here we defined the instance to be used for the +% font family |cmr| and just told it to use the Times font for +% typesetting the slash symbol which turns out to be a solidus by +% default. +% +% The option \texttt{cm-recommended} which is loaded by default uses +% the Times solidus for Computer Modern Roman and Computer Modern +% Sans Serif and the Palatino solidus for Computer Modern Typewriter +% Type. This looks quite good. Should you however not want this you +% can use the option \texttt{cm-standard} which produces somewhat +% acceptable results using Computer Modern exclusively. +% +% So what about old style figures? If you use the \pkg{eco} +% package you might define an instance similar to this (`cmor' is +% the name of the roman font activated by \pkg{eco}): +% \DeclareInstance{xfrac}{cmor}{text} +% { +% slash-symbol-font = ptm, +% numerator-font = cmr, +% denominator-font = cmr +% } +% \begin{verbatim} +% \DeclareInstance{xfrac}{cmor}{text} +% { +% slash-symbol-font = ptm, +% numerator-font = cmr, +% denominator-font = cmr +% } +% \end{verbatim} +% We also use regular Computer Modern Roman for typesetting ND, so +% we end up with \switch{cmor}{\sfrac{1}{2}} and +% \switch{cmor}{\sfrac{8}{9}} instead of +% \switch{cmor}{\nicefrac{1}{2}} and \switch{cmor}{\nicefrac{8}{9}}. +% Much better. +% +% There are also situations where other tricks are useful. If you +% don't have the inferior and superior figures available in a font, +% or the font doesn't have a wider design for small font sizes, you +% can cheat by manually scaling the ND-pair. I got nice results for +% Adobe's Stempel Garamond (with small caps and old style figures) +% with the following setup: +% \begin{verbatim} +% \DeclareInstance{xfrac}{pegj}{text} +% { +% numerator-font = pegx, +% denominator-font = pegx, +% scale-factor = 0.9, +% h-scale = 1.1 +% } +% \end{verbatim} +% We use the font family |pegx| (Stempel Garamond with real small +% caps) for typesetting the ND-pair. Additionally the key +% \key{scale-factor} specifies that the font size used for the +% ND-pair should be $0.9$ of the height of the solidus, and the key +% \key{h-scale} specifies that the ND-pair should be scaled an extra +% 10\,\% horizontally. +% +% Should you be so fortunate the have a font with inferior and +% superior figures like in the Monotype Janson example from Philipp +% Lehman's excellent \emph{The Font Installation Guide}. In that +% example Philipp defines the font families |mjn0| for the inferior +% figures and |mjn1| for the superior. Thus to get the \cs{sfrac} +% command to use them on the fly for the font family |mjnj| (Janson, +% old style figures) we would say +% \begin{verbatim} +% \DeclareInstance{xfrac}{mjnj}{text} +% { +% numerator-font = mjn1, +% denominator-font = mjn0, +% scaling = false, +% numerator-bot-sep = 0 pt, +% denominator-bot-sep = 0 pt +% } +% \end{verbatim} +% I think this example is a very clean way to do it. An alternative +% approach could be to use the keys \key{numerator-format} and +% \key{denominator-format} to process the arguments and let them +% determine what to do. +% +% \subsection{Math Mode} +% +% In math mode the choices are a lot fewer because first of all +% \TeX{} comes with a built-in limitation of $16$ math families. +% Additionally we will not need a solidus for typesetting split +% fractions in math, as tradition is to use a virgule instead. We +% define the basic |mathdefault| instance to simply use the math +% family in use when the instance is run. So if we're in normal math +% like |$\sfrac{7}{9}$| we simply get family~$-1$. If we're inside a +% \cs{mathbf} we're in family~$4$ (in the standard setup at least), +% and so the fraction is typeset with the same math family. Simple, +% isn't? +% +% You can also declare instances for the math families, but I really +% don't see why you would. If you do then name them according to the +% scheme \texttt{mathfam\meta{N}}, where \meta{N} is the family number, and +% only do it if you \emph{really} know how to set up math fonts. +% That is, if \cs{DeclareMathAlphabet} is unbeknownst to you, then +% just don't go there. +% +% Another example: If we want \cs{sfrac} to produce split fractions +% without doing anything at all, we can choose the collection +% |plainmath|, which is defined as +% \begin{verbatim} +% \DeclareCollectionInstance{plainmath}{xfrac}{mathdefault}{math} +% { +% denominator-bot-sep = 0 pt, +% numerator-bot-sep = 0 pt, +% numerator-top-sep = \c_max_dim, +% scaling = false, +% slash-right-mkern = 0 mu, +% slash-left-mkern = 0 mu +% } +% \end{verbatim} +% This creates an alternative version of the instance |mathdefault| +% with settings as specified by the keys. In the default math setup +% \key{numerator-top-sep} is set to 0~pt, and here we set +% \key{numerator-bot-sep} to 0~pt as well, so in order to avoid +% over-specification (and an error message) we must set +% \key{numerator-top-sep} to \cs{c_max_dim}. We activate (obeying +% normal scoping rules) it with: +% \begin{verbatim} +% \UseCollection{xfrac}{plainmath} +% \end{verbatim} +% Then |$\sfrac{8}{13}$| produces^^A +% \begingroup +% \UseCollection{xfrac}{plainmath} +% $\sfrac{8}{13}$ and just typing |$8/13$| gives the same result: +% $8/13$. +% \endgroup +% +% \section{The Template Interface} +% +% \begin{TemplateInterfaceDescription}{xfrac} +% +% \TemplateArgument{1}{The numerator} +% +% \TemplateArgument{2}{The separator} +% +% \TemplateArgument{3}{The denominator} +% +% \TemplateSemantics +% +% Typesets arguments 1 and 3 separated by argument 2, which in text +% mode by default is a \emph{solidus}. This is taken from +% \pkg{textcomp} where it is denoted \cs{textfractionsolidus}. +% This is the character used for the ready made split level +% fractions such as \textonehalf{}---except in the (European) Computer +% Modern fonts. In math mode a \emph{virgule} is used instead as +% this is more appropriate and it is always available in the math +% fonts. The solidus is a text symbol only. +% +% \end{TemplateInterfaceDescription} +% +% \begin{TemplateDescription}{xfrac}{text} +% +% \TemplateKey{numerator-font}{tokenlist} +% {Font family specification to use for the numerator.} +% {\cs{f@family}} +% +% \TemplateKey{numerator-format}{function 1 arg} +% {Action to be taken on the numerator.} +% {Process argument unchanged} +% +% \TemplateKey{slash-symbol}{tokenlist} +% {The separator symbol. If not specified the default value will be +% used instead.} +% {Solidus (\cs{textfractionsolidus})} +% +% \TemplateKey{slash-symbol-font}{tokenlist} +% {Font family specification to use for the separator symbol.} +% {\cs{f@family}} +% +% \TemplateKey{slash-symbol-format}{function 1 arg} +% {Action to be taken on the separator symbol.} +% {Process argument unchanged} +% +% \TemplateKey{denominator-font}{tokenlist} +% {Font family specification to use for the denominator.} +% {\cs{f@family}} +% +% \TemplateKey{denominator-format}{function 1 arg} +% {Action to be taken on the denominator.} +% {Process argument unchanged} +% +% \TemplateKey{h-scale}{real} +% {Factor by which the numerator and denominator should be +% horizontally scaled. It should only be used if the real superior +% and inferior fonts are not available. For instance Stempel +% Garamond looks excellent if scaled 10\,\% extra horizontally, \emph{i.e.}, +% by a factor of 1.1.} +% {1} +% +% \TemplateKey{v-scale}{real} +% {Same as \key{h-scale} only vertically. Probably not of much use +% but added for completeness.} +% {1} +% +% \TemplateKey{scale-factor}{real} +% {Fraction of the size of \key{slash-symbol}. Used for setting the +% font size of numerator and denominator. Usually a value of app.\ +% \sfrac{5}{6} produces fine results. It should only be used if the +% real superior and inferior fonts are not available. As an example +% Stempel Garamond looks better if the factor is 0.9.} +% {0.83333} +% +% \TemplateKey{scale-relative}{choice} +% {If set to |true| the font size of the numerator and denominator +% is scaled with respect to the height of the \key{slash-symbol}. If +% set to |false| the font is scaled with respect to the total height +% of the \key{slash-symbol}.} +% {true} +% +% \TemplateKey{scaling}{choice} +% {If set to |true| the fonts are allowed to scale. If set to +% |false| they are not. See the `Janson' example for an application.} +% {true} +% +% \TemplateKey{numerator-top-sep}{length} +% {Dimension specifying the space between the top of the +% \key{slash-symbol} and the top of the numerator. If not specified, +% the depth of the solidus will be used, because this value will +% make the fraction look even.} +% {Unspecified} +% +% \TemplateKey{numerator-bot-sep}{length} +% {Dimension specifying the lift of the numerator from the +% baseline.} +% {Unspecified} +% +% \TemplateKey{denominator-bot-sep}{length} +% {Dimension specifying the lift of the denominator from the +% baseline.} +% {Unspecified} +% +% \TemplateKey{slash-right-kern}{length} +% {Dimension specifying the kerning between the \key{slash-symbol} +% and the numerator.} +% {\texttt{0pt}} +% +% \TemplateKey{slash-left-kern}{length} +% {Dimension specifying the kerning between the \key{slash-symbol} +% and the denominator.} +% {\texttt{0pt}} +% +% \TemplateKey{math-mode}{choice} +% {Are we in math mode or not?} +% {false} +% +% \TemplateKey{phantom}{tokenlist} +% {A character that suits the common cases. As we would mostly want +% to use numbers in text mode we choose a \enquote{tall} number, while in +% math it is somewhat different.} +% {8} +% +% \TemplateSemantics +% +% This template is also the foundation for the \enquote{math} template. The +% keys \key{slash-right-mkern} and \key{slash-left-mkern} can only +% be used in math mode and are not shown here. +% +% \end{TemplateDescription} +% +% \begin{TemplateDescription}{xfrac}{math} +% +% \TemplateKey{numerator-font}{tokenlist} +% {Font family specification to use for the numerator.} +% {\cs{number}\cs{fam}} +% +% \TemplateKey{slash-symbol}{tokenlist} +% {The separator symbol. If not specified the default value will be +% used instead.} +% {Virgule ($/$)} +% +% \TemplateKey{slash-symbol-font}{tokenlist} +% {Font family specification to use for the separator symbol.} +% {\cs{number}\cs{fam}} +% +% \TemplateKey{denominator-font}{tokenlist} +% {Font family specification to use for the denominator.} +% {\cs{number}\cs{fam}} +% +% \TemplateKey{scale-factor}{real} +% {Fraction of the size of \key{slash-symbol}. In math mode we +% cannot rely on the fonts to be able to scale, but giving a default +% scale of 0.7 fits into the regular size changing scheme---the +% default scheme has values $(D,T,S,SS)=(1,1,0.7,0.5)$ whereas we +% with a default \key{scale-factor} of 0.7 get $(1,1,0.7,0.49)$. +% That's close enough.} +% {0.7} +% +% \TemplateKey{scale-relative}{choice} +% {If set to |true| the font size of the numerator and denominator +% is scaled with respect to the height of the \key{slash-symbol}. If +% set to |false| the font is scaled with respect to the total height +% of the \key{slash-symbol}.} +% {false} +% +% \TemplateKey{scaling}{choice} +% {If set to |true| the fonts are allowed to scale. If set to +% |false| they are not. See the |plainmath| example for an application.} +% {true} +% +% \TemplateKey{numerator-top-sep}{length} +% {Dimension specifying the space between the top of the +% \key{slash-symbol} and the top of the numerator. If not specified, +% the depth of the virgule will be used, because this value will +% make the fraction look even.} +% {\texttt{0pt}} +% +% \TemplateKey{denominator-bot-sep}{length} +% {Dimension specifying the lift of the denominator from the +% baseline.} +% {\texttt{0pt}} +% +% \TemplateKey{slash-right-mkern}{muskip} +% {Same as \key{slash-right-kern} but for math mode only and should +% be specified in \texttt{mu} units.} +% {\texttt{-2mu}} +% +% \TemplateKey{slash-left-mkern}{muskip} +% {Same as \key{slash-left-kern} but for math mode only and should +% be specified in \texttt{mu} units.} +% {\texttt{-1mu}} +% +% \TemplateKey{math-mode}{choice} +% {Are we in math mode or not?} +% {true} +% +% \TemplateKey{phantom}{tokenlist} +% {A character that suits the common cases. In math we have a high +% risk of using a parenthesis, so we choose that. Text mode is +% another story.} +% {(^^A) +% } +% +% \TemplateSemantics +% +% This template is a restricted version of the |text| template. Only +% the keys that are different from the |text| template are shown +% here. Also bear in mind that the attributes \key{slash-left-kern} +% and \key{slash-right-kern} have no meaning in this template. +% +% \end{TemplateDescription} +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{xfrac} Implementation} +% +% \begin{macrocode} +%<*package> +% \end{macrocode} +% +% \begin{macrocode} +%<@@=xfrac> +% \end{macrocode} +% +% \begin{macrocode} +\ProvidesExplPackage{xfrac}{2019-05-28}{} + {L3 Experimental split-level fractions} +% \end{macrocode} +% +% \begin{variable}{\l_@@_cm_std_bool} +% There is one option to support. +% \begin{macrocode} +\keys_define:nn { xfrac } + { + cm-recommended .choice:, + cm-recommended / + false .code:n = + { \bool_set_true:N \l_@@_cm_std_bool }, + cm-recommended / + true .code:n = + { \bool_set_false:N \l_@@_cm_std_bool }, + cm-recommended .default:n = { true }, + cm-standard .bool_set:N = \l_@@_cm_std_bool + } +\ProcessKeysOptions { xfrac } +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_slash_box} +% \begin{variable}{\l_@@_tmp_box} +% In keeping with the \LaTeX3 philosophy, rather than use generic +% scratch boxes and get confused, \pkg{xfrac} reserves its own named +% working space. +% \begin{macrocode} +\box_new:N \l_@@_slash_box +\box_new:N \l_@@_tmp_box +% \end{macrocode} +% \end{variable} +% \end{variable} +% +% \begin{macro}{\@@_tmp:w} +% Used for the raised boxes: weird as it does not take an argument +% but the \cs{raisebox} does. +% \begin{macrocode} +\cs_new:Npn \@@_tmp:w { } +% \end{macrocode} +% \end{macro} +% +% +% \subsection{Initialisation of variables} +% +% Variables used in templates have to be set up: there is not +% much to say about these, other than that they must exist. +% +% \begin{variable}{\l_@@_denominator_bot_sep_dim} +% \begin{variable}{\l_@@_numerator_bot_sep_dim} +% \begin{variable}{\l_@@_numerator_top_sep_dim} +% \begin{variable}{\l_@@_slash_left_sep_dim} +% \begin{variable}{\l_@@_slash_right_sep_dim} +% Fixed lengths. +% \begin{macrocode} +\dim_new:N \l_@@_denominator_bot_sep_dim +\dim_new:N \l_@@_numerator_bot_sep_dim +\dim_new:N \l_@@_numerator_top_sep_dim +\dim_new:N \l_@@_slash_left_sep_dim +\dim_new:N \l_@@_slash_right_sep_dim +% \end{macrocode} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% +% \begin{variable}{\l_@@_slash_left_muskip} +% \begin{variable}{\l_@@_slash_right_muskip} +% Math mode skips. +% \begin{macrocode} +\muskip_new:N \l_@@_slash_left_muskip +\muskip_new:N \l_@@_slash_right_muskip +% \end{macrocode} +% \end{variable} +% \end{variable} +% +% \begin{variable}{\l_@@_hscale_fp} +% \begin{variable}{\l_@@_scale_factor_fp} +% \begin{variable}{\l_@@_vscale_fp} +% Floating point values. +% \begin{macrocode} +\fp_new:N \l_@@_hscale_fp +\fp_new:N \l_@@_scale_factor_fp +\fp_new:N \l_@@_vscale_fp +% \end{macrocode} +% \end{variable} +% \end{variable} +% \end{variable} +% +% \begin{variable}{\l_@@_denominator_font_tl} +% \begin{variable}{\l_@@_numerator_font_tl} +% \begin{variable}{\l_@@_phantom_tl } +% \begin{variable}{\l_@@_slash_symbol_tl} +% \begin{variable}{\l_@@_slash_symbol_font_tl} +% Token lists, which include floating-point numbers and math(s) +% skips. +% \begin{macrocode} +\tl_new:N \l_@@_denominator_font_tl +\tl_new:N \l_@@_numerator_font_tl +\tl_new:N \l_@@_phantom_tl +\tl_new:N \l_@@_slash_symbol_tl +\tl_new:N \l_@@_slash_symbol_font_tl +% \end{macrocode} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% \end{variable} +% +% \begin{macro}{\@@_fontscale:} +% \begin{macro}{\@@_math:n} +% \begin{macro}{\@@_denominator_font_change:} +% \begin{macro}{\@@_denominator_format:n} +% \begin{macro}{\@@_numerator_font_change:} +% \begin{macro}{\@@_numerator_format:n} +% \begin{macro}{\@@_relscale:} +% \begin{macro}{\@@_slash_symbol_font_change:} +% \begin{macro}{\@@_slash_symbol_format:n} +% \begin{macro}{\@@_text_or_math:n} +% Functions, either things which are calculated \enquote{on the fly} +% (no argument required) or are functions taking one argument in the +% code. +% \begin{macrocode} +\cs_new:Npn \@@_fontscale: { } +\cs_new:Npn \@@_math:n #1 { } +\cs_new:Npn \@@_denominator_font_change: { } +\cs_new:Npn \@@_denominator_format:n #1 { } +\cs_new:Npn \@@_numerator_font_change: { } +\cs_new:Npn \@@_numerator_format:n #1 { } +\cs_new:Npn \@@_relscale: { } +\cs_new:Npn \@@_slash_symbol_font_change: { } +\cs_new:Npn \@@_slash_symbol_format:n #1 { } +\cs_new:Npn \@@_text_or_math:n #1 { } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{The template} +% +% There is only one object type in \pkg{xfrac}, rather unimaginatively +% named \texttt{xfrac}. +% \begin{macrocode} +\DeclareObjectType { xfrac } { 3 } +% \end{macrocode} +% +% A single template interface is used for both text and math(s), which +% does make a few things a little complex later. +% \begin{macrocode} +\DeclareTemplateInterface { xfrac } { text } { 3 } + { + denominator-bot-sep : length = \c_max_dim , + denominator-font : tokenlist = \f@family , + denominator-format : function 1 = #1 , + h-scale : real = 1 , + math-mode : choice { false , true } + = false , + numerator-font : tokenlist = \f@family , + numerator-format : function 1 = #1 , + numerator-bot-sep : length = \c_max_dim , + numerator-top-sep : length = \c_max_dim , + phantom : tokenlist = 8 , + scale-factor : real = 0.83333 , + scale-relative : choice { false , true } + = true , + scaling : choice { false , true } + = true , + slash-left-kern : length = 0 pt , + slash-left-mkern : muskip = -2 mu , + slash-right-kern : length = 0 pt , + slash-right-mkern : muskip = -1 mu , + slash-symbol : tokenlist = \textfractionsolidus , + slash-symbol-font : tokenlist = \f@family , + slash-symbol-format : function 1 = #1 , + v-scale : real = 1 , + } +% \end{macrocode} +% Most of the variable binding is quite simple: of course, the choices +% are a little more complicated. That is particularly true where +% these have to set up \enquote{on the fly} functions. +% \begin{macrocode} +\DeclareTemplateCode { xfrac } { text } { 3 } + { + denominator-bot-sep = \l_@@_denominator_bot_sep_dim , + denominator-font = \l_@@_denominator_font_tl , + denominator-format = \@@_denominator_format:n , + h-scale = \l_@@_hscale_fp , + math-mode = + { + false = \cs_set_eq:NN \@@_math:n \use:n, + true = \cs_set_eq:NN \@@_math:n \ensuremath + }, + numerator-font = \l_@@_numerator_font_tl , + numerator-format = \@@_numerator_format:n , + numerator-bot-sep = \l_@@_numerator_bot_sep_dim , + numerator-top-sep = \l_@@_numerator_top_sep_dim , + phantom = \l_@@_phantom_tl , + scale-factor = \l_@@_scale_factor_fp , + scale-relative = + { + false = + \cs_set:Npn \@@_relscale: + { + \dim_eval:n + { \box_ht:N \l_@@_tmp_box + \box_dp:N \l_@@_tmp_box } + }, + true = + \cs_set:Npn \@@_relscale: + { \dim_eval:n { \box_ht:N \l_@@_slash_box } } + }, + scaling = + { + false = \cs_set_eq:NN \@@_fontscale: \prg_do_nothing:, + true = + \cs_set:Npn \@@_fontscale: + { + \fontsize + { \fp_to_dim:n { \l_@@_scale_factor_fp * \@@_relscale: } } + { \c_zero_dim } + \selectfont + } + }, + slash-left-kern = \l_@@_slash_left_sep_dim , + slash-left-mkern = \l_@@_slash_left_muskip , + slash-right-kern = \l_@@_slash_right_sep_dim , + slash-right-mkern = \l_@@_slash_right_muskip , + slash-symbol = \l_@@_slash_symbol_tl , + slash-symbol-font = \l_@@_slash_symbol_font_tl , + slash-symbol-format = \@@_slash_symbol_format:n , + v-scale = \l_@@_vscale_fp + } +% \end{macrocode} +% The implementation part starts with applying all of the settings +% from above. The first part of the set up is then to determine +% whether the surroundings are text or math(s), and react accordingly. +% \begin{macrocode} + { + \AssignTemplateKeys + \mode_if_math:TF + { + \cs_set_eq:NN \@@_text_or_math:n \text + \cs_set:Npx \@@_denominator_font_change: + { \tex_fam:D \l_@@_denominator_font_tl } + \cs_set:Npx \@@_numerator_font_change: + { \tex_fam:D \l_@@_numerator_font_tl } + \cs_set:Npx \@@_slash_symbol_font_change: + { \tex_fam:D \l_@@_slash_symbol_font_tl } + } + { + \cs_set_eq:NN \@@_text_or_math:n \mbox + \cs_set:Npn \@@_denominator_font_change: + { + \fontfamily { \l_@@_denominator_font_tl } + \selectfont + } + \cs_set:Npn \@@_numerator_font_change: + { + \fontfamily { \l_@@_numerator_font_tl } + \selectfont + } + \cs_set:Npn \@@_slash_symbol_font_change: + { + \fontfamily { \l_@@_slash_symbol_font_tl } + \selectfont + } + } +% \end{macrocode} +% Everything is now either inside \cs{text} or an \cs{mbox}, depending +% upon the surroundings. First, there are some boxes to set up. +% \begin{macrocode} + \@@_text_or_math:n + { + \m@th + \hbox_set:Nn \l_@@_tmp_box + { \@@_math:n { \vphantom { ( ) } } } + \hbox_set:Nn \l_@@_slash_box + { + \@@_math:n + { + \@@_slash_symbol_format:n + { + \@@_math:n + { + \@@_slash_symbol_font_change: + \IfNoValueTF {#2} + { \l_@@_slash_symbol_tl } {#2} + } + } + } + } +% \end{macrocode} +% Check on the numerator separator dimensions. The code starts with the +% assumption that neither has been given, as this can then be used to +% set up a default, which is also used when both values are set +% erroneously. +% \begin{macrocode} + \cs_set:Npn \@@_tmp:w + { + \raisebox + { + \dim_eval:n + { + \box_ht:N \l_@@_slash_box + - \box_dp:N \l_@@_slash_box + - \height + } + } + } + \dim_compare:nNnTF + { \l_@@_numerator_top_sep_dim } = { \c_max_dim } + { + \dim_compare:nNnF + { \l_@@_numerator_bot_sep_dim } = { \c_max_dim } + { + \cs_set:Npn \@@_tmp:w + { + \raisebox + { \dim_use:N \l_@@_numerator_bot_sep_dim } + } + } + } + { + \dim_compare:nNnTF + { \l_@@_numerator_bot_sep_dim } = { \c_max_dim } + { + \cs_set:Npn \@@_tmp:w + { + \raisebox + { + \dim_eval:n + { + \box_ht:N \l_@@_slash_box + - \dim_use:N \l_@@_numerator_top_sep_dim + - \height + } + } + } + } + { + \msg_error:nn { xfrac } + { over-specified-numerator-sep } + } + } +% \end{macrocode} +% Typeset the numerator. +% \begin{macrocode} + \@@_tmp:w + { + \@@_fontscale: + \@@_numerator_format:n + { + \scalebox + { \fp_use:N \l_@@_hscale_fp } + [ \fp_use:N \l_@@_vscale_fp ] + { + \@@_math:n + { + \@@_numerator_font_change: + { + \vphantom { \l_@@_phantom_tl } + #1 + } + } + } + } + } + \@@_math:n + { % THIS IS JUST WRONG! + \mode_if_math:TF + { \tex_mskip:D \l_@@_slash_right_muskip } + { \tex_hskip:D \l_@@_slash_right_sep_dim } + } +% \end{macrocode} +% Typeset the separator. +% \begin{macrocode} + \box_use:N \l_@@_slash_box + \@@_math:n + { + \mode_if_math:TF + { \tex_mskip:D \l_@@_slash_left_muskip } + { \tex_hskip:D \l_@@_slash_left_sep_dim } + } +% \end{macrocode} +% Typeset the denominator. +% \begin{macrocode} + \dim_compare:nNnTF + { \l_@@_denominator_bot_sep_dim } = { \c_max_dim } + { + \cs_set:Npn \@@_tmp:w + { \raisebox { - \box_dp:N \l_@@_slash_box } } + } + { + \cs_set:Npn \@@_tmp:w + { + \raisebox + { \dim_use:N \l_@@_denominator_bot_sep_dim } + } + } + \@@_tmp:w + { + \@@_fontscale: + \@@_denominator_format:n + { + \scalebox + { \fp_use:N \l_@@_hscale_fp } + [ \fp_use:N \l_@@_vscale_fp ] + { + \@@_math:n + { + \@@_denominator_font_change: + { + \vphantom { \l_@@_phantom_tl } + #3 + } + } + } + } + } + } + } +% \end{macrocode} +% +% Since math(s) and text mode are wildly different entities we define a +% separate template for each. You already saw the \enquote{text} +% template, and here is the \enquote{math} template. +% \begin{macrocode} +\DeclareRestrictedTemplate { xfrac } { text } { math } + { + numerator-font = \number \fam , + slash-symbol = / , + slash-symbol-font = \number \fam , + denominator-font = \number \fam , + scale-factor = 0.7 , + scale-relative = false , + scaling = true , + numerator-top-sep = 0 pt , + denominator-bot-sep = 0 pt , + math-mode = true , + phantom = ( % ) + } +% \end{macrocode} +% +%\subsection{The standard instances} +% +% For the default instances we just use the relevant templates with +% the default settings. +% +% The default \enquote{text} instance. +% \begin{macrocode} +\DeclareInstance { xfrac } { default } { text } { } +% \end{macrocode} +% +% The default \enquote{math(s)} instance. +% \begin{macrocode} +\DeclareInstance { xfrac } { mathdefault } { math } { } +% \end{macrocode} +% +% \begin{macrocode} +\DeclareCollectionInstance { plainmath } { xfrac } { mathdefault } { math } + { + denominator-bot-sep = 0 pt , + numerator-bot-sep = 0 pt , + numerator-top-sep = \c_max_dim , + scale-factor = 1 , + scale-relative = false , + scaling = true , + slash-right-mkern = 0 mu , + slash-left-mkern = 0 mu + } +% \end{macrocode} +% +% Default Computer Modern setup. Far from optimal, but better than +% nothing. +% \begin{macrocode} +\DeclareInstance { xfrac } { cmr } { text } + { + denominator-bot-sep = 0 pt , + numerator-top-sep = 0.2 ex , + slash-left-kern = -0.1 em , + slash-right-kern = -0.1 em + } +\DeclareInstance { xfrac } { cmss } { text } + { + denominator-bot-sep = 0 pt , + numerator-top-sep = 0.2 ex , + slash-left-kern = -0.1 em , + slash-right-kern = -0.1 em + } +\DeclareInstance { xfrac } { cmtt } { text } + { + denominator-bot-sep = 0 pt , + numerator-top-sep = 0.2 ex , + slash-left-kern = -0.1 em , + slash-right-kern = -0.1 em + } +% \end{macrocode} +% +% We can do better for the Computer Modern fonts. For cmr and cmss +% we choose Times, and for cmtt use Palatino. +% \begin{macrocode} +\bool_if:NF \l_@@_cm_std_bool + { + \DeclareInstance { xfrac } { cmr } { text } + { slash-symbol-font = ptm } + \DeclareInstance { xfrac } { cmss } { text } + { slash-symbol-font = ptm } + \DeclareInstance { xfrac } { cmtt } { text } + { slash-symbol-font = ppl } + } +% \end{macrocode} +% +% Things works slightly better with Latin Modern. +% \begin{macrocode} +\DeclareInstance { xfrac } { lmr } { text } + { + denominator-bot-sep = 0 pt , + numerator-top-sep = 0.1 ex , + slash-left-kern = -0.15 em , + slash-right-kern = -0.15 em + } +\DeclareInstance { xfrac } { lmss } { text } + { + denominator-bot-sep = 0 pt , + numerator-top-sep = 0 pt , + slash-left-kern = -0.15 em , + slash-right-kern = -0.15 em + } +\DeclareInstance { xfrac } { lmtt } { text } + { + denominator-bot-sep = 0 pt , + numerator-top-sep = 0 pt , + slash-left-kern = -0.15 em , + slash-right-kern = -0.15 em + } +% \end{macrocode} +% +% \subsection{The user command} +% +% \begin{macro}{\sfrac} +% Currently there is just a single user command. \cs{sfrac} takes +% two mandatory arguments: numerator and denominator. It can take an +% optional argument between the mandatory specifying the separator +% like this: +% \begin{verbatim} +% \sfrac{7}[/]{12} +% \end{verbatim} +% It also has an optional argument that comes before the first +% mandatory argument. If used it will use that instance instead of +% the auto-detected one, so a user who has defined the instance +% \enquote{cmr2} may use +% \begin{verbatim} +% \sfrac[cmr2]{7}{12} +% \end{verbatim} +% and get the settings from \enquote{cmr2} instead of the settings of +% the current font family. +% \begin{macrocode} +\NewDocumentCommand \sfrac { o m o m } + { + \mode_if_math:TF + { + \IfInstanceExistTF { xfrac } { mathfam \number \fam } + { \UseInstance { xfrac } { mathfam \number \fam } } + { \UseInstance { xfrac } { mathdefault } } + {#2} {#3} {#4} + } + { + \IfInstanceExistTF { xfrac } {#1} + { \UseInstance { xfrac } {#1} } + { + \IfInstanceExistTF { xfrac } { \f@family } + { \UseInstance { xfrac } { \f@family } } + { \UseInstance { xfrac } { default } } + } + {#2} {#3} {#4} + } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Messages} +% +% Just the one. +% \begin{macrocode} +\msg_new:nnnn { xfrac } { over-specified-numerator-sep } + { You have specified both numerator-top-sep and numerator-bot-sep} + {I will pretend that you didn't specify either of them} +% \end{macrocode} +% +% \begin{macrocode} +% +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex diff --git a/macros/latex/contrib/l3packages/xfrac.ins b/macros/latex/contrib/l3packages/xfrac.ins new file mode 100644 index 0000000000..39ecdd34b3 --- /dev/null +++ b/macros/latex/contrib/l3packages/xfrac.ins @@ -0,0 +1,48 @@ +\iffalse meta-comment + +File: xfrac.ins + +Copyright (C) 2004,2008-2010 Morten Hoegholm + (C) 2011,2012,2016,2017,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 + + http://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 released version of this bundle is available from CTAN. + +\fi + +\input l3docstrip.tex +\askforoverwritefalse + +\preamble + +Copyright (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: + + http://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. + +\endpreamble +% stop docstrip adding \endinput +\postamble +\endpostamble + +\keepsilent + +\generate{\file{xfrac.sty} {\from{xfrac.dtx} {package}}} + +\endbatchfile diff --git a/macros/latex/contrib/l3packages/xfrac.pdf b/macros/latex/contrib/l3packages/xfrac.pdf new file mode 100644 index 0000000000..9a4da3d7b3 Binary files /dev/null and b/macros/latex/contrib/l3packages/xfrac.pdf differ diff --git a/macros/latex/contrib/l3packages/xparse.dtx b/macros/latex/contrib/l3packages/xparse.dtx new file mode 100644 index 0000000000..6aee5f22f6 --- /dev/null +++ b/macros/latex/contrib/l3packages/xparse.dtx @@ -0,0 +1,4886 @@ +% \iffalse meta-comment +% +%% File: xparse.dtx +% +% Copyright (C) 1999 Frank Mittelbach, Chris Rowley, David Carlisle +% (C) 2004-2008 Frank Mittelbach, The LaTeX3 Project +% (C) 2009-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-04-12] +%\@ifpackagelater{expl3}{2018-04-12} +% {} +% {% +% \PackageError{xparse}{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 xparse will abort!% +% }% +% \endinput +% } +% +%<*driver> +\documentclass[full]{l3doc} +\usepackage{amstext} +\begin{document} + \DocInput{\jobname.dtx} +\end{document} +% +% \fi +% +% \providecommand\acro[1]{\textsc{\MakeLowercase{#1}}} +% \newenvironment{arg-description}{% +% \begin{itemize}\def\makelabel##1{\hss\llap{\bfseries##1}}}{\end{itemize}} +% +% \title{^^A +% The \textsf{xparse} package\\ Document command parser^^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} +% +% The \pkg{xparse} package provides a high-level interface for +% producing document-level commands. In that way, it is intended as +% a replacement for the \LaTeXe{} \cs{newcommand} macro. However, +% \pkg{xparse} works so that the interface to a function (optional +% arguments, stars and mandatory arguments, for example) is separate +% from the internal implementation. \pkg{xparse} provides a normalised +% input for the internal form of a function, independent of the +% document-level argument arrangement. +% +% At present, the functions in \pkg{xparse} which are regarded as +% \enquote{stable} are: +% \begin{itemize} +% \item \cs{NewDocumentCommand}\\ +% \cs{RenewDocumentCommand}\\ +% \cs{ProvideDocumentCommand}\\ +% \cs{DeclareDocumentCommand} +% \item \cs{NewDocumentEnvironment}\\ +% \cs{RenewDocumentEnvironment}\\ +% \cs{ProvideDocumentEnvironment}\\ +% \cs{DeclareDocumentEnvironment} +% \item \cs{NewExpandableDocumentCommand}\\ +% \cs{RenewExpandableDocumentCommand}\\ +% \cs{ProvideExpandableDocumentCommand}\\ +% \cs{DeclareExpandableDocumentCommand} +% \item \cs{IfNoValue(TF)} +% \item \cs{IfValue(TF)} +% \item \cs{IfBoolean(TF)} +% \end{itemize} +% with the other functions currently regarded as \enquote{experimental}. Please +% try all of the commands provided here, but be aware that the +% experimental ones may change or disappear. +% +% \section{Specifying arguments} +% +% Before introducing the functions used to create document commands, +% the method for specifying arguments with \pkg{xparse} will be +% illustrated. In order to allow each argument to be defined +% independently, \pkg{xparse} does not simply need to know the +% number of arguments for a function, but also the nature of each +% one. This is done by constructing an \emph{argument specification}, +% which defines the number of arguments, the type of each argument +% and any additional information needed for \pkg{xparse} to read the +% user input and properly pass it through to internal functions. +% +% The basic form of the argument specifier is a list of letters, where +% each letter defines a type of argument. As will be described below, +% some of the types need additional information, such as default values. +% The argument types can be divided into two, those which define +% arguments that are mandatory (potentially raising an error if not +% found) and those which define optional arguments. The mandatory types +% are: +% \begin{itemize}[font=\ttfamily] +% \item[m] A standard mandatory argument, which can either be a single +% token alone or multiple tokens surrounded by curly braces |{}|. +% Regardless of the input, the argument will be passed to the +% internal code without the outer braces. This is the \pkg{xparse} +% type specifier for a normal \TeX{} argument. +% \item[r] Given as \texttt{r}\meta{char1}\meta{char2}, this denotes a +% \enquote{required} delimited argument, where the delimiters are +% \meta{char1} and \meta{char2}. If the opening delimiter +% \meta{char1} is missing, the default marker |-NoValue-| will be +% inserted after a suitable error. +% \item[R] Given as \texttt{R}\meta{char1}\meta{char2}\marg{default}, +% this is a \enquote{required} delimited argument as for~\texttt{r}, +% but it has a user-definable recovery \meta{default} instead of +% |-NoValue-|. +% \item[v] Reads an argument \enquote{verbatim}, between the following +% character and its next occurrence, in a way similar to the argument +% of the \LaTeXe{} command \cs{verb}. Thus a \texttt{v}-type argument +% is read between two identical characters, which cannot be any of |%|, |\|, +% |#|, |{|, |}| or \verb*| |. +% The verbatim argument can also be enclosed between braces, |{| and |}|. +% A command with a verbatim +% argument will produce an error when it appears within an argument of +% another function. +% \item[b] Only suitable in the argument specification of an +% environment, it denotes the body of the environment, between +% |\begin|\marg{environment} and |\end|\marg{environment}. See +% Section~\ref{sec:body} for details. +% \end{itemize} +% The types which define optional arguments are: +% \begin{itemize}[font=\ttfamily] +% \item[o] A standard \LaTeX{} optional argument, surrounded with square +% brackets, which will supply +% the special |-NoValue-| marker if not given (as described later). +% \item[d] Given as \texttt{d}\meta{char1}\meta{char2}, an optional +% argument which is delimited by \meta{char1} and \meta{char2}. +% As with \texttt{o}, if no +% value is given the special marker |-NoValue-| is returned. +% \item[O] Given as \texttt{O}\marg{default}, is like \texttt{o}, but +% returns \meta{default} if no value is given. +% \item[D] Given as \texttt{D}\meta{char1}\meta{char2}\marg{default}, +% it is as for \texttt{d}, but returns \meta{default} if no value is given. +% Internally, the \texttt{o}, \texttt{d} and \texttt{O} types are +% short-cuts to an appropriated-constructed \texttt{D} type argument. +% \item[s] An optional star, which will result in a value +% \cs{BooleanTrue} if a star is present and \cs{BooleanFalse} +% otherwise (as described later). +% \item[t] An optional \meta{char}, which will result in a value +% \cs{BooleanTrue} if \meta{char} is present and \cs{BooleanFalse} +% otherwise. Given as \texttt{t}\meta{char}. +% \item[e] Given as \texttt{e}\marg{chars}, a set of optional +% \emph{embellishments}, each of which requires a \emph{value}. +% If an embellishment is not present, |-NoValue-| is returned. Each +% embellishment gives one argument, ordered as for the list of +% \meta{chars} in the argument specification. All \meta{chars} +% must be distinct. \emph{This is an experimental type}. +% \item[E] As for \texttt{e} but returns one or more \meta{defaults} +% if values are not given: \texttt{E}\marg{chars}\marg{defaults}. See +% Section~\ref{sec:embellishment} for more details. +% \end{itemize} +% +% Using these specifiers, it is possible to create complex input syntax +% very easily. For example, given the argument definition +% `|s o o m O{default}|', the input `|*[Foo]{Bar}|' would be parsed as: +% \begin{itemize}[nolistsep] +% \item |#1| = |\BooleanTrue| +% \item |#2| = |Foo| +% \item |#3| = |-NoValue-| +% \item |#4| = |Bar| +% \item |#5| = |default| +% \end{itemize} +% whereas `|[One][Two]{}[Three]|' would be parsed as: +% \begin{itemize}[nolistsep] +% \item |#1| = |\BooleanFalse| +% \item |#2| = |One| +% \item |#3| = |Two| +% \item |#4| = || +% \item |#5| = |Three| +% \end{itemize} +% +% Delimited argument types (\texttt{d}, \texttt{o} and \texttt{r}) are +% defined such that they require matched pairs of delimiters when collecting +% an argument. For example +% \begin{verbatim} +% \NewDocumentCommand{\foo}{o}{#1} +% \foo[[content]] % #1 = "[content]" +% \foo[[] % Error: missing closing "]" +% \end{verbatim} +% Also note that |{| and |}| cannot be used as delimiters as they are used +% by \TeX{} as grouping tokens. Arguments to be grabbed inside these tokens +% must be created as either \texttt{m}- or \texttt{g}-type arguments. +% +% Within delimited arguments, non-balanced or otherwise awkward tokens may +% be included by protecting the entire argument with a brace pair +% \begin{verbatim} +% \NewDocumentCommand{\foobar}{o}{#1} +% \foobar[{[}] % Allowed as the "[" is 'hidden' +% \end{verbatim} +% These braces will be stripped only if they surround the \emph{entire} content +% of the optional argument +% \begin{verbatim} +% \NewDocumentCommand{\foobaz}{o}{#1} +% \foobaz[{abc}] % => "abc" +% \foobaz[ {abc}] % => " {abc}" +% \end{verbatim} +% +% Two more characters have a special meaning when creating an argument +% specifier. First, \texttt{+} is used to make an argument long (to +% accept paragraph tokens). In contrast to \LaTeXe's \cs{newcommand}, +% this applies on an argument-by-argument basis. So modifying the +% example to `|s o o +m O{default}|' means that the mandatory argument +% is now \cs{long}, whereas the optional arguments are not. +% +% Secondly, the character \texttt{>} is used to declare so-called +% \enquote{argument processors}, which can be used to modify the contents of an +% argument before it is passed to the macro definition. The use of +% argument processors is a somewhat advanced topic, (or at least a less +% commonly used feature) and is covered in Section~\ref{sec:processors}. +% +% When an optional argument is followed by a mandatory argument with the +% same delimiter, \pkg{xparse} issues a warning because the optional +% argument could not be omitted by the user, thus becoming in effect +% mandatory. This can apply to \texttt{o}, \texttt{d}, \texttt{O}, +% \texttt{D}, \texttt{s}, \texttt{t}, \texttt{e}, and \texttt{E} type +% arguments followed by \texttt{r} or \texttt{R}-type required +% arguments, but also to \texttt{g} or \texttt{G} type arguments +% followed by \texttt{m} type arguments. +% +% As \pkg{xparse} is also used to describe interfaces that have appeared +% in the wider \LaTeXe{} eco-system, it also defines additional argument +% types, described in Section~\ref{sec:backwards}: the mandatory types +% \texttt{l} and \texttt{u} and the optional brace group types +% \texttt{g} and \texttt{G}. Their use is not recommended because it is +% simpler for a user if all packages use a similar syntax. For the same +% reason, delimited arguments \texttt{r}, \texttt{R}, \texttt{d} and +% \texttt{D} should normally use delimiters that are naturally paired, +% such as |[| and |]| or |(| and |)|, or that are identical, such as |"| +% and~|"|. A very common syntax is to have one optional argument +% \texttt{o} treated as a key--value list (using for instance +% \pkg{l3keys}) followed by some mandatory arguments~\texttt{m} (or +% \texttt{+m}). +% +% \subsection{Spacing and optional arguments} +% +% \TeX{} will find the first argument after a function name irrespective +% of any intervening spaces. This is true for both mandatory and +% optional arguments. So |\foo[arg]| and \verb*|\foo [arg]| are +% equivalent. Spaces are also ignored when collecting arguments up +% to the last mandatory argument to be collected (as it must exist). +% So after +% \begin{verbatim} +% \NewDocumentCommand \foo { m o m } { ... } +% \end{verbatim} +% the user input |\foo{arg1}[arg2]{arg3}| and +% \verb*|\foo{arg1} [arg2] {arg3}| will both be parsed in the same +% way. +% +% The behavior of optional arguments \emph{after} any mandatory arguments is +% selectable. The standard settings will allow spaces here, and thus +% with +% \begin{verbatim} +% \NewDocumentCommand \foobar { m o } { ... } +% \end{verbatim} +% both |\foobar{arg1}[arg2]| and \verb*|\foobar{arg1} [arg2]| will find an +% optional argument. This can be changed by giving the modified |!| in +% the argument specification: +% \begin{verbatim} +% \NewDocumentCommand \foobar { m !o } { ... } +% \end{verbatim} +% where \verb*|\foobar{arg1} [arg2]| will not find an optional argument. +% +% There is one subtly here due to the difference in handling by \TeX{} +% of \enquote{control symbols}, where the command name is made up of a single +% character, such as \enquote{\cmd{\\}}. Spaces are not ignored by \TeX{} +% here, and thus it is possible to require an optional argument directly +% follow such a command. The most common example is the use of \cmd{\\} in +% \pkg{amsmath} environments. In \pkg{xparse} terms it has signature +% \begin{verbatim} +% \DeclareDocumentCommad \\ { !s !o } { ... } +% \end{verbatim} +% +% \subsection{Required delimited arguments} +% +% The contrast between a delimited (\texttt{D}-type) and \enquote{required +% delimited} (\texttt{R}-type) argument is that an error will be raised if +% the latter is missing. Thus for example +% \begin{verbatim} +% \NewDocumentCommand {\foobaz} {r()m} {} +% \foobaz{oops} +% \end{verbatim} +% will lead to an error message being issued. The marker |-NoValue-| +% (\texttt{r}-type) or user-specified default (for \texttt{R}-type) will be +% inserted to allow error recovery. +% +% \subsection{Verbatim arguments} +% +% Arguments of type~\texttt{v} are read in verbatim mode, which will +% result in the grabbed argument consisting of tokens of category codes +% $12$~(\enquote{other}) and $13$~(\enquote{active}), except spaces, +% which are given category code $10$~(\enquote{space}). The argument is +% delimited in a similar manner to the \LaTeXe{} \cs{verb} function, or +% by (correctly nested) pairs of braces. +% +% Functions containing verbatim arguments cannot appear in the arguments +% of other functions. The \texttt{v}~argument specifier includes code to check +% this, and will raise an error if the grabbed argument has already been +% tokenized by \TeX{} in an irreversible way. +% +% By default, an argument of type~\texttt{v} must be at most one line. +% Prefixing with \texttt{+} allows line breaks within the argument. +% +% Users should note that support for verbatim arguments is somewhat +% experimental. Feedback is therefore very welcome on the \texttt{LaTeX-L} +% mailing list. +% +% \subsection{Default values of arguments} +% +% Uppercase argument types (\texttt{O}, \texttt{D}, \ldots{}) allow to +% specify a default value to be used when the argument is missing; their +% lower-case counterparts use the special marker |-NoValue-|. The +% default value can be expressed in terms of the value of any other +% arguments by using |#1|, |#2|, and so on. +% \begin{verbatim} +% \NewDocumentCommand {\conjugate} { m O{#1ed} O{#2} } {(#1,#2,#3)} +% \conjugate {walk} % => (walk,walked,walked) +% \conjugate {find} [found] % => (find,found,found) +% \conjugate {do} [did] [done] % => (do,did,done) +% \end{verbatim} +% The default values may refer to arguments that appear later in the +% argument specification. For instance a command could accept two +% optional arguments, equal by default: +% \begin{verbatim} +% \NewDocumentCommand {\margins} { O{#3} m O{#1} m } {(#1,#2,#3,#4)} +% \margins {a} {b} % => {(-NoValue-,a,-NoValue-,b)} +% \margins [1cm] {a} {b} % => {(1cm,a,1cm,b)} +% \margins {a} [1cm] {b} % => {(1cm,a,1cm,b)} +% \margins [1cm] {a} [2cm] {b} % => {(1cm,a,2cm,b)} +% \end{verbatim} +% +% Users should note that support for default arguments referring to +% other arguments is somewhat experimental. Feedback is therefore very +% welcome on the \texttt{LaTeX-L} mailing list. +% +% \subsection{Default values for \enquote{embellishments}} +% \label{sec:embellishment} +% +% The \texttt{E}-type argument allows one default value per test character. +% This is achieved by giving a list of defaults for each entry in the +% list, for example: +% \begin{verbatim} +% E{^_}{{UP}{DOWN}} +% \end{verbatim} +% If the list of default values is \emph{shorter} than the list of test characters, +% the special |-NoValue-| marker will be returned (as for the \texttt{e}-type +% argument). Thus for example +% \begin{verbatim} +% E{^_}{{UP}} +% \end{verbatim} +% has default \texttt{UP} for the |^| test character, but will return the +% |-NoValue-| marker as a default for |_|. This allows mixing of explicit +% defaults with testing for missing values. +% +% \subsection{Body of an environment} +% \label{sec:body} +% +% While environments |\begin|\marg{environment} \dots{} +% |\end|\marg{environment} are typically used in cases where the code +% implementing the \meta{environment} does not need to access the +% contents of the environment (its \enquote{body}), it is sometimes +% useful to have the body as a standard argument. +% +% This is achieved in \pkg{xparse} by ending the argument specification +% with~\texttt{b}. The approach taken in \pkg{xparse} is +% different from the earlier packages \pkg{environ} or \pkg{newenviron}: +% the body of the environment is provided to the code part as a usual +% argument |#1|, |#2| etc.\@, rather than stored in a macro such as +% \cs[no-index]{BODY}. +% +% For instance +% \begin{verbatim} +% \NewDocumentEnvironment { twice } +% { O{\ttfamily} +b } +% {#2#1#2} {} +% \begin{twice}[\itshape] +% Hello world! +% \end{twice} +% \end{verbatim} +% typesets \enquote{Hello world!{\itshape Hello world!}}. +% +% The prefix |+| is used to allow multiple paragraphs in the +% environment's body. Argument processors can also be applied to +% \texttt{b}~arguments. +% +% By default, spaces are trimmed at both ends of the body: in the +% example there would otherwise be spaces coming from the ends the lines +% after |[\itshape]| and |world!|. Putting the prefix |!| before +% \texttt{b} suppresses space-trimming. +% +% When \texttt{b} is used in the argument specification, +% the last argument of \cs{NewDocumentEnvironment}, which consists of +% an \meta{end code} to insert at |\end|\marg{environment}, is +% redundant since one can simply put that code at the end of the +% \meta{start code}. Nevertheless this (empty) \meta{end code} must be +% provided. +% +% Environments that use this feature can be nested. +% +% Users should note that this feature is somewhat experimental. Feedback +% is therefore very welcome on the \texttt{LaTeX-L} mailing list. +% +% \subsection{Starred environments} +% +% Many packages define environments with and without \texttt{*} in their +% name, for instance \texttt{tabular} and \texttt{tabular*}. At +% present, \pkg{xparse} does not provide specific tools to define these: +% one should simply define the two environment separately, for instance +% \begin{verbatim} +% \NewDocumentEnvironment { tabular } { o +m } {...} {...} +% \NewDocumentEnvironment { tabular* } { m o +m } {...} {...} +% \end{verbatim} +% Of course the implementation of these two environments, denoted +% \enquote{\texttt{...}} in this example, can rely on the same internal +% commands. +% +% Note that this situation is different from the \texttt{s} argument +% type: if the signature of an environment starts with~\texttt{s} then +% the star is searched for after the argument of \cs{begin}. For +% instance, the following typesets \texttt{star}. +% \begin{verbatim} +% \NewDocumentEnvironment { envstar } { s } +% {\IfBooleanTF {#1} {star} {no star}} {} +% \begin{envstar}* +% \end{envstar} +% \end{verbatim} +% +% \subsection{Backwards Compatibility} +% \label{sec:backwards} +% +% One role of \pkg{xparse} is to describe existing \LaTeX{} interfaces, +% including some that are rather unusual in \LaTeX{} (as opposed to +% formats such as plain \TeX{}) such as delimited arguments. As such, +% the package defines some argument specifiers that should largely be +% avoided nowadays as using them in packages leads to inconsistent user +% interfaces. The simplest syntax is often best, with argument +% specifications such as |mmmm| or |ommmm|, namely an optional argument +% followed by some standard mandatory ones. The optional argument can +% be made to support key--value syntax using tools from \pkg{l3keys}. +% +% The argument types that are not recommended any longer are: +% \begin{itemize}[font=\ttfamily] +% \item[l] A mandatory argument which reads everything up to the first +% begin-group token: in standard \LaTeX{} this is a left brace. +% \item[u] Reads a mandatory argument \enquote{until} \meta{tokens} are encountered, +% where the desired \meta{tokens} are given as an argument to the +% specifier: \texttt{u}\marg{tokens}. +% \item[g] An optional argument given inside a pair of \TeX{} group +% tokens (in standard \LaTeX{}, |{| \ldots |}|), which returns +% |-NoValue-| if not present. +% \item[G] As for \texttt{g} but returns \meta{default} if no value +% is given: \texttt{G}\marg{default}. +% \end{itemize} +% +% \section{Declaring commands and environments} +% +% With the concept of an argument specifier defined, it is now +% possible to describe the methods available for creating both +% functions and environments using \pkg{xparse}. +% +% The interface-building commands are the preferred method for +% creating document-level functions in \LaTeX3. All of the functions +% generated in this way are naturally robust (using the \eTeX{} +% \cs{protected} mechanism). +% +% \begin{function} +% { +% \NewDocumentCommand , +% \RenewDocumentCommand , +% \ProvideDocumentCommand , +% \DeclareDocumentCommand +% } +% \begin{syntax} +% \cs{NewDocumentCommand} \meta{function} \Arg{arg spec} \Arg{code} +% \end{syntax} +% This family of commands are used to create a document-level +% \meta{function}. The argument specification for the function is +% given by \meta{arg spec}, and the function expands to the +% \meta{code} with |#1|, |#2|, etc.\ replaced by the arguments found +% by \pkg{xparse}. +% \end{function} +% +% As an example: +% \begin{verbatim} +% \NewDocumentCommand \chapter { s o m } +% { +% \IfBooleanTF {#1} +% { \typesetstarchapter {#3} } +% { \typesetnormalchapter {#2} {#3} } +% } +% \end{verbatim} +% would be a way to define a \cs{chapter} command which would +% essentially behave like the current \LaTeXe{} command (except that it +% would accept an optional argument even when a \texttt{*} was parsed). +% The \cs{typesetnormalchapter} could test its first argument for being +% |-NoValue-| to see if an optional argument was present. +% +% The difference between the \cs{New\ldots} \cs{Renew\ldots}, +% \cs{Provide\ldots} and \cs{Declare\ldots} versions is the behaviour +% if \meta{function} is already defined. +% \begin{itemize} +% \item \cs{NewDocumentCommand} will issue an error if \meta{function} +% has already been defined. +% \item \cs{RenewDocumentCommand} will issue an error if \meta{function} +% has not previously been defined. +% \item \cs{ProvideDocumentCommand} creates a new definition for +% \meta{function} only if one has not already been given. +% \item \cs{DeclareDocumentCommand} will always create the new +% definition, irrespective of any existing \meta{function} with the +% same name. This should be used sparingly. +% \end{itemize} +% +% \begin{texnote} +% Unlike \LaTeXe{}'s \cs{newcommand} and relatives, the +% \cs{NewDocumentCommand} family of functions do not prevent creation of +% functions with names starting \cs{end\ldots}. +% \end{texnote} +% +% \begin{function} +% { +% \NewDocumentEnvironment , +% \RenewDocumentEnvironment , +% \ProvideDocumentEnvironment , +% \DeclareDocumentEnvironment +% } +% \begin{syntax} +% \cs{NewDocumentEnvironment} \Arg{environment} \Arg{arg spec} +% ~~\Arg{start code} \Arg{end code} +% \end{syntax} +% These commands work in the same way as \cs{NewDocumentCommand}, +% etc.\@, but create environments (\cs{begin}\Arg{environment} \ldots{} +% \cs{end}\Arg{environment}). Both the \meta{start code} and +% \meta{end code} +% may access the arguments as defined by \meta{arg spec}. +% The arguments will be given following \cs{begin}\Arg{environment}. +% \end{function} +% +% \section{Other \pkg{xparse} commands} +% +% \subsection{Testing special values} +% +% Optional arguments created using \pkg{xparse} make use of dedicated +% variables to return information about the nature of the argument +% received. +% +% \begin{function}[EXP]{\IfNoValueT, \IfNoValueF, \IfNoValueTF} +% \begin{syntax} +% \cs{IfNoValueTF} \Arg{argument} \Arg{true code} \Arg{false code} +% \cs{IfNoValueT} \Arg{argument} \Arg{true code} +% \cs{IfNoValueF} \Arg{argument} \Arg{false code} +% \end{syntax} +% The \cs{IfNoValue(TF)} tests are used to check if \meta{argument} (|#1|, +% |#2|, \emph{etc.}) is the special |-NoValue-| marker For example +% \begin{verbatim} +% \NewDocumentCommand \foo { o m } +% { +% \IfNoValueTF {#1} +% { \DoSomethingJustWithMandatoryArgument {#2} } +% { \DoSomethingWithBothArguments {#1} {#2} } +% } +% \end{verbatim} +% will use a different internal function if the optional argument +% is given than if it is not present. +% +% Note that three tests are available, depending on which outcome +% branches are required: \cs{IfNoValueTF}, \cs{IfNoValueT} and +% \cs{IfNoValueF}. +% +% As the \cs{IfNoValue(TF)} tests are expandable, it is possible to +% test these values later, for example at the point of typesetting or +% in an expansion context. +% +% It is important to note that |-NoValue-| is constructed such that it +% will \emph{not} match the simple text input |-NoValue-|, \emph{i.e.} +% that +% \begin{verbatim} +% \IfNoValueTF{-NoValue-} +% \end{verbatim} +% will be logically \texttt{false}. +% +% When two optional arguments follow each other (a syntax we typically +% discourage), it can make sense to allow users of the command to +% specify only the second argument by providing an empty first +% argument. Rather than testing separately for emptyness and for +% |-NoValue-| it is then best to use the argument type~|O| with an +% empty default value, and simply test for emptyness using the +% \pkg{expl3} conditional \cs{tl_if_blank:nTF} or its \pkg{etoolbox} +% analogue \tn{ifblank}. +% \end{function} +% +% \begin{function}[EXP]{\IfValueT, \IfValueF, \IfValueTF} +% \begin{syntax} +% \cs{IfValueTF} \Arg{argument} \Arg{true code} \Arg{false code} +% \end{syntax} +% The reverse form of the \cs{IfNoValue(TF)} tests are also available +% as \cs{IfValue(TF)}. The context will determine which logical +% form makes the most sense for a given code scenario. +% \end{function} +% +% \begin{variable}{\BooleanFalse, \BooleanTrue} +% The \texttt{true} and \texttt{false} flags set when searching for +% an optional character (using \texttt{s} or \texttt{t\meta{char}}) have +% names which are accessible outside of code blocks. +% \end{variable} +% +% \begin{function}[EXP]{\IfBooleanT, \IfBooleanF, \IfBooleanTF} +% \begin{syntax} +% \cs{IfBooleanTF} \Arg{argument} \Arg{true code} \Arg{false code} +% \end{syntax} +% Used to test if \meta{argument} (|#1|, |#2|, \emph{etc.}) is +% \cs{BooleanTrue} or \cs{BooleanFalse}. For example +% \begin{verbatim} +% \NewDocumentCommand \foo { s m } +% { +% \IfBooleanTF {#1} +% { \DoSomethingWithStar {#2} } +% { \DoSomethingWithoutStar {#2} } +% } +% \end{verbatim} +% checks for a star as the first argument, then chooses the action to +% take based on this information. +% \end{function} +% +% \subsection{Argument processors} +% \label{sec:processors} +% +% \pkg{xparse} introduces the idea of an argument processor, which is +% applied to an argument \emph{after} it has been grabbed by the +% underlying system but before it is passed to \meta{code}. An argument +% processor can therefore be used to regularise input at an early stage, +% allowing the internal functions to be completely independent of input +% form. Processors are applied to user input and to default values for +% optional arguments, but \emph{not} to the special |-NoValue-| marker. +% +% Each argument processor is specified by the syntax +% \texttt{>}\marg{processor} in the argument specification. Processors +% are applied from right to left, so that +% \begin{verbatim} +% >{\ProcessorB} >{\ProcessorA} m +% \end{verbatim} +% would apply \cs{ProcessorA} +% followed by \cs{ProcessorB} to the tokens grabbed by the \texttt{m} +% argument. +% +% \begin{variable}{\ProcessedArgument} +% \pkg{xparse} defines a very small set of processor functions. In the +% main, it is anticipated that code writers will want to create their +% own processors. These need to accept one argument, which is the +% tokens as grabbed (or as returned by a previous processor function). +% Processor functions should return the processed argument as the +% variable \cs{ProcessedArgument}. +% \end{variable} +% +% \begin{function}{\ReverseBoolean} +% \begin{syntax} +% \cs{ReverseBoolean} +% \end{syntax} +% This processor reverses the logic of \cs{BooleanTrue} and +% \cs{BooleanFalse}, so that the example from earlier would become +% \begin{verbatim} +% \NewDocumentCommand \foo { > { \ReverseBoolean } s m } +% { +% \IfBooleanTF #1 +% { \DoSomethingWithoutStar {#2} } +% { \DoSomethingWithStar {#2} } +% } +% \end{verbatim} +% \end{function} +% +% \begin{function}[updated = 2012-02-12]{\SplitArgument} +% \begin{syntax} +% \cs{SplitArgument} \Arg{number} \Arg{token(s)} +% \end{syntax} +% This processor splits the argument given at each occurrence of the +% \meta{tokens} up to a maximum of \meta{number} tokens (thus +% dividing the input into $\text{\meta{number}} + 1$ parts). +% An error is given if too many \meta{tokens} are present in the +% input. The processed input is placed inside +% $\text{\meta{number}} + 1$ sets of braces for further use. +% If there are fewer than \Arg{number} of \Arg{tokens} in the argument +% then |-NoValue-| markers are added at the end of the processed +% argument. +% \begin{verbatim} +% \NewDocumentCommand \foo +% { > { \SplitArgument { 2 } { ; } } m } +% { \InternalFunctionOfThreeArguments #1 } +% \end{verbatim} +% If only a single character \meta{token} is used for the split, any +% category code $13$ (active) character matching the \meta{token} will +% be replaced before the split takes place. +% Spaces are trimmed at each end of each item parsed. +% \end{function} +% +% \begin{function}{\SplitList} +% \begin{syntax} +% \cs{SplitList} \Arg{token(s)} +% \end{syntax} +% This processor splits the argument given at each occurrence of the +% \meta{token(s)} where the number of items is not fixed. Each item is +% then wrapped in braces within |#1|. The result is that the +% processed argument can be further processed using a mapping function. +% \begin{verbatim} +% \NewDocumentCommand \foo +% { > { \SplitList { ; } } m } +% { \MappingFunction #1 } +% \end{verbatim} +% If only a single character \meta{token} is used for the split, any +% category code $13$ (active) character matching the \meta{token} will +% be replaced before the split takes place. +% Spaces are trimmed at each end of each item parsed. +% \end{function} +% +% \begin{function}[EXP]{\ProcessList} +% \begin{syntax} +% \cs{ProcessList} \Arg{list} \Arg{function} +% \end{syntax} +% To support \cs{SplitList}, the function \cs{ProcessList} is available +% to apply a \meta{function} to every entry in a \meta{list}. The +% \meta{function} should absorb one argument: the list entry. For example +% \begin{verbatim} +% \NewDocumentCommand \foo +% { > { \SplitList { ; } } m } +% { \ProcessList {#1} { \SomeDocumentFunction } } +% \end{verbatim} +% +% \textbf{This function is experimental.} +% \end{function} +% +% \begin{function}{\TrimSpaces} +% \begin{syntax} +% \cs{TrimSpaces} +% \end{syntax} +% Removes any leading and trailing spaces (tokens with character code~$32$ +% and category code~$10$) for the ends of the argument. Thus for example +% declaring a function +% \begin{verbatim} +% \NewDocumentCommand \foo +% { > { \TrimSpaces } m } +% { \showtokens {#1} } +% \end{verbatim} +% and using it in a document as +% \begin{verbatim} +% \foo{ hello world } +% \end{verbatim} +% will show \texttt{hello world} at the terminal, with the space at each +% end removed. \cs{TrimSpaces} will remove multiple spaces from the ends of +% the input in cases where these have been included such that the standard +% \TeX{} conversion of multiple spaces to a single space does not apply. +% +% \textbf{This function is experimental.} +% \end{function} +% +% \subsection{Fully-expandable document commands} +% +% There are \emph{very rare} occasion when it may be useful to create +% functions using a fully-expandable argument grabber. To support this, +% \pkg{xparse} can create expandable functions as well as the usual +% robust ones. This imposes a number of restrictions on the nature of +% the arguments accepted by a function, and the code it implements. +% This facility should only be used when \emph{absolutely necessary}; +% if you do not understand when this might be, \emph{do not use these +% functions}! +% +% \begin{function} +% { +% \NewExpandableDocumentCommand , +% \RenewExpandableDocumentCommand , +% \ProvideExpandableDocumentCommand , +% \DeclareExpandableDocumentCommand +% } +% \begin{syntax} +% \cs{NewExpandableDocumentCommand} +% ~~~~\meta{function} \Arg{arg spec} \Arg{code} +% \end{syntax} +% This family of commands is used to create a document-level \meta{function}, +% which will grab its arguments in a fully-expandable manner. The +% argument specification for the function is given by \meta{arg spec}, +% and the function will execute \meta{code}. In general, \meta{code} will +% also be fully expandable, although it is possible that this will +% not be the case (for example, a function for use in a table might +% expand so that \cs{omit} is the first non-expandable non-space token). +% +% Parsing arguments expandably imposes a number of restrictions on +% both the type of arguments that can be read and the error checking +% available: +% \begin{itemize} +% \item The last argument (if any are present) must be one of the +% mandatory types \texttt{m}, \texttt{r}, \texttt{R}, \texttt{l} +% or~\texttt{u}. +% \item All short arguments appear before long arguments. +% \item The mandatory argument types \texttt{l} and \texttt{u} may +% not be used after optional arguments. +% \item The optional argument types \texttt{g} +% and \texttt{G} are not available. +% \item The \enquote{verbatim} argument type \texttt{v} is not available. +% \item Argument processors (using \texttt{>}) are not available. +% \item It is not possible to differentiate between, for example +% |\foo[| and |\foo{[}|: in both cases the \texttt{[} will be +% interpreted as the start of an optional argument. As a +% result, checking for optional arguments is less robust than +% in the standard version. +% \end{itemize} +% \pkg{xparse} will issue an error if an argument specifier is given +% which does not conform to the first six requirements. The last +% item is an issue when the function is used, and so is beyond the +% scope of \pkg{xparse} itself. +% \end{function} +% +% \subsection{Access to the argument specification} +% +% The argument specifications for document commands and environments are +% available for examination and use. +% +% \begin{function}{\GetDocumentCommandArgSpec, \GetDocumentEnvironmentArgSpec} +% \begin{syntax} +% \cs{GetDocumentCommandArgSpec} \meta{function} +% \cs{GetDocumentEnvironmentArgSpec} \meta{environment} +% \end{syntax} +% These functions transfer the current argument specification for the +% requested \meta{function} or \meta{environment} into the token list +% variable \cs{ArgumentSpecification}. If the \meta{function} or +% \meta{environment} has no known argument specification then an error +% is issued. The assignment to \cs{ArgumentSpecification} is local to +% the current \TeX{} group. +% \end{function} +% +% \begin{function} +% {\ShowDocumentCommandArgSpec, \ShowDocumentEnvironmentArgSpec} +% \begin{syntax} +% \cs{ShowDocumentCommandArgSpec} \meta{function} +% \cs{ShowDocumentEnvironmentArgSpec} \meta{environment} +% \end{syntax} +% These functions show the current argument specification for the +% requested \meta{function} or \meta{environment} at the terminal. If +% the \meta{function} or \meta{environment} has no known argument +% specification then an error is issued. +% \end{function} +% +% \section{Load-time options} +% +% \DescribeOption{log-declarations} +% The package recognises the load-time option \texttt{log-declarations}, +% which is a key--value option taking the value \texttt{true} and +% \texttt{false}. By default, the option is set to \texttt{false}, meaning +% that no command or environment declared is logged. By loading +% \pkg{xparse} using +% \begin{verbatim} +% \usepackage[log-declarations=true]{xparse} +% \end{verbatim} +% each new, declared or renewed command or environment is logged. +% +% \end{documentation} +% +% \begin{implementation} +% +% \section{\pkg{xparse} implementation} +% +% \begin{macrocode} +%<*package> +% \end{macrocode} +% +% \begin{macrocode} +%<@@=xparse> +% \end{macrocode} +% +% \begin{macrocode} +\ProvidesExplPackage{xparse}{2019-05-28}{} + {L3 Experimental document command parser} +% \end{macrocode} +% +% \subsection{Variables and constants} +% +% \begin{variable}{\c_@@_special_chars_seq} +% In \IniTeX{} mode, we store special characters in a sequence. +% Maybe |$| or |&| will have to be added later. +% \begin{macrocode} +%<*initex> +\seq_new:N \c_@@_special_chars_seq +\seq_set_split:Nnn \c_@@_special_chars_seq { } + { \ \\ \{ \} \# \^ \_ \% \~ } +% +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_arg_spec_tl} +% Holds the argument specification after normalization of shorthands. +% \begin{macrocode} +\tl_new:N \l_@@_arg_spec_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_args_tl} +% Token list variable for grabbed arguments. +% \begin{macrocode} +\tl_new:N \l_@@_args_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_args_i_tl, \l_@@_args_ii_tl} +% Hold the modified arguments when dealing with default values or +% processors. +% \begin{macrocode} +\tl_new:N \l_@@_args_i_tl +\tl_new:N \l_@@_args_ii_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_current_arg_int} +% The number of the current argument being set up: this is used to +% make sure there are at most 9 arguments, then for creating the +% expandable auxiliary functions and knowing how many arguments the +% code function should take. +% \begin{macrocode} +\int_new:N \l_@@_current_arg_int +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_defaults_bool, \l_@@_defaults_tl} +% The boolean indicates whether there are any argument with default +% value other than |-NoValue-|; the token list holds the code to +% determine these default values in terms of other arguments. +% \begin{macrocode} +\bool_new:N \l_@@_defaults_bool +\tl_new:N \l_@@_defaults_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_environment_bool} +% Generating environments uses the same mechanism as generating functions. +% However, full processing of arguments is always needed for environments, +% and so the function-generating code needs to know this. +% This variable is also used at run time to give correct error messages. +% \begin{macrocode} +\bool_new:N \l_@@_environment_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_environment_str} +% Name of the environment, used at definition time and at run time. +% \begin{macrocode} +\str_new:N \l_@@_environment_str +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_expandable_bool} +% Used to indicate if an expandable command is begin generated, as this +% affects both the acceptable argument types and how they are implemented. +% \begin{macrocode} +\bool_new:N \l_@@_expandable_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_expandable_aux_name_tl} +% Used to create pretty-printing names for the auxiliaries: although the +% immediate definition does not vary, the full expansion does and so it +% does not count as a constant. +% \begin{macrocode} +\tl_new:N \l_@@_expandable_aux_name_tl +\tl_set:Nn \l_@@_expandable_aux_name_tl + { + \l_@@_function_tl \c_space_tl + ( arg~ \int_use:N \l_@@_current_arg_int ) + } +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\g_@@_grabber_int} +% Used (in exceptional cases) to get unique names for grabbers used by +% expandable commands. +% \begin{macrocode} +\int_new:N \g_@@_grabber_int +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_fn_tl} +% For passing the pre-formed name of the auxiliary to be used as the +% parsing function. +% \begin{macrocode} +\tl_new:N \l_@@_fn_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_fn_code_tl} +% For passing the pre-formed name of the auxiliary that contains the +% actual code. +% \begin{macrocode} +\tl_new:N \l_@@_fn_code_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_function_tl} +% Holds the control sequence name of the function currently being +% defined: used to avoid passing this as an argument and to avoid repeated +% use of \cs{cs_to_str:N}. +% \begin{macrocode} +\tl_new:N \l_@@_function_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_grab_expandably_bool} +% When defining a non-expandable command, indicates whether the +% arguments can all safely be grabbed by expandable grabbers. This is +% to support abuses of \pkg{xparse} that use protected functions +% inside csname constructions. +% \begin{macrocode} +\bool_new:N \l_@@_grab_expandably_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_obey_spaces_bool} +% For trailing optionals. +% \begin{macrocode} +\bool_new:N \l_@@_obey_spaces_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_last_delimiters_tl} +% Holds the delimiters (first tokens) of all optional arguments since +% the previous mandatory argument, to warn about cases where it would +% be impossible to omit optional arguments completely because the +% following mandatory argument has the same delimiter as one of the +% optional arguments. +% \begin{macrocode} +\tl_new:N \l_@@_last_delimiters_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_long_bool} +% Used to indicate that an argument is long, on a per-argument basis. +% \begin{macrocode} +\bool_new:N \l_@@_long_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_m_args_int} +% The number of \texttt{m} arguments: if this is the same as the total +% number of arguments, then a short-cut can be taken in the creation of +% the grabber code. +% \begin{macrocode} +\int_new:N \l_@@_m_args_int +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_prefixed_bool} +% When preparing the signature of non-expandable commands, indicates +% that the current argument is affected by a processor or by |+| +% (namely is long). +% \begin{macrocode} +\bool_new:N \l_@@_prefixed_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_process_all_tl, \l_@@_process_one_tl, \l_@@_process_some_bool} +% When preparing the signature, the processors that will be applied to +% a given argument are collected in \cs{l_@@_process_one_tl}, while +% \cs{l_@@_process_all_tl} contains processors for all arguments. The +% boolean indicates whether there are any processors (to bypass the +% whole endeavour otherwise). +% \begin{macrocode} +\tl_new:N \l_@@_process_all_tl +\tl_new:N \l_@@_process_one_tl +\bool_new:N \l_@@_process_some_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_saved_args_tl} +% Stores \cs{l_@@_args_tl} to deal with space-trimming of +% \texttt{b}-type arguments. +% \begin{macrocode} +\tl_new:N \l_@@_saved_args_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_signature_tl} +% Used when constructing the signature (code for argument grabbing) to +% hold what will become the implementation of the main function. +% When arguments are grabbed (at point of use of the command/environment), +% it also stores the code for grabbing the remaining arguments. +% \begin{macrocode} +\tl_new:N \l_@@_signature_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable} +% {\l_@@_some_obey_spaces_bool, \l_@@_some_long_bool, \l_@@_some_short_bool} +% These flags are set while normalizing the argument specification. +% The \texttt{obey_spaces} one is used to detect when |!| is used on +% an argument that is not a trailing optional argument. +% The other two are used to check whether all short arguments appear +% before long arguments: this is needed to grab arguments expandably. +% As soon as the first long argument is seen (other than +% \texttt{t}-type, whose long status is ignored) the +% \texttt{some_long} flag is set. The \texttt{some_short} flag is +% used for expandable commands, to know whether to define a short +% auxiliary too. +% \begin{macrocode} +\bool_new:N \l_@@_some_obey_spaces_bool +\bool_new:N \l_@@_some_long_bool +\bool_new:N \l_@@_some_short_bool +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_tmp_prop, \l_@@_tmpa_tl, \l_@@_tmpb_tl} +% \begin{macro}{\@@_tmp:w} +% Scratch space. +% \begin{macrocode} +\prop_new:N \l_@@_tmp_prop +\tl_new:N \l_@@_tmpa_tl +\tl_new:N \l_@@_tmpb_tl +\cs_new_eq:NN \@@_tmp:w ? +% \end{macrocode} +% \end{macro} +% \end{variable} +% +% \subsection{Declaring commands and environments} +% +% \begin{macro}{\@@_declare_cmd:Nnn, \@@_declare_expandable_cmd:Nnn} +% \begin{macro}{\@@_declare_cmd_aux:Nnn} +% \begin{macro} +% {\@@_declare_cmd_internal:Nnnn, \@@_declare_cmd_internal:cnxn} +% The main functions for creating commands set the appropriate flag then +% use the same internal code to do the definition. +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare_cmd:Nnn + { + \bool_set_false:N \l_@@_expandable_bool + \@@_declare_cmd_aux:Nnn + } +\cs_new_protected:Npn \@@_declare_expandable_cmd:Nnn + { + \bool_set_true:N \l_@@_expandable_bool + \@@_declare_cmd_aux:Nnn + } +% \end{macrocode} +% The first stage is to log information, both for the user in the log and +% for programmatic use in a property list of all declared commands. +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare_cmd_aux:Nnn #1#2#3 + { + \cs_if_exist:NTF #1 + { + \__kernel_msg_info:nnxx { xparse } { redefine-command } + { \token_to_str:N #1 } { \tl_to_str:n {#2} } + } + { + \bool_lazy_or:nnT + { \cs_if_exist_p:c { \cs_to_str:N #1 ~ code } } + { \cs_if_exist_p:c { \cs_to_str:N #1 ~ defaults } } + { + \__kernel_msg_warning:nnx { xparse } { unsupported-let } + { \token_to_str:N #1 } + } + \__kernel_msg_info:nnxx { xparse } { define-command } + { \token_to_str:N #1 } { \tl_to_str:n {#2} } + } + \bool_set_false:N \l_@@_environment_bool + \@@_declare_cmd_internal:Nnnn #1 {#2} {#3} { } + } +% \end{macrocode} +% At definition time, the variable \cs{l_@@_fn_tl} is only used for error messages. +% The real business of defining a document command starts with setting up +% the appropriate name, then normalizing the argument specification to get rid of +% shorthands. +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare_cmd_internal:Nnnn #1#2#3#4 + { + \tl_set:Nx \l_@@_function_tl { \cs_to_str:N #1 } + \tl_set:Nx \l_@@_fn_tl + { \exp_not:c { \l_@@_function_tl \c_space_tl } } + \@@_normalize_arg_spec:n {#2} + \exp_args:No \@@_prepare_signature:n \l_@@_arg_spec_tl + \@@_declare_cmd_code:Nnn #1 {#2} {#3} + #4 + \@@_break_point:n {#2} + } +\cs_generate_variant:Nn \@@_declare_cmd_internal:Nnnn { cnx } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_break_point:n} +% A marker used to escape from creating a definition if necessary. +% \begin{macrocode} +\cs_new_eq:NN \@@_break_point:n \use_none:n +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_declare_cmd_code:Nnn} +% \begin{macro} +% {\@@_declare_cmd_code_aux:Nnn, \@@_declare_cmd_code_expandable:Nnn} +% The appropriate auxiliary is called. +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare_cmd_code:Nnn + { + \bool_if:NTF \l_@@_grab_expandably_bool + { \@@_declare_cmd_code_expandable:Nnn } + { \@@_declare_cmd_code_aux:Nnn } + } +% \end{macrocode} +% Standard functions call \cs{@@_start:nNNnnn}, which receives the +% argument specification, an auxiliary used for +% grabbing arguments, an auxiliary containing the code, and then the +% signature, default arguments, and processors. +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare_cmd_code_aux:Nnn #1#2#3 + { + \cs_generate_from_arg_count:cNnn + { \l_@@_function_tl \c_space_tl code } + \cs_set_protected:Npn \l_@@_current_arg_int {#3} + \cs_set_protected_nopar:Npx #1 + { + \bool_if:NTF \l_@@_environment_bool + { + \@@_start_env:nnnnn { \exp_not:n {#2} } + { \l_@@_environment_str } + } + { + \@@_start:nNNnnn { \exp_not:n {#2} } + \exp_not:c { \l_@@_function_tl \c_space_tl } + \exp_not:c { \l_@@_function_tl \c_space_tl code } + } + { \exp_not:o \l_@@_signature_tl } + { + \bool_if:NT \l_@@_defaults_bool + { \exp_not:o \l_@@_defaults_tl } + } + { + \bool_if:NT \l_@@_process_some_bool + { \exp_not:o \l_@@_process_all_tl } + } + } + } +% \end{macrocode} +% Expandable functions and functions whose arguments can be grabbed +% expandably call \cs{@@_start_expandable:nNNNNn}, which receives the +% argument specification, four auxiliaries (two for grabbing arguments, one for +% the code, and one for default arguments), and finally the signature. +% Non-expandable functions that take this branch should nevertheless +% be protected, as well as their \texttt{code} function. They will +% only be expanded in contexts such as constructing a csname. +% The two grabbers (named after the function with one or two spaces) +% are needed when there are both short and long arguments; otherwise +% the same grabber is included twice in the definition. If all +% arguments are long or all are short the (only) grabber is defined +% correspondingly to be long/short. Otherwise two grabbers are +% defined, one long, one short. +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare_cmd_code_expandable:Nnn #1#2#3 + { + \exp_args:Ncc \cs_generate_from_arg_count:NNnn + { \l_@@_function_tl \c_space_tl code } + { cs_set \bool_if:NF \l_@@_expandable_bool { _protected } :Npn } + \l_@@_current_arg_int {#3} + \bool_if:NT \l_@@_defaults_bool + { + \use:x + { + \cs_generate_from_arg_count:cNnn + { \l_@@_function_tl \c_space_tl defaults } + \cs_set:Npn \l_@@_current_arg_int + { \exp_not:o \l_@@_defaults_tl } + } + } + \bool_if:NTF \l_@@_expandable_bool + { \cs_set_nopar:Npx } { \cs_set_protected_nopar:Npx } #1 + { + \exp_not:N \@@_start_expandable:nNNNNn + { \exp_not:n {#2} } + \exp_not:c { \l_@@_function_tl \c_space_tl } + \exp_not:c + { + \l_@@_function_tl \c_space_tl + \bool_if:NT \l_@@_some_short_bool + { \bool_if:NT \l_@@_some_long_bool { \c_space_tl } } + } + \exp_not:c { \l_@@_function_tl \c_space_tl code } + \bool_if:NTF \l_@@_defaults_bool + { \exp_not:c { \l_@@_function_tl \c_space_tl defaults } } + { ? } + { \exp_not:o \l_@@_signature_tl } + } + \bool_if:NTF \l_@@_some_long_bool + { + \bool_if:NT \l_@@_some_short_bool + { + \cs_set_nopar:cpx { \l_@@_function_tl \c_space_tl \c_space_tl } + ##1##2 { ##1 {##2} } + } + \cs_set:cpx + } + { \cs_set_nopar:cpx } + { \l_@@_function_tl \c_space_tl } ##1##2 { ##1 {##2} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_declare_env:nnnn} +% \begin{macro}{\@@_declare_env_internal:nnnn} +% The lead-off to creating an environment is much the same as that for +% creating a command: issue the appropriate message, store the argument +% specification then hand off to an internal function. +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare_env:nnnn #1#2 + { + \str_set:Nx \l_@@_environment_str {#1} + \str_set:Nx \l_@@_environment_str + { \tl_trim_spaces:o { \l_@@_environment_str } } +%<*initex> + \cs_if_exist:cTF { environment~ \l_@@_environment_str } +% +%<*package> + \cs_if_exist:cTF { \l_@@_environment_str } +% + { + \__kernel_msg_info:nnxx { xparse } { redefine-environment } + { \l_@@_environment_str } { \tl_to_str:n {#2} } + } + { + \__kernel_msg_info:nnxx { xparse } { define-environment } + { \l_@@_environment_str } { \tl_to_str:n {#2} } + } + \bool_set_false:N \l_@@_expandable_bool + \bool_set_true:N \l_@@_environment_bool + \exp_args:NV \@@_declare_env_internal:nnnn + \l_@@_environment_str {#2} + } +% \end{macrocode} +% Creating a document environment requires a few more steps than creating +% a single command. In order to pass the arguments of the command to the +% end of the function, it is necessary to store the grabbed arguments. +% To do that, the function used at the end of the environment has to be +% redefined to contain the appropriate information. To minimize the amount +% of expansion at point of use, the code here is expanded now as well as +% when used. +% The last argument of \cs{@@_declare_cmd_internal:Nnnn} is only run +% if the definition succeeded. In package mode this ensures that the +% original definition of the environment is not changed if the +% definition fails for any reason. This also avoids an error when +% defining the \verb*|end aux | function when the user asks for more +% than $9$ arguments. +% \begin{macrocode} +\cs_new_protected:Npn \@@_declare_env_internal:nnnn #1#2#3#4 + { + \@@_declare_cmd_internal:cnxn { environment~ #1 } {#2} + { + \cs_set_nopar:Npx \exp_not:c { environment~ #1 ~end~aux } + { + \exp_not:N \exp_not:N \exp_not:c { environment~ #1~end~aux~ } + \exp_not:n { \exp_not:o \l_@@_args_tl } + } + \exp_not:n {#3} + } + { + \cs_set_nopar:cpx { environment~ #1 ~end } + { \exp_not:c { environment~ #1 ~end~aux } } + \cs_generate_from_arg_count:cNnn + { environment~ #1 ~end~aux~ } \cs_set:Npn + \l_@@_current_arg_int {#4} +%<*package> + \cs_set_eq:cc {#1} { environment~ #1 } + \cs_set_eq:cc { end #1 } { environment~ #1 ~end } +% + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Structure of \pkg{xparse} commands} +% +% \begin{macro}{\@@_start_env:nnnnn, \@@_start:nNNnnn} +% For error messages that occur during run-time when getting arguments +% of environments it is necessary to keep track of the environment +% name. We begin non-expandable commands with a token equal to +% \cs{scan_stop:}, whose name gives a reasonable error message if the +% command is used inside a csname and protects against +% \texttt{f}-expansion. This is useless for environments since +% \cs{begin} is already not expandable. +% \begin{macrocode} +\cs_new_protected:Npn \@@_start_env:nnnnn #1#2 + { +%<*package> + \cs_if_exist_use:N \conditionally@traceoff +% + \str_set:Nn \l_@@_environment_str {#2} + \bool_set_true:N \l_@@_environment_bool + \@@_start_aux:ccnnnn + { environment~ \l_@@_environment_str \c_space_tl } + { environment~ \l_@@_environment_str \c_space_tl code } + {#1} + } +\cs_new_protected:Npx \@@_start:nNNnnn #1#2#3 + { +%<*package> + \exp_not:n { \cs_if_exist_use:N \conditionally@traceoff } +% + \exp_not:c { xparse~function~is~not~expandable } + \exp_not:n { \bool_set_false:N \l_@@_environment_bool } + \exp_not:N \@@_start_aux:NNnnnn + #2 #3 {#1} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_start_aux:NNnnnn, \@@_start_aux:ccnnnn} +% This sets up a few variables to minimize the boilerplate code +% included in all \pkg{xparse}-defined commands. It then runs the +% grabbers~|#4|. Again, the argument specification |#1| is only for +% diagnostics. +% \begin{macrocode} +\cs_new_protected:Npn \@@_start_aux:NNnnnn #1#2#3#4#5#6 + { + \tl_clear:N \l_@@_args_tl + \tl_set:Nn \l_@@_fn_tl {#1} + \tl_set:Nn \l_@@_fn_code_tl {#2} + \tl_set:Nn \l_@@_defaults_tl {#5} + \tl_set:Nn \l_@@_process_all_tl {#6} + #4 \@@_run_code: + } +\cs_generate_variant:Nn \@@_start_aux:NNnnnn { cc } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_run_code:} +% After arguments are grabbed, this function is responsible for +% inserting default values, running processors, and finally running +% the code. +% \begin{macrocode} +\cs_new_protected:Npn \@@_run_code: + { + \tl_if_empty:NF \l_@@_defaults_tl { \@@_defaults: } + \tl_if_empty:NF \l_@@_process_all_tl { \@@_args_process: } +%<*package> + \cs_if_exist_use:N \conditionally@traceon +% + \exp_after:wN \l_@@_fn_code_tl \l_@@_args_tl + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_defaults:} +% \begin{macro}{\@@_defaults_def:, \@@_defaults_def:nn, \@@_defaults_def:nnn} +% \begin{macro}{\@@_defaults_aux:, \@@_defaults_error:w} +% First construct \cs{@@_tmp:w} (see below) that will receive +% the arguments found so far and determine default values for any +% missing argument. Then call it repeatedly until the set of +% arguments stabilizes. Since that could lead to an infinite loop we +% only call it up to nine times, the maximal number needed for +% stabilization if there is a chain of arguments that depend on each +% other. If that fails to stabilize raise an error. +% \begin{macrocode} +\cs_new_protected:Npn \@@_defaults: + { + \@@_defaults_def: + \tl_set_eq:NN \l_@@_args_i_tl \l_@@_args_tl + \@@_defaults_aux: \@@_defaults_aux: \@@_defaults_aux: + \@@_defaults_aux: \@@_defaults_aux: \@@_defaults_aux: + \@@_defaults_aux: \@@_defaults_aux: \@@_defaults_aux: + \@@_defaults_error:w + \q_recursion_stop + \tl_set_eq:NN \l_@@_args_tl \l_@@_args_i_tl + } +\cs_new_protected:Npn \@@_defaults_aux: + { + \tl_set:Nx \l_@@_args_ii_tl + { \exp_after:wN \@@_tmp:w \l_@@_args_i_tl } + \tl_if_eq:NNT \l_@@_args_ii_tl \l_@@_args_i_tl + { \use_none_delimit_by_q_recursion_stop:w } + \tl_set_eq:NN \l_@@_args_i_tl \l_@@_args_ii_tl + } +\cs_new_protected:Npn \@@_defaults_error:w \q_recursion_stop + { + \__kernel_msg_error:nnx { xparse } { loop-in-defaults } + { \@@_environment_or_command: } + } +% \end{macrocode} +% To construct \cs{@@_tmp:w}, first go through the arguments +% found and the corresponding defaults, building a token list with +% |{#|\meta{arg number}|}| for arguments found in the input (whose +% default will not be used) and otherwise +% |{|\cs{exp_not:n}\Arg{default}|}| for arguments whose default will +% be used. +% \begin{macrocode} +\cs_new_protected:Npn \@@_defaults_def: + { + \tl_clear:N \l_@@_tmpa_tl + \int_zero:N \l_@@_current_arg_int + \@@_tl_mapthread_function:NNN \l_@@_args_tl \l_@@_defaults_tl + \@@_defaults_def:nn + \cs_generate_from_arg_count:NNVo \@@_tmp:w \cs_set:Npn + \l_@@_current_arg_int \l_@@_tmpa_tl + } +\cs_generate_variant:Nn \cs_generate_from_arg_count:NNnn { NNVo } +\cs_new_protected:Npn \@@_defaults_def:nn + { + \int_incr:N \l_@@_current_arg_int + \exp_args:NV \@@_defaults_def:nnn \l_@@_current_arg_int + } +\cs_new_protected:Npn \@@_defaults_def:nnn #1#2#3 + { + \tl_put_right:Nx \l_@@_tmpa_tl + { + { + \exp_not:N \exp_not:n + { + \tl_if_novalue:nTF {#2} + { \exp_not:o {#3} } + { \exp_not:n { ## #1 } } + } + } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_args_process:} +% \begin{macro}{\@@_args_process_loop:nn, \@@_args_process_aux:n, \@@_args_process_aux:nn} +% Loop through arguments (stored in \cs{l_@@_args_tl}) and the +% corresponding processors (in \cs{l_@@_process_all_tl}) +% simultaneously, apply all processors for each argument and store the +% result back into \cs{l_@@_args_tl}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_args_process: + { + \tl_clear:N \l_@@_args_ii_tl + \@@_tl_mapthread_function:NNN + \l_@@_args_tl + \l_@@_process_all_tl + \@@_args_process_loop:nn + \tl_set_eq:NN \l_@@_args_tl \l_@@_args_ii_tl + } +\cs_new_protected:Npn \@@_args_process_loop:nn #1#2 + { + \tl_set:Nn \ProcessedArgument {#1} + \tl_if_novalue:nF {#1} + { \tl_map_function:nN {#2} \@@_args_process_aux:n } + \tl_put_right:No \l_@@_args_ii_tl + { \exp_after:wN { \ProcessedArgument } } + } +\cs_new_protected:Npn \@@_args_process_aux:n + { \exp_args:No \@@_args_process_aux:nn { \ProcessedArgument } } +\cs_new_protected:Npn \@@_args_process_aux:nn #1#2 { #2 {#1} } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_start_expandable:nNNNNn} +% This is called for all expandable commands. |#6| is the signature, +% responsible for grabbing arguments. |#5| is used to determine +% default values (or is |?| if there are none). |#4| is the code to run. +% |#2|~and~|#3| are functions (named after the command) that grab a single +% argument in the input stream (|#3|~is~short). The argument specification |#1| is +% only used by diagnostic functions. +% \begin{macrocode} +\cs_new:Npn \@@_start_expandable:nNNNNn #1#2#3#4#5#6 + { #6 \@@_end_expandable:NNw #5 #4 \q_@@ #2#3 } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_end_expandable:NNw} +% \begin{macro}[EXP]{\@@_end_expandable_aux:w} +% \begin{macro}[EXP]{\@@_end_expandable_aux:nNNNN} +% \begin{macro}[EXP]{\@@_end_expandable_defaults:nnnNNn} +% \begin{macro}[EXP]{\@@_end_expandable_defaults:nnw} +% \begin{macro}[EXP]{\@@_end_expandable_defaults:nw} +% Followed by a function |#1| to determine default values (or |?| if +% there are no defaults), the code +% |#2|, arguments that have been grabbed, then \cs{q_@@} and two generic +% grabbers. The idea to find default values is similar to the +% non-expandable case but we cannot define an auxiliary function, so +% at every step in the loop we need to go through all arguments +% searching for which ones started out as |-NoValue-| and replacing +% these by the newly computed values. In fact we need to keep track +% of three versions of all arguments: the original version, the +% previous version with default values, and the currently built +% version (first argument of \cs{@@_end_expandable_defaults:nnnNNn}). +% \begin{macrocode} +\cs_new:Npn \@@_end_expandable:NNw #1#2 + { \@@_end_expandable_aux:w #1#2 \prg_do_nothing: } +\cs_new:Npn \@@_end_expandable_aux:w #1#2#3 \q_@@ + { \exp_args:No \@@_end_expandable_aux:nNNNN {#3} #1 #2 } +\cs_new:Npn \@@_end_expandable_aux:nNNNN #1#2#3#4#5 + { + \token_if_eq_charcode:NNT ? #2 { \exp_after:wN \use_iv:nnnn } + \@@_end_expandable_defaults:nnnNNn {#1} { } {#1} #2#3 + { } { } { } { } { } { } { } { } { } { } + { + \__kernel_msg_expandable_error:nnf + { xparse } { loop-in-defaults } + { \exp_args:Nf \tl_trim_spaces:n { \token_to_str:N #4 } } + \use_iv:nnnn + } + \q_stop + } +\cs_new:Npn \@@_end_expandable_defaults:nnnNNn #1#2#3#4#5#6 + { + #6 + \str_if_eq:nnTF {#1} {#2} + { \use_i_delimit_by_q_stop:nw { #5 #1 } } + { + \exp_args:No \@@_tl_mapthread_function:nnN + { #4 #1 } {#3} + \@@_end_expandable_defaults:nnw + \@@_end_expandable_defaults:nnnNNn { } {#1} {#3} #4 #5 + } + } +\cs_new:Npn \@@_end_expandable_defaults:nnw #1#2 + { + \tl_if_novalue:nTF {#2} + { \exp_args:No \@@_end_expandable_defaults:nw {#1} } + { \@@_end_expandable_defaults:nw {#2} } + } +\cs_new:Npn \@@_end_expandable_defaults:nw + #1#2 \@@_end_expandable_defaults:nnnNNn #3 + { #2 \@@_end_expandable_defaults:nnnNNn { #3 {#1} } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Normalizing the argument specifications} +% +% The goal here is to expand aliases and check that the argument +% specification is valid before the main parsing run. If it is not +% valid the entire set up is abandoned to avoid any strange internal +% errors. A function is provided for each argument type that will grab +% any extra data items and call the loop function after performing the +% following checks and tasks. +% \begin{itemize} +% \item Check that each argument has the correct number of data items +% associated with it, and that where a single character is required, +% one has actually been supplied. +% \item Check that processors and the markers~|+| and~|!| are followed +% by an argument for which they make sense, and are not redundant. +% \item Check the absence of forbidden types for expandable commands, +% namely \texttt{G}/\texttt{v} always, and \texttt{l}/\texttt{u} +% after optional arguments (\pkg{xparse} may have inserted braces +% due to a failed search for an optional argument). +% \item Check that no optional argument is followed by a mandatory +% argument with the same delimiter, as otherwise the optional +% argument could never be omitted. +% \item Keep track in \cs{l_@@_some_long_bool} and +% \cs{l_@@_some_short_bool} of whether the command has some +% long/short arguments. +% \item Keep track in \cs{l_@@_grab_expandably_bool} of whether all +% arguments are \texttt{m}/\texttt{l}/\texttt{u} type and short +% arguments appear before long ones, in which case they can be +% grabbed expandably just as safely as they could be grabbed +% nonexpandably. Regardless of that, arguments of expandable +% commands will be grabbed expandably and arguments of environments +% will not (because the list of arguments built by non-expandable +% grabbing is used to pass them to the end-environment code). +% \end{itemize} +% Further checks happen at the end of the loop: +% \begin{itemize} +% \item that there are at most $9$ arguments; +% \item that an expandable command does not end with an optional +% argument (this case is detected by using the fact that +% \cs{l_@@_last_delimiters_tl} is cleared by every mandatory argument +% and filled by every optional argument). +% \end{itemize} +% +% \begin{macro}{\@@_normalize_arg_spec:n} +% \begin{macro}{\@@_normalize_arg_spec_loop:n} +% Loop through the argument specification, calling an auxiliary +% specific to each argument type. If any argument is unknown stop the +% definition. +% \begin{macrocode} +\cs_new_protected:Npn \@@_normalize_arg_spec:n #1 + { + \int_zero:N \l_@@_current_arg_int + \tl_clear:N \l_@@_last_delimiters_tl + \tl_clear:N \l_@@_arg_spec_tl + \bool_set_true:N \l_@@_grab_expandably_bool + \bool_set_false:N \l_@@_obey_spaces_bool + \bool_set_false:N \l_@@_long_bool + \bool_set_false:N \l_@@_some_obey_spaces_bool + \bool_set_false:N \l_@@_some_long_bool + \bool_set_false:N \l_@@_some_short_bool + \@@_normalize_arg_spec_loop:n #1 + \q_recursion_tail \q_recursion_tail \q_recursion_tail \q_recursion_stop + \int_compare:nNnT \l_@@_current_arg_int > 9 + { + \__kernel_msg_error:nnxx { xparse } { too-many-arguments } + { \@@_environment_or_command: } { \tl_to_str:n {#1} } + \@@_bad_def:wn + } + \bool_if:NT \l_@@_expandable_bool + { + \tl_if_empty:NF \l_@@_last_delimiters_tl + { + \__kernel_msg_error:nnxx { xparse } { expandable-ending-optional } + { \iow_char:N \\ \l_@@_function_tl } { \tl_to_str:n {#1} } + \@@_bad_def:wn + } + } + \bool_if:NT \l_@@_expandable_bool + { \bool_set_true:N \l_@@_grab_expandably_bool } + \bool_if:NT \l_@@_environment_bool + { \bool_set_false:N \l_@@_grab_expandably_bool } + } +\cs_new_protected:Npn \@@_normalize_arg_spec_loop:n #1 + { + \quark_if_recursion_tail_stop:n {#1} + \int_incr:N \l_@@_current_arg_int + \cs_if_exist_use:cF { @@_normalize_type_ \tl_to_str:n {#1} :w } + { + \__kernel_msg_error:nnxx { xparse } { unknown-argument-type } + { \@@_environment_or_command: } { \tl_to_str:n {#1} } + \@@_bad_def:wn + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \@@_normalize_type_d:w, +% \@@_normalize_type_e:w, +% \@@_normalize_type_g:w, +% \@@_normalize_type_o:w, +% \@@_normalize_type_O:w, +% \@@_normalize_type_r:w, +% \@@_normalize_type_s:w, +% } +% These argument types are aliases of more general ones, for example +% with the default argument |-NoValue-|. To easily insert that marker +% expanded in the definitions we call \cs{@@_tmp:w} with the argument +% |-NoValue-|. For argument types that need additional data, check +% that that data is present (not \cs{q_recursion_tail}) before +% proceeding. +% \begin{macrocode} +\cs_set_protected:Npn \@@_tmp:w #1 + { + \cs_new_protected:Npn \@@_normalize_type_d:w ##1##2 + { + \quark_if_recursion_tail_stop_do:nn {##2} { \@@_bad_arg_spec:wn } + \@@_normalize_type_D:w {##1} {##2} {#1} + } + \cs_new_protected:Npn \@@_normalize_type_e:w ##1 + { + \quark_if_recursion_tail_stop_do:nn {##1} { \@@_bad_arg_spec:wn } + \@@_normalize_type_E:w {##1} { } + } + \cs_new_protected:Npn \@@_normalize_type_g:w + { \@@_normalize_type_G:w {#1} } + \cs_new_protected:Npn \@@_normalize_type_o:w + { \@@_normalize_type_D:w [ ] {#1} } + \cs_new_protected:Npn \@@_normalize_type_O:w + { \@@_normalize_type_D:w [ ] } + \cs_new_protected:Npn \@@_normalize_type_r:w ##1##2 + { + \quark_if_recursion_tail_stop_do:nn {##2} { \@@_bad_arg_spec:wn } + \@@_normalize_type_R:w {##1} {##2} {#1} + } + \cs_new_protected:Npn \@@_normalize_type_s:w + { \@@_normalize_type_t:w * } + } +\exp_args:No \@@_tmp:w { \c_novalue_tl } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \@@_normalize_type_>:w, +% \@@_normalize_type_+:w, +% \@@_normalize_type_!:w, +% } +% Check that these prefixes have arguments, namely that the next token +% is not \cs{q_recursion_tail}, and remember to leave it after the +% looping macro. Processors are forbidden in expandable commands. +% If all is good, store the prefix in the cleaned up +% \cs{l_@@_arg_spec_tl}, and decrement the argument number as prefixes +% do not correspond to arguments. +% \begin{macrocode} +\cs_new_protected:cpn { @@_normalize_type_>:w } #1#2 + { + \quark_if_recursion_tail_stop_do:nn {#2} { \@@_bad_arg_spec:wn } + \bool_if:NT \l_@@_expandable_bool + { + \__kernel_msg_error:nnxx { xparse } { processor-in-expandable } + { \iow_char:N \\ \l_@@_function_tl } { \tl_to_str:n {#1} } + \@@_bad_def:wn + } + \tl_put_right:Nx \l_@@_arg_spec_tl { > { \tl_trim_spaces:n {#1} } } + \int_decr:N \l_@@_current_arg_int + \bool_set_false:N \l_@@_grab_expandably_bool + \@@_normalize_arg_spec_loop:n {#2} + } +\cs_new_protected:cpn { @@_normalize_type_+:w } #1 + { + \quark_if_recursion_tail_stop_do:nn {#1} { \@@_bad_arg_spec:wn } + \bool_if:NT \l_@@_long_bool + { + \__kernel_msg_error:nnxx { xparse } { two-markers } + { \@@_environment_or_command: } { + } + \@@_bad_def:wn + } + \bool_set_true:N \l_@@_long_bool + \int_decr:N \l_@@_current_arg_int + \@@_normalize_arg_spec_loop:n {#1} + } +\cs_new_protected:cpn { @@_normalize_type_!:w } #1 + { + \quark_if_recursion_tail_stop_do:nn {#1} { \@@_bad_arg_spec:wn } + \bool_if:NT \l_@@_obey_spaces_bool + { + \__kernel_msg_error:nnxx { xparse } { two-markers } + { \@@_environment_or_command: } { ! } + \@@_bad_def:wn + } + \bool_set_true:N \l_@@_obey_spaces_bool + \bool_set_true:N \l_@@_some_obey_spaces_bool + \int_decr:N \l_@@_current_arg_int + \@@_normalize_arg_spec_loop:n {#1} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \@@_normalize_type_D:w, +% \@@_normalize_type_E:w, +% \@@_normalize_type_G:w, +% \@@_normalize_type_t:w, +% } +% \begin{macro}{\@@_normalize_E_unique_check:w} +% Optional argument types. Check that all required data is present +% (and consists of single characters if applicable) and check for +% forbidden types for expandable commands. For \texttt{E}-type +% require that there is at least one embellishment, that each one is a +% single character, and that there aren't more optional arguments than +% embellishments; also remember that each embellishment counts as one +% argument for \cs{l_@@_current_arg_int}. Then in each case +% store the data in \cs{l_@@_arg_spec_tl}, and +% for later checks store in \cs{l_@@_last_delimiters_tl} the tokens +% whose presence determines whether there is an optional argument (for +% braces store |{}|, seen later as an empty delimiter). +% \begin{macrocode} +\cs_new_protected:Npn \@@_normalize_type_D:w #1#2#3 + { + \quark_if_recursion_tail_stop_do:nn {#3} { \@@_bad_arg_spec:wn } + \@@_single_char_check:n {#1} + \@@_single_char_check:n {#2} + \@@_add_arg_spec:n { D #1 #2 {#3} } + \tl_put_right:Nn \l_@@_last_delimiters_tl {#1} + \bool_set_false:N \l_@@_grab_expandably_bool + \@@_normalize_arg_spec_loop:n + } +\cs_new_protected:Npn \@@_normalize_type_E:w #1#2 + { + \quark_if_recursion_tail_stop_do:nn {#2} { \@@_bad_arg_spec:wn } + \tl_if_blank:nT {#1} { \@@_bad_arg_spec:wn } + \tl_map_function:nN {#1} \@@_single_char_check:n + \@@_normalize_E_unique_check:w #1 \q_nil \q_stop + \int_compare:nNnT { \tl_count:n {#2} } > { \tl_count:n {#1} } + { \@@_bad_arg_spec:wn } + \@@_add_arg_spec:n { E {#1} {#2} } + \tl_put_right:Nn \l_@@_last_delimiters_tl {#1} + \bool_set_false:N \l_@@_grab_expandably_bool + \int_add:Nn \l_@@_current_arg_int { \tl_count:n {#1} - 1 } + \@@_normalize_arg_spec_loop:n + } +\cs_new_protected:Npn \@@_normalize_E_unique_check:w #1#2 \q_stop + { + \quark_if_nil:NF #1 + { + \tl_if_in:nnT {#2} {#1} { \@@_bad_arg_spec:wn } + \@@_normalize_E_unique_check:w #2 \q_stop + } + } +\cs_new_protected:Npn \@@_normalize_type_G:w #1 + { + \quark_if_recursion_tail_stop_do:nn {#1} { \@@_bad_arg_spec:wn } + \@@_normalize_check_gv:N G + \@@_add_arg_spec:n { G {#1} } + \tl_put_right:Nn \l_@@_last_delimiters_tl { { } } + \@@_normalize_arg_spec_loop:n + } +\cs_new_protected:Npn \@@_normalize_type_t:w #1 + { + \quark_if_recursion_tail_stop_do:Nn #1 { \@@_bad_arg_spec:wn } + \@@_single_char_check:n {#1} + \tl_put_right:Nx \l_@@_arg_spec_tl + { + \bool_if:NT \l_@@_obey_spaces_bool { ! } + t \exp_not:n {#1} + } + \tl_put_right:Nn \l_@@_last_delimiters_tl {#1} + \bool_set_false:N \l_@@_grab_expandably_bool + \bool_set_false:N \l_@@_obey_spaces_bool + \bool_set_false:N \l_@@_long_bool + \@@_normalize_arg_spec_loop:n + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro} +% { +% \@@_normalize_type_l:w, +% \@@_normalize_type_m:w, +% \@@_normalize_type_R:w, +% \@@_normalize_type_u:w +% \@@_normalize_type_v:w +% } +% Mandatory arguments. First check the required data is present, +% consists of single characters where applicable, and that the argument +% type is allowed for expandable commands if applicable. For the +% \texttt{m} and \texttt{R} argument types check that they do not +% follow some optional argument with that delimiter as otherwise the +% optional argument could not be omitted. Then save data in +% \cs{l_@@_arg_spec_tl}, count the mandatory argument, and empty the +% list of last delimiters. +% \begin{macrocode} +\cs_new_protected:Npn \@@_normalize_type_l:w + { + \@@_normalize_check_lu:N l + \@@_add_arg_spec_mandatory:n { l } + \@@_normalize_arg_spec_loop:n + } +\cs_new_protected:Npn \@@_normalize_type_m:w + { + \@@_delimiter_check:nnn { } { m } { \iow_char:N \{ } + \@@_add_arg_spec_mandatory:n { m } + \@@_normalize_arg_spec_loop:n + } +\cs_new_protected:Npn \@@_normalize_type_R:w #1#2#3 + { + \quark_if_recursion_tail_stop_do:nn {#3} { \@@_bad_arg_spec:wn } + \@@_single_char_check:n {#1} + \@@_single_char_check:n {#2} + \@@_delimiter_check:nnn {#1} { R/r } { \tl_to_str:n {#1} } + \bool_set_false:N \l_@@_grab_expandably_bool + \@@_add_arg_spec_mandatory:n { R #1 #2 {#3} } + \@@_normalize_arg_spec_loop:n + } +\cs_new_protected:Npn \@@_normalize_type_u:w #1 + { + \quark_if_recursion_tail_stop_do:nn {#1} { \@@_bad_arg_spec:wn } + \@@_normalize_check_lu:N u + \@@_add_arg_spec_mandatory:n { u {#1} } + \@@_normalize_arg_spec_loop:n + } +\cs_new_protected:Npn \@@_normalize_type_v:w + { + \@@_normalize_check_gv:N v + \@@_add_arg_spec_mandatory:n { v } + \@@_normalize_arg_spec_loop:n + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_normalize_type_b:w} +% This argument type is not allowed for commands. This is only +% allowed at the end of the argument specification, hence we check +% that |#1| is the end. +% \begin{macrocode} +\cs_new_protected:Npn \@@_normalize_type_b:w #1 + { + \bool_if:NF \l_@@_environment_bool + { + \__kernel_msg_error:nnxx + { xparse } { invalid-command-arg } + { \@@_environment_or_command: } { b } + \@@_bad_def:wn + } + \tl_clear:N \l_@@_last_delimiters_tl + \@@_add_arg_spec:n { b } + \quark_if_recursion_tail_stop:n {#1} + \__kernel_msg_error:nnxx { xparse } { arg-after-body } + { \@@_environment_or_command: } + { \tl_to_str:n {#1} } + \@@_bad_def:wn + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_single_char_check:n} +% Checks that what should be single characters really are single +% characters (possibly surrounded by spaces). +% \begin{macrocode} +\cs_new_protected:Npn \@@_single_char_check:n #1 + { + \tl_trim_spaces_apply:nN {#1} \tl_if_single_token:nTF + { + \group_begin: + \tex_escapechar:D = 92 \scan_stop: + \exp_args:Nx \tl_if_single_token:nF + { \exp_args:No \tl_to_str:n { \use:nn #1 { } } } + { + \__kernel_msg_warning:nnxx { xparse } { not-single-char } + { \@@_environment_or_command: } { \tl_to_str:n {#1} } + } + \group_end: + } + { + \__kernel_msg_error:nnxx { xparse } { not-single-char } + { \@@_environment_or_command: } { \tl_to_str:n {#1} } + \@@_bad_def:wn + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_normalize_check_gv:N, \@@_normalize_check_lu:N} +% Called for arguments that are always forbidden, or forbidden after +% an optional argument, for expandable commands. +% \begin{macrocode} +\cs_new_protected:Npn \@@_normalize_check_gv:N #1 + { + \bool_if:NT \l_@@_expandable_bool + { + \__kernel_msg_error:nnxx + { xparse } { invalid-expandable-argument-type } + { \iow_char:N \\ \l_@@_function_tl } { \tl_to_str:n {#1} } + \@@_bad_def:wn + } + \bool_set_false:N \l_@@_grab_expandably_bool + } +\cs_new_protected:Npn \@@_normalize_check_lu:N #1 + { + \bool_if:NT \l_@@_expandable_bool + { + \tl_if_empty:NF \l_@@_last_delimiters_tl + { + \__kernel_msg_error:nnxx + { xparse } { invalid-after-optional-expandably } + { \iow_char:N \\ \l_@@_function_tl } { \tl_to_str:n {#1} } + \@@_bad_def:wn + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_delimiter_check:nnn} +% Called for \texttt{m} and \texttt{R} arguments. Checks that the +% leading token does not coincide with the token denoting the presence +% of a previous optional argument. Instead of dealing with braces for +% the \texttt{m}-type we use an empty delimiter to denote that case. +% \begin{macrocode} +\cs_new_protected:Npn \@@_delimiter_check:nnn #1#2#3 + { + \tl_map_inline:Nn \l_@@_last_delimiters_tl + { + \tl_if_eq:nnT {##1} {#1} + { + \__kernel_msg_warning:nnxx { xparse } { optional-mandatory } + {#2} {#3} + } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_bad_arg_spec:wn, \@@_bad_def:wn} +% If the argument specification is wrong, this provides an escape from +% the entire definition process. +% \begin{macrocode} +\cs_new_protected:Npn \@@_bad_arg_spec:wn #1 \@@_break_point:n #2 + { + \__kernel_msg_error:nnxx { xparse } { bad-arg-spec } + { \@@_environment_or_command: } { \tl_to_str:n {#2} } + } +\cs_new_protected:Npn \@@_bad_def:wn #1 \@@_break_point:n #2 { } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_arg_spec:n, \@@_add_arg_spec_mandatory:n} +% When adding an argument to the argument specification, set the +% \texttt{some_long} or \texttt{some_short} booleans as appropriate +% and clear the booleans keeping track of |+| and |!| markers. +% Before that, test for a short argument following some long +% arguments: this is forbidden for expandable commands and prevents +% grabbing arguments expandably. +% +% For mandatory arguments do some more work, in particular complain if +% they were preceeded by~|!|. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_arg_spec:n #1 + { + \bool_lazy_and:nnT + { ! \l_@@_long_bool } + { \l_@@_some_long_bool } + { + \bool_if:NT \l_@@_expandable_bool + { + \__kernel_msg_error:nnx { xparse } { inconsistent-long } + { \iow_char:N \\ \l_@@_function_tl } + \@@_bad_def:wn + } + \bool_set_false:N \l_@@_grab_expandably_bool + } + \bool_if:NTF \l_@@_long_bool + { \bool_set_true:N \l_@@_some_long_bool } + { \bool_set_true:N \l_@@_some_short_bool } + \tl_put_right:Nx \l_@@_arg_spec_tl + { + \bool_if:NT \l_@@_long_bool { + } + \bool_if:NT \l_@@_obey_spaces_bool { ! } + \exp_not:n {#1} + } + \bool_set_false:N \l_@@_long_bool + \bool_set_false:N \l_@@_obey_spaces_bool + } +\cs_new_protected:Npn \@@_add_arg_spec_mandatory:n #1 + { + \bool_if:NT \l_@@_some_obey_spaces_bool + { + \__kernel_msg_error:nnxx { xparse } { non-trailing-obey-spaces } + { \@@_environment_or_command: } { \tl_to_str:n {#1} } + \@@_bad_def:wn + } + \tl_clear:N \l_@@_last_delimiters_tl + \@@_add_arg_spec:n {#1} + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Preparing the signature: general mechanism} +% +% \begin{macro}{\@@_prepare_signature:n} +% \begin{macro}{\@@_prepare_signature:N} +% \begin{macro}{\@@_prepare_signature_bypass:N} +% Actually creating the signature uses the same loop approach as +% normalizing the signature. There are first a number of variables which need +% to be set to track what is going on. Many of these variables are unused +% when defining expandable commands. +% \begin{macrocode} +\cs_new_protected:Npn \@@_prepare_signature:n #1 + { + \int_zero:N \l_@@_current_arg_int + \bool_set_false:N \l_@@_long_bool + \bool_set_false:N \l_@@_obey_spaces_bool + \int_zero:N \l_@@_m_args_int + \bool_set_false:N \l_@@_defaults_bool + \tl_clear:N \l_@@_defaults_tl + \tl_clear:N \l_@@_process_all_tl + \tl_clear:N \l_@@_process_one_tl + \bool_set_false:N \l_@@_process_some_bool + \tl_clear:N \l_@@_signature_tl + \@@_prepare_signature:N #1 \q_recursion_tail \q_recursion_stop + \bool_if:NF \l_@@_expandable_bool { \@@_flush_m_args: } + } +% \end{macrocode} +% The main looping function does not take an argument, but carries out the +% reset on the processor boolean. This is split off from the rest of the +% process so that when actually setting up processors the flag-reset can +% be bypassed. +% +% For each known argument type there is an appropriate function to actually +% do the addition to the signature. These are separate for expandable and +% standard functions, as the approaches are different. +% \begin{macrocode} +\cs_new_protected:Npn \@@_prepare_signature:N + { + \bool_set_false:N \l_@@_prefixed_bool + \@@_prepare_signature_bypass:N + } +\cs_new_protected:Npn \@@_prepare_signature_bypass:N #1 + { + \quark_if_recursion_tail_stop:N #1 + \use:c + { + @@_add + \bool_if:NT \l_@@_grab_expandably_bool { _expandable } + _type_ \token_to_str:N #1 :w + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Setting up a standard signature} +% +% Each argument-adding function appends to the signature a grabber (and +% for some types, the delimiters or default value), except the one for +% \texttt{m} arguments. These are collected and added to the signature +% all at once by \cs{@@_flush_m_args:}, called for every other argument +% type. All of the functions then call the loop function +% \cs{@@_prepare_signature:N}. Default values of arguments are +% collected by \cs{@@_add_default:n} rather than being stored with the +% argument; this function and \cs{@@_add_default:} are also responsible +% for keeping track of \cs{l_@@_current_arg_int}. +% +% \begin{macro}{\@@_add_type_+:w} +% Making the next argument long means setting the flag. The \texttt{m} +% arguments are recorded here as +% this has to be done for every case where there is then a long argument. +% \begin{macrocode} +\cs_new_protected:cpn { @@_add_type_+:w } + { + \@@_flush_m_args: + \bool_set_true:N \l_@@_long_bool + \bool_set_true:N \l_@@_prefixed_bool + \@@_prepare_signature_bypass:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_type_!:w} +% Much the same for controlling trailing optional arguments. +% \begin{macrocode} +\cs_new_protected:cpn { @@_add_type_!:w } + { + \@@_flush_m_args: + \bool_set_true:N \l_@@_obey_spaces_bool + \bool_set_true:N \l_@@_prefixed_bool + \@@_prepare_signature_bypass:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_type_>:w} +% When a processor is found, the processor code is stored. It will be +% used by \cs{@@_args_process:} once arguments are all found. Here too +% the loop calls \cs{@@_prepare_signature_bypass:N} rather than +% \cs{@@_prepare_signature:N} so that the flag is not reset. +% \begin{macrocode} +\cs_new_protected:cpn { @@_add_type_>:w } #1 + { + \@@_flush_m_args: + \bool_set_true:N \l_@@_prefixed_bool + \bool_set_true:N \l_@@_process_some_bool + \tl_put_left:Nn \l_@@_process_one_tl { {#1} } + \@@_prepare_signature_bypass:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_type_b:w} +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_type_b:w + { + \@@_flush_m_args: + \@@_add_default: + \@@_add_grabber:N b + \@@_prepare_signature:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_type_D:w} +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_type_D:w #1#2#3 + { + \@@_flush_m_args: + \@@_add_default:n {#3} + \@@_add_grabber:N D + \tl_put_right:Nn \l_@@_signature_tl { #1 #2 } + \@@_prepare_signature:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_type_E:w} +% The \texttt{E}-type argument needs a special handling of default +% values. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_type_E:w #1#2 + { + \@@_flush_m_args: + \@@_add_default_E:nn {#1} {#2} + \@@_add_grabber:N E + \tl_put_right:Nn \l_@@_signature_tl { {#1} } + \@@_prepare_signature:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_type_G:w} +% For the \texttt{G} type, the grabber and the default are added to the +% signature. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_type_G:w #1 + { + \@@_flush_m_args: + \@@_add_default:n {#1} + \@@_add_grabber:N G + \@@_prepare_signature:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_type_l:w} +% Finding \texttt{l} arguments is very simple: there is nothing to do +% other than add the grabber. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_type_l:w + { + \@@_flush_m_args: + \@@_add_default: + \@@_add_grabber:N l + \@@_prepare_signature:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_type_m:w} +% The \texttt{m} type is special as short arguments which are not +% post-processed are simply counted at this stage. Thus there is a check +% to see if either of these cases apply. If so, a one-argument grabber +% is added to the signature. On the other hand, if a standard short +% argument is required it is simply counted at this stage, to be +% added later using \cs{@@_flush_m_args:}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_type_m:w + { + \@@_add_default: + \bool_if:NTF \l_@@_prefixed_bool + { \@@_add_grabber:N m } + { \int_incr:N \l_@@_m_args_int } + \@@_prepare_signature:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_type_R:w} +% The \texttt{R}-type argument is very similar to the \texttt{D}-type. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_type_R:w #1#2#3 + { + \@@_flush_m_args: + \@@_add_default:n {#3} + \@@_add_grabber:N R + \tl_put_right:Nn \l_@@_signature_tl { #1 #2 } + \@@_prepare_signature:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_type_t:w} +% Setting up a \texttt{t} argument means collecting one token for the test, +% and adding it along with the grabber to the signature. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_type_t:w #1 + { + \@@_flush_m_args: + \@@_add_default: + \@@_add_grabber:N t + \tl_put_right:Nn \l_@@_signature_tl {#1} + \@@_prepare_signature:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_type_u:w} +% At the set up stage, the \texttt{u} type argument is identical to the +% \texttt{G} type except for the name of the grabber function. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_type_u:w #1 + { + \@@_flush_m_args: + \@@_add_default: + \@@_add_grabber:N u + \tl_put_right:Nn \l_@@_signature_tl { {#1} } + \@@_prepare_signature:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_type_v:w} +% At this stage, the \texttt{v} argument is identical to \texttt{l} +% except that since the grabber may fail to read a verbatim argument +% we need a default value. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_type_v:w + { + \@@_flush_m_args: + \exp_args:No \@@_add_default:n \c_novalue_tl + \@@_add_grabber:N v + \@@_prepare_signature:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_flush_m_args:} +% As \texttt{m} arguments are simply counted, there is a need to add +% them to the token register in a block. As this function can only +% be called if something other than \texttt{m} turns up, the flag can +% be switched here. +% \begin{macrocode} +\cs_new_protected:Npn \@@_flush_m_args: + { + \int_compare:nNnT \l_@@_m_args_int > 0 + { + \tl_put_right:Nx \l_@@_signature_tl + { \exp_not:c { @@_grab_m_ \int_use:N \l_@@_m_args_int :w } } + \tl_put_right:Nx \l_@@_process_all_tl + { \prg_replicate:nn { \l_@@_m_args_int } { { } } } + } + \int_zero:N \l_@@_m_args_int + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_grabber:N} +% To keep the various checks needed in one place, adding the grabber +% to the signature is done here. The only questions are whether the +% grabber should be long or not, and whether to obey spaces. The +% \cs{l_@@_obey_spaces_bool} boolean can only be \texttt{true} for +% trailing optional arguments. In that case spaces will not be +% ignored when looking for that optional argument. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_grabber:N #1 + { + \tl_put_right:Nx \l_@@_signature_tl + { + \exp_not:c + { + @@_grab_ #1 + \bool_if:NT \l_@@_long_bool { _long } + \bool_if:NT \l_@@_obey_spaces_bool { _obey_spaces } + :w + } + } + \bool_set_false:N \l_@@_long_bool + \bool_set_false:N \l_@@_obey_spaces_bool + \tl_put_right:Nx \l_@@_process_all_tl + { { \exp_not:o \l_@@_process_one_tl } } + \tl_clear:N \l_@@_process_one_tl + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_default:n, \@@_add_default:, \@@_add_default_E:nn} +% Store the default value of an argument, or rather code that gives +% that default value (it may involve other arguments). This is +% \cs{c_novalue_tl} for arguments with no actual default or with +% default |-NoValue-|; and (in a brace group) \cs{prg_do_nothing:} +% followed by a default value for others. For \texttt{E}-type +% arguments, pad the defaults |#2| with some \cs{c_novalue_tl} +% until there are as many as embellishments~|#1|. These functions are +% also used when defining expandable commands. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_default:n #1 + { + \tl_if_novalue:nTF {#1} + { \@@_add_default: } + { + \int_incr:N \l_@@_current_arg_int + \bool_set_true:N \l_@@_defaults_bool + \tl_put_right:Nn \l_@@_defaults_tl { { \prg_do_nothing: #1 } } + } + } +\cs_new_protected:Npn \@@_add_default: + { + \int_incr:N \l_@@_current_arg_int + \tl_put_right:Nn \l_@@_defaults_tl { \c_novalue_tl } + } +\cs_new_protected:Npn \@@_add_default_E:nn #1#2 + { + \tl_map_function:nN {#2} \@@_add_default:n + \prg_replicate:nn + { \tl_count:n {#1} - \tl_count:n {#2} } + { \@@_add_default: } + } +% \end{macrocode} +% \end{macro} +% +% \subsection{Setting up expandable types} +% +% The approach here is not dissimilar to that for standard types, but fewer types +% are supported. There is +% also a need to define the per-function auxiliaries: this is done here, while +% the general grabbers are dealt with later. +% +% \begin{macro}{\@@_add_expandable_type_+:w} +% We have already checked that short arguments are before long +% arguments, so \cs{l_@@_long_bool} only changes from \texttt{false} +% to \texttt{true} once (and there is no need to reset it after each +% argument). Also knock back the argument count because |+| is not an +% argument. Continue the loop. +% \begin{macrocode} +\cs_new_protected:cpn { @@_add_expandable_type_+:w } + { + \bool_set_true:N \l_@@_long_bool + \@@_prepare_signature:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_expandable_type_D:w} +% \begin{macro}{\@@_add_expandable_type_D_aux:NNNn} +% \begin{macro}{\@@_add_expandable_type_D_aux:NNN} +% \begin{macro}{\@@_add_expandable_type_D_aux:NN} +% The set up for \texttt{D}-type arguments involves constructing a +% rather complex auxiliary which is used +% repeatedly when grabbing. There is an auxiliary here so that the +% \texttt{R}-type can share code readily: |#1| is |D| or~|R|. +% The |_aux:NN| auxiliary is needed if the two delimiting tokens are +% identical: in contrast to the non-expandable route, the grabber here +% has to act differently for this case. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_expandable_type_D:w + { \@@_add_expandable_type_D_aux:NNNn D } +\cs_new_protected:Npn \@@_add_expandable_type_D_aux:NNNn #1#2#3#4 + { + \@@_add_default:n {#4} + \tl_if_eq:nnTF {#2} {#3} + { \@@_add_expandable_type_D_aux:NN #1 #2 } + { \@@_add_expandable_type_D_aux:NNN #1 #2 #3 } + \@@_prepare_signature:N + } +\cs_new_protected:Npn \@@_add_expandable_type_D_aux:NNN #1#2#3 + { + \bool_if:NTF \l_@@_long_bool + { \cs_set:cpx } + { \cs_set_nopar:cpx } + { \l_@@_expandable_aux_name_tl } ##1 ##2 #2 ##3 \q_@@ ##4 #3 + { ##1 {##2} {##3} {##4} } + \@@_add_expandable_grabber:nn {#1} + { + \exp_not:c { \l_@@_expandable_aux_name_tl } + \exp_not:n { #2 #3 } + } + } +\cs_new_protected:Npn \@@_add_expandable_type_D_aux:NN #1#2 + { + \bool_if:NTF \l_@@_long_bool + { \cs_set:cpx } + { \cs_set_nopar:cpx } + { \l_@@_expandable_aux_name_tl } ##1 #2 ##2 #2 + { ##1 {##2} } + \@@_add_expandable_grabber:nn { #1_alt } + { + \exp_not:c { \l_@@_expandable_aux_name_tl } + \exp_not:n {#2} + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_add_expandable_type_E:w} +% \begin{macro}{\@@_add_expandable_type_E_aux:n} +% For each embellishment, use \cs{@@_get_grabber:NN} to obtain an +% auxiliary delimited by that token and store a pair constituted of +% the auxiliary and the token in \cs{l_@@_tmpb_tl}, before appending +% the whole set of these pairs to the signature, and an equal number +% of |-NoValue-| markers (regardless of the default values of +% arguments). Set the current argument appropriately. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_expandable_type_E:w #1#2 + { + \@@_add_default_E:nn {#1} {#2} + \tl_clear:N \l_@@_tmpb_tl + \tl_map_function:nN {#1} \@@_add_expandable_type_E_aux:n + \@@_add_expandable_grabber:nn + { E \bool_if:NT \l_@@_long_bool { _long } } + { + { \exp_not:o \l_@@_tmpb_tl } + { + \prg_replicate:nn { \tl_count:n {#1} } + { { \c_novalue_tl } } + } + } + \@@_prepare_signature:N + } +\cs_new_protected:Npn \@@_add_expandable_type_E_aux:n #1 + { + \@@_get_grabber:NN #1 \l_@@_tmpa_tl + \tl_put_right:Nx \l_@@_tmpb_tl + { \exp_not:o \l_@@_tmpa_tl \exp_not:N #1 } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_add_expandable_type_l:w} +% Reuse type \texttt{u}, thanks to the fact that \TeX{} macros whose +% parameter text ends with |#| in fact end up being delimited by an +% open brace. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_expandable_type_l:w + { \@@_add_expandable_type_u:w ## } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_expandable_type_m:w} +% Unlike the standard case, when working expandably each argument is always +% grabbed separately. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_expandable_type_m:w + { + \@@_add_default: + \@@_add_expandable_grabber:nn + { m \bool_if:NT \l_@@_long_bool { _long } } { } + \@@_prepare_signature:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_expandable_type_R:w} +% The \texttt{R}-type is very similar to the \texttt{D}-type +% argument, and so the same internals are used. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_expandable_type_R:w + { \@@_add_expandable_type_D_aux:NNNn R } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_expandable_type_t:w} +% An auxiliary delimited by |#1| is built now. It will be used to +% test for the presence of that token. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_expandable_type_t:w #1 + { + \@@_add_default: + \@@_get_grabber:NN #1 \l_@@_tmpa_tl + \@@_add_expandable_grabber:nn { t } + { + \exp_not:o \l_@@_tmpa_tl + \exp_not:N #1 + } + \@@_prepare_signature:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_expandable_type_u:w} +% Define an auxiliary that will be used directly in the signature. It +% grabs one argument delimited by |#1| and places it before \cs{q_@@}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_expandable_type_u:w #1 + { + \@@_add_default: + \bool_if:NTF \l_@@_long_bool + { \cs_set:cpn } + { \cs_set_nopar:cpn } + { \l_@@_expandable_aux_name_tl } ##1 \q_@@ ##2 ##3 ##4 #1 + { ##1 {##4} \q_@@ ##2 ##3 } + \@@_add_expandable_grabber:nn { u } + { \exp_not:c { \l_@@_expandable_aux_name_tl } } + \@@_prepare_signature:N + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_expandable_grabber:nn} +% This is called for all arguments to place the right grabber in the +% signature. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_expandable_grabber:nn #1#2 + { + \tl_put_right:Nx \l_@@_signature_tl + { \exp_not:c { @@_expandable_grab_ #1 :w } #2 } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_get_grabber:NN} +% \begin{macro}{\@@_get_grabber_auxi:NN} +% \begin{macro}{\@@_get_grabber_auxii:NN} +% Given a token |#1|, defines an expandable function delimited by that +% token and stores it in the token list~|#2|. The function is named +% after the token, unless that function name is already taken by some +% other grabber (this can happen in the rare case where delimiters +% with different category codes are used in the same document): in +% that case use a global counter to get a unique name. Since the +% grabbers are not named after \pkg{xparse} commands they should not +% be used to get material from the input stream. +% \begin{macrocode} +\cs_new_protected:Npn \@@_get_grabber:NN #1#2 + { + \cs_set:Npn \@@_tmp:w ##1 #1 {##1} + \exp_args:Nc \@@_get_grabber_auxi:NN + { @@_grabber_ \token_to_str:N #1 :w } #2 + } +\cs_new_protected:Npn \@@_get_grabber_auxi:NN #1#2 + { + \cs_if_eq:NNTF \@@_tmp:w #1 + { \tl_set:Nn #2 {#1} } + { + \cs_if_exist:NTF #1 + { + \int_gincr:N \g_@@_grabber_int + \exp_args:Nc \@@_get_grabber_auxi:NN + { + @@_grabber_ + - \int_use:N \g_@@_grabber_int :w + } + #2 + } + { \@@_get_grabber_auxii:NN #1 #2 } + } + } +\cs_new_protected:Npn \@@_get_grabber_auxii:NN #1#2 + { + \cs_set_eq:NN #1 \@@_tmp:w + \tl_set:Nn #2 {#1} + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Grabbing arguments} +% +% All of the grabbers follow the same basic pattern. The initial +% function stores in \cs{l_@@_signature_tl} the code to grab further +% arguments, defines (the function in) \cs{l_@@_fn_tl} that will grab +% the argument, and calls it. +% +% Defining \cs{l_@@_fn_tl} means determining whether to use +% \cs{cs_set:Npn} or \cs{cs_set_nopar:Npn}, and for optional arguments +% whether to skip spaces. Once the argument is found, \cs{l_@@_fn_tl} +% calls \cs{@@_add_arg:n}, responsible for calling processors and +% grabbing further arguments. +% +% \begin{macro} +% { +% \@@_grab_b:w, +% \@@_grab_b_long:w, +% \@@_grab_b_obey_spaces:w, +% \@@_grab_b_long_obey_spaces:w, +% \@@_grab_b_aux:NNw, +% \@@_grab_b_end:Nw +% } +% This uses the well-tested code of \texttt{D}-type arguments, +% skipping the peeking step because the \texttt{b}-type argument is +% always present, and adding a cleanup stage at the end by hijacking +% the signature. The clean-up consists of properly +% dealing with \cs{l_@@_args_tl} and also putting back the \cs{end} +% that served as an end-delimiter: this \cs{end} receives the +% environment name as its argument and is run normally. The +% \texttt{D}-type code stores the argument found (body of the +% environment) as a brace group in \cs{l_@@_args_tl} and depending on +% the presence of a prefix~|!| we trim spaces or not before adding +% this braced argument into the saved \cs{l_@@_args_tl}. +% The strange \verb*|\begin | control sequence is there for display +% purposes only: it has to look like |\begin| in the terminal but +% not to delimited arguments. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_b:w + { \@@_grab_b_aux:NNw \cs_set_protected_nopar:Npn \tl_trim_spaces:n } +\cs_new_protected:Npn \@@_grab_b_long:w + { \@@_grab_b_aux:NNw \cs_set_protected:Npn \tl_trim_spaces:n } +\cs_new_protected:Npn \@@_grab_b_obey_spaces:w + { \@@_grab_b_aux:NNw \cs_set_protected_nopar:Npn \exp_not:n } +\cs_new_protected:Npn \@@_grab_b_long_obey_spaces:w + { \@@_grab_b_aux:NNw \cs_set_protected:Npn \exp_not:n } +\cs_new_protected:Npn \@@_grab_b_aux:NNw #1#2#3 \@@_run_code: + { + \@@_grab_D_aux:NNnN \begin \end {#3} #1 + \tl_put_left:Nn \l_@@_signature_tl { \@@_grab_b_end:Nw #2 } + \tl_set_eq:NN \l_@@_saved_args_tl \l_@@_args_tl + \tl_clear:N \l_@@_args_tl + \exp_args:Nc \l_@@_fn_tl { begin ~ } + } +\cs_new_protected:Npn \@@_grab_b_end:Nw #1#2 \@@_run_code: + { + \tl_set:Nx \l_@@_args_tl + { + \exp_not:V \l_@@_saved_args_tl + { \exp_after:wN #1 \l_@@_args_tl } + } + #2 + \@@_run_code: + \end + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_grab_D:w} +% \begin{macro}{\@@_grab_D_long:w} +% \begin{macro}{\@@_grab_D_obey_spaces:w} +% \begin{macro}{\@@_grab_D_long_obey_spaces:w} +% The generic delimited argument grabber. The auxiliary function does +% a peek test before calling \cs{@@_grab_D_call:Nw}, so that the +% optional nature of the argument works as expected. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_D:w #1#2#3 \@@_run_code: + { + \@@_grab_D_aux:NNnNN #1 #2 {#3} \cs_set_protected_nopar:Npn + \@@_peek_nonspace_remove:NTF + } +\cs_new_protected:Npn \@@_grab_D_long:w #1#2#3 \@@_run_code: + { + \@@_grab_D_aux:NNnNN #1 #2 {#3} \cs_set_protected:Npn + \@@_peek_nonspace_remove:NTF + } +\cs_new_protected:Npn \@@_grab_D_obey_spaces:w #1#2#3 \@@_run_code: + { + \@@_grab_D_aux:NNnNN #1 #2 {#3} \cs_set_protected_nopar:Npn + \peek_meaning_remove:NTF + } +\cs_new_protected:Npn \@@_grab_D_long_obey_spaces:w #1#2#3 \@@_run_code: + { + \@@_grab_D_aux:NNnNN #1 #2 {#3} \cs_set_protected:Npn + \peek_meaning_remove:NTF + } +% \end{macrocode} +% \begin{macro}{\@@_grab_D_aux:NNnNN} +% \begin{macro}{\@@_grab_D_aux:NNnN} +% This is a bit complicated. The idea is that, in order to check for +% nested optional argument tokens (\texttt{[[...]]} and so on) the +% argument needs to be grabbed without removing any braces at all. If +% this is not done, then cases like |[{[}]| fail. So after testing for +% an optional argument, it is collected piece-wise. Inserting a quark +% prevents loss of braces, and there is then a test to see if there are +% nested delimiters to handle. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_D_aux:NNnNN #1#2#3#4#5 + { + \@@_grab_D_aux:NNnN #1#2 {#3} #4 + #5 #1 + { \@@_grab_D_call:Nw #1 } + { \@@_add_arg:o \c_novalue_tl } + } +% \end{macrocode} +% Inside the \enquote{standard} grabber, there is a test to see if the +% grabbed argument is entirely enclosed by braces. There are a couple of +% extra factors to allow for: the argument might be entirely empty, and +% spaces at the start and end of the input must be retained around a brace +% group. Also notice that a \emph{blank} argument might still contain +% spaces. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_D_aux:NNnN #1#2#3#4 + { + \tl_set:Nn \l_@@_signature_tl {#3} + \exp_after:wN #4 \l_@@_fn_tl ##1 #2 + { + \tl_if_in:nnTF {##1} {#1} + { \@@_grab_D_nested:NNnN #1 #2 {##1} #4 } + { + \tl_if_blank:oTF { \use_none:n ##1 } + { \@@_add_arg:o { \use_none:n ##1 } } + { + \str_if_eq:eeTF + { \exp_not:o { \use_none:n ##1 } } + { { \exp_not:o { \use_ii:nnn ##1 \q_nil } } } + { \@@_add_arg:o { \use_ii:nn ##1 } } + { \@@_add_arg:o { \use_none:n ##1 } } + } + } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \begin{macro}{\@@_grab_D_nested:NNnN} +% \begin{macro}{\@@_grab_D_nested:w} +% \begin{macro}{\l_@@_nesting_a_tl} +% \begin{macro}{\l_@@_nesting_b_tl} +% \begin{macro}{\q_@@} +% Catching nested optional arguments means more work. The aim here is +% to collect up each pair of optional tokens without \TeX{} helping out, +% and without counting anything. The code above will already have +% removed the leading opening token and a closing token, but the +% wrong one. The aim is then to work through the material grabbed +% so far and divide it up on each opening token, grabbing a closing +% token to match (thus working in pairs). Once there are no opening +% tokens, then there is a second check to see if there are any +% opening tokens in the second part of the argument (for things +% like |[][]|). Once everything has been found, the entire collected +% material is added to the output as a single argument. The only tricky part +% here is ensuring that any grabbing function that might run away is named +% after the function currently being parsed and not after \pkg{xparse}. That +% leads to some rather complex nesting! There is also a need to prevent the +% loss of any braces, hence the insertion and removal of quarks along the +% way. +% \begin{macrocode} +\tl_new:N \l_@@_nesting_a_tl +\tl_new:N \l_@@_nesting_b_tl +\quark_new:N \q_@@ +\cs_new_protected:Npn \@@_grab_D_nested:NNnN #1#2#3#4 + { + \tl_clear:N \l_@@_nesting_a_tl + \tl_clear:N \l_@@_nesting_b_tl + \exp_after:wN #4 \l_@@_fn_tl ##1 #1 ##2 \q_@@ ##3 #2 + { + \tl_put_right:No \l_@@_nesting_a_tl { \use_none:n ##1 #1 } + \tl_put_right:No \l_@@_nesting_b_tl { \use_i:nn #2 ##3 } + \tl_if_in:nnTF {##2} {#1} + { + \l_@@_fn_tl + \q_nil ##2 \q_@@ \ERROR + } + { + \tl_put_right:Nx \l_@@_nesting_a_tl + { \@@_grab_D_nested:w \q_nil ##2 \q_stop } + \tl_if_in:NnTF \l_@@_nesting_b_tl {#1} + { + \tl_set_eq:NN \l_@@_tmpa_tl \l_@@_nesting_b_tl + \tl_clear:N \l_@@_nesting_b_tl + \exp_after:wN \l_@@_fn_tl \exp_after:wN + \q_nil \l_@@_tmpa_tl \q_nil \q_@@ \ERROR + } + { + \tl_put_right:No \l_@@_nesting_a_tl + \l_@@_nesting_b_tl + \@@_add_arg:V \l_@@_nesting_a_tl + } + } + } + \l_@@_fn_tl #3 \q_nil \q_@@ \ERROR + } +\cs_new:Npn \@@_grab_D_nested:w #1 \q_nil \q_stop + { \exp_not:o { \use_none:n #1 } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \begin{macro}{\@@_grab_D_call:Nw} +% For \texttt{D} and \texttt{R}-type arguments, to avoid losing any +% braces, a token needs to be inserted before the argument to be grabbed. +% If the argument runs away because the closing token is missing then this +% inserted token shows up in the terminal. Ideally, |#1| would therefore be +% used directly, but that is no good as it will mess up the rest of the +% grabber. Instead, a copy of |#1| with an altered category code is used, +% as this will look right in the terminal but will not mess up the grabber. +% The only issue then is that the category code of |#1| is unknown. So there +% is a quick test to ensure that the inserted token can never be matched by +% the grabber. (This assumes that the open and close delimiters are not the +% same character with different category codes, but that really should not +% happen in any sensible document-level syntax.) +% \begin{macrocode} +\cs_set_protected_nopar:Npn \@@_grab_D_call:Nw #1 + { + \token_if_eq_catcode:NNTF + #1 + { + \exp_after:wN \exp_after:wN \exp_after:wN + \l_@@_fn_tl \char_generate:nn { `#1 } { 11 } + } + { + \exp_after:wN \l_@@_fn_tl + \token_to_str:N #1 + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \@@_grab_E:w, \@@_grab_E_long:w, +% \@@_grab_E_obey_spaces:w, \@@_grab_E_long_obey_spaces:w +% } +% \begin{macro}{\@@_grab_E:nnNN} +% \begin{macro}{\@@_grab_E_loop:NnN} +% \begin{macro}{\@@_grab_E_finalise:} +% Everything here needs to point to a loop. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_E:w #1#2 \@@_run_code: + { + \@@_grab_E:nnNN {#1} {#2} + \cs_set_protected_nopar:Npn + \@@_peek_nonspace_remove:NTF + } +\cs_new_protected:Npn \@@_grab_E_long:w #1#2 \@@_run_code: + { + \@@_grab_E:nnNN {#1} {#2} + \cs_set_protected:Npn + \@@_peek_nonspace_remove:NTF + } +\cs_new_protected:Npn \@@_grab_E_obey_spaces:w #1#2 \@@_run_code: + { + \@@_grab_E:nnNN {#1} {#2} + \cs_set_protected_nopar:Npn + \peek_meaning_remove:NTF + } +\cs_new_protected:Npn \@@_grab_E_long_obey_spaces:w #1#2 \@@_run_code: + { + \@@_grab_E:nnNN {#1} {#2} + \cs_set_protected:Npn + \peek_meaning_remove:NTF + } +% \end{macrocode} +% A loop is needed here to allow a random ordering of keys. These are +% searched for one at a time, with any not found needing to be tracked: +% they can appear later. The grabbed values are held in a property list +% which is then turned into an ordered list to be passed back to the user. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_E:nnNN #1#2#3#4 + { + \exp_after:wN #3 \l_@@_fn_tl ##1##2##3 + { + \prop_put:Nnn \l_@@_tmp_prop {##1} {##3} + \@@_grab_E_loop:NnN #4 { } ##2 \q_recursion_stop + } + \prop_clear:N \l_@@_tmp_prop + \tl_set:Nn \l_@@_signature_tl {#2} + \cs_set_protected:Npn \@@_grab_E_finalise: + { + \tl_map_inline:nn {#1} + { + \prop_get:NnNF \l_@@_tmp_prop {####1} \l_@@_tmpb_tl + { \tl_set_eq:NN \l_@@_tmpb_tl \c_novalue_tl } + \tl_put_right:Nx \l_@@_args_tl + { { \exp_not:V \l_@@_tmpb_tl } } + } + \l_@@_signature_tl \@@_run_code: + } + \@@_grab_E_loop:NnN #4 { } #1 \q_recursion_tail \q_recursion_stop + } +\cs_new_protected:Npn \@@_grab_E_loop:NnN #1#2#3#4 \q_recursion_stop + { + \cs_if_eq:NNTF #3 \q_recursion_tail + { \@@_grab_E_finalise: } + { + #1 #3 + { \l_@@_fn_tl #3 {#2#4} } + { \@@_grab_E_loop:NnN #1 {#2#3} #4 \q_recursion_stop } + } + } +\cs_new_protected:Npn \@@_grab_E_finalise: { } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_grab_G:w} +% \begin{macro}{\@@_grab_G_long:w} +% \begin{macro}{\@@_grab_G_obey_spaces:w} +% \begin{macro}{\@@_grab_G_long_obey_spaces:w} +% \begin{macro}{\@@_grab_G_aux:nNN} +% Optional groups are checked by meaning, so that the same code will +% work with, for example, \ConTeXt{}-like input. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_G:w #1 \@@_run_code: + { + \@@_grab_G_aux:nNN {#1} \cs_set_protected_nopar:Npn + \@@_peek_nonspace:NTF + } +\cs_new_protected:Npn \@@_grab_G_long:w #1 \@@_run_code: + { + \@@_grab_G_aux:nNN {#1} \cs_set_protected:Npn + \@@_peek_nonspace:NTF + } +\cs_new_protected:Npn \@@_grab_G_obey_spaces:w #1 \@@_run_code: + { + \@@_grab_G_aux:nNN {#1} \cs_set_protected_nopar:Npn + \peek_meaning:NTF + } +\cs_new_protected:Npn \@@_grab_G_long_obey_spaces:w #1 \@@_run_code: + { + \@@_grab_G_aux:nNN {#1} \cs_set_protected:Npn + \peek_meaning:NTF + } +\cs_new_protected:Npn \@@_grab_G_aux:nNN #1#2#3 + { + \tl_set:Nn \l_@@_signature_tl {#1} + \exp_after:wN #2 \l_@@_fn_tl ##1 + { \@@_add_arg:n {##1} } + #3 \c_group_begin_token + { \l_@@_fn_tl } + { \@@_add_arg:o \c_novalue_tl } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_grab_l:w} +% \begin{macro}{\@@_grab_l_long:w} +% \begin{macro}{\@@_grab_l_aux:nN} +% Argument grabbers for mandatory \TeX{} arguments are pretty simple. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_l:w #1 \@@_run_code: + { \@@_grab_l_aux:nN {#1} \cs_set_protected_nopar:Npn } +\cs_new_protected:Npn \@@_grab_l_long:w #1 \@@_run_code: + { \@@_grab_l_aux:nN {#1} \cs_set_protected:Npn } +\cs_new_protected:Npn \@@_grab_l_aux:nN #1#2 + { + \tl_set:Nn \l_@@_signature_tl {#1} + \exp_after:wN #2 \l_@@_fn_tl ##1## + { \@@_add_arg:n {##1} } + \l_@@_fn_tl + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_grab_m:w} +% \begin{macro}{\@@_grab_m_long:w} +% Collecting a single mandatory argument is quite easy. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_m:w #1 \@@_run_code: + { + \tl_set:Nn \l_@@_signature_tl {#1} + \exp_after:wN \cs_set_protected_nopar:Npn \l_@@_fn_tl ##1 + { \@@_add_arg:n {##1} } + \l_@@_fn_tl + } +\cs_new_protected:Npn \@@_grab_m_long:w #1 \@@_run_code: + { + \tl_set:Nn \l_@@_signature_tl {#1} + \exp_after:wN \cs_set_protected:Npn \l_@@_fn_tl ##1 + { \@@_add_arg:n {##1} } + \l_@@_fn_tl + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_grab_m_1:w} +% \begin{macro}{\@@_grab_m_2:w} +% \begin{macro}{\@@_grab_m_3:w} +% \begin{macro}{\@@_grab_m_4:w} +% \begin{macro}{\@@_grab_m_5:w} +% \begin{macro}{\@@_grab_m_6:w} +% \begin{macro}{\@@_grab_m_7:w} +% \begin{macro}{\@@_grab_m_8:w} +% \begin{macro}{\@@_grab_m_9:w} +% \begin{macro}{\@@_grab_m_aux:Nnnnnnnnn} +% Grabbing 1--8 mandatory arguments is done by giving 8--1 known +% arguments to a 9-argument function that stores them in +% \cs{l_@@_args_tl}. For simplicity, grabbing 9 mandatory arguments +% is done by grabbing 5 then 4 arguments. +% \begin{macrocode} +\cs_new_protected_nopar:Npn \@@_grab_m_aux:Nnnnnnnnn #1#2#3#4#5#6#7#8#9 + { + \tl_put_right:No \l_@@_args_tl + { #1 {#2} {#3} {#4} {#5} {#6} {#7} {#8} {#9} } + \l_@@_signature_tl \@@_run_code: + } +\cs_new_protected:cpn { @@_grab_m_1:w } #1 \@@_run_code: + { + \tl_set:Nn \l_@@_signature_tl {#1} + \exp_after:wN \cs_set_eq:NN \l_@@_fn_tl \@@_grab_m_aux:Nnnnnnnnn + \l_@@_fn_tl \use_none:nnnnnnn { } { } { } { } { } { } { } + } +\cs_new_protected:cpn { @@_grab_m_2:w } #1 \@@_run_code: + { + \tl_set:Nn \l_@@_signature_tl {#1} + \exp_after:wN \cs_set_eq:NN \l_@@_fn_tl \@@_grab_m_aux:Nnnnnnnnn + \l_@@_fn_tl \use_none:nnnnnn { } { } { } { } { } { } + } +\cs_new_protected:cpn { @@_grab_m_3:w } #1 \@@_run_code: + { + \tl_set:Nn \l_@@_signature_tl {#1} + \exp_after:wN \cs_set_eq:NN \l_@@_fn_tl \@@_grab_m_aux:Nnnnnnnnn + \l_@@_fn_tl \use_none:nnnnn { } { } { } { } { } + } +\cs_new_protected:cpn { @@_grab_m_4:w } #1 \@@_run_code: + { + \tl_set:Nn \l_@@_signature_tl {#1} + \exp_after:wN \cs_set_eq:NN \l_@@_fn_tl \@@_grab_m_aux:Nnnnnnnnn + \l_@@_fn_tl \use_none:nnnn { } { } { } { } + } +\cs_new_protected:cpn { @@_grab_m_5:w } #1 \@@_run_code: + { + \tl_set:Nn \l_@@_signature_tl {#1} + \exp_after:wN \cs_set_eq:NN \l_@@_fn_tl \@@_grab_m_aux:Nnnnnnnnn + \l_@@_fn_tl \use_none:nnn { } { } { } + } +\cs_new_protected:cpn { @@_grab_m_6:w } #1 \@@_run_code: + { + \tl_set:Nn \l_@@_signature_tl {#1} + \exp_after:wN \cs_set_eq:NN \l_@@_fn_tl \@@_grab_m_aux:Nnnnnnnnn + \l_@@_fn_tl \use_none:nn { } { } + } +\cs_new_protected:cpn { @@_grab_m_7:w } #1 \@@_run_code: + { + \tl_set:Nn \l_@@_signature_tl {#1} + \exp_after:wN \cs_set_eq:NN \l_@@_fn_tl \@@_grab_m_aux:Nnnnnnnnn + \l_@@_fn_tl \use_none:n { } + } +\cs_new_protected:cpn { @@_grab_m_8:w } #1 \@@_run_code: + { + \tl_set:Nn \l_@@_signature_tl {#1} + \exp_after:wN \cs_set_eq:NN \l_@@_fn_tl \@@_grab_m_aux:Nnnnnnnnn + \l_@@_fn_tl \prg_do_nothing: + } +\cs_new_protected:cpx { @@_grab_m_9:w } + { + \exp_not:c { @@_grab_m_5:w } + \exp_not:c { @@_grab_m_4:w } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_grab_R:w, \@@_grab_R_long:w} +% \begin{macro}{\@@_grab_R_aux:NNnN} +% The grabber for \texttt{R}-type arguments is basically the same as +% that for \texttt{D}-type ones, but always skips spaces (as it is mandatory) +% and has a hard-coded error message. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_R:w #1#2#3 \@@_run_code: + { \@@_grab_R_aux:NNnN #1 #2 {#3} \cs_set_protected_nopar:Npn } +\cs_new_protected:Npn \@@_grab_R_long:w #1#2#3 \@@_run_code: + { \@@_grab_R_aux:NNnN #1 #2 {#3} \cs_set_protected:Npn } +\cs_new_protected:Npn \@@_grab_R_aux:NNnN #1#2#3#4 + { + \@@_grab_D_aux:NNnN #1 #2 {#3} #4 + \@@_peek_nonspace_remove:NTF #1 + { \@@_grab_D_call:Nw #1 } + { + \__kernel_msg_error:nnxx { xparse } { missing-required } + { \@@_environment_or_command: } + { \token_to_str:N #1 } + \@@_add_arg:o \c_novalue_tl + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_grab_t:w} +% \begin{macro}{\@@_grab_t_obey_spaces:w} +% \begin{macro}{\@@_grab_t_aux:NNw} +% Dealing with a token is quite easy. Check the match, remove the +% token if needed and add a flag to the output. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_t:w + { \@@_grab_t_aux:NNw \@@_peek_nonspace_remove:NTF } +\cs_new_protected:Npn \@@_grab_t_obey_spaces:w + { \@@_grab_t_aux:NNw \peek_meaning_remove:NTF } +\cs_new_protected:Npn \@@_grab_t_aux:NNw #1#2#3 \@@_run_code: + { + \tl_set:Nn \l_@@_signature_tl {#3} + \exp_after:wN \cs_set_protected:Npn \l_@@_fn_tl + { + #1 #2 + { \@@_add_arg:n { \BooleanTrue } } + { \@@_add_arg:n { \BooleanFalse } } + } + \l_@@_fn_tl + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_grab_u:w} +% \begin{macro}{\@@_grab_u_long:w} +% \begin{macro}{\@@_grab_u_aux:nnN} +% Grabbing up to a list of tokens is quite easy: define the grabber, +% and then collect. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_u:w #1#2 \@@_run_code: + { \@@_grab_u_aux:nnN {#1} {#2} \cs_set_protected_nopar:Npn } +\cs_new_protected:Npn \@@_grab_u_long:w #1#2 \@@_run_code: + { \@@_grab_u_aux:nnN {#1} {#2} \cs_set_protected:Npn } +\cs_new_protected:Npn \@@_grab_u_aux:nnN #1#2#3 + { + \tl_set:Nn \l_@@_signature_tl {#2} + \exp_after:wN #3 \l_@@_fn_tl ##1 #1 + { \@@_add_arg:n {##1} } + \l_@@_fn_tl + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_grab_v:w} +% \begin{macro}{\@@_grab_v_long:w} +% \begin{macro}{\@@_grab_v_aux:w} +% \begin{macro}{\@@_grab_v_group_end:} +% \begin{variable}{\l_@@_v_arg_tl} +% The opening delimiter is the first non-space token, and is never +% read verbatim. This is required by consistency with the case where +% the preceding argument was optional and absent: then \TeX{} has +% already read and tokenized that token when looking for the optional +% argument. The first thing is thus to check is that this delimiter +% is a character, and to distinguish the case of a left brace (in that +% case, \cs{group_align_safe_end:} is needed to compensate for the +% begin-group character that was just seen). Then set verbatim +% catcodes with \cs{@@_grab_v_aux_catcodes:}. +% +% The group keep catcode changes local, and +% \cs{group_align_safe_begin/end:} allow to use a character +% with category code~$4$ (normally |&|) as the delimiter. +% It is ended by \cs{@@_grab_v_group_end:}, which smuggles +% the collected argument out of the group. +% \begin{macrocode} +\tl_new:N \l_@@_v_arg_tl +\cs_new_protected:Npn \@@_grab_v:w + { + \bool_set_false:N \l_@@_long_bool + \@@_grab_v_aux:w + } +\cs_new_protected:Npn \@@_grab_v_long:w + { + \bool_set_true:N \l_@@_long_bool + \@@_grab_v_aux:w + } +\cs_new_protected:Npn \@@_grab_v_aux:w #1 \@@_run_code: + { + \tl_set:Nn \l_@@_signature_tl {#1} + \group_begin: + \group_align_safe_begin: + \tex_escapechar:D = 92 \scan_stop: + \tl_clear:N \l_@@_v_arg_tl + \peek_remove_spaces:n + { + \peek_meaning_remove:NTF \c_group_begin_token + { + \group_align_safe_end: + \@@_grab_v_bgroup: + } + { + \peek_N_type:TF + { \@@_grab_v_aux_test:N } + { \@@_grab_v_aux_abort:n { } } + } + } + } +\cs_new_protected:Npn \@@_grab_v_group_end: + { + \group_align_safe_end: + \exp_args:NNNo + \group_end: + \tl_set:Nn \l_@@_v_arg_tl { \l_@@_v_arg_tl } + } +% \end{macrocode} +% \end{variable} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_grab_v_aux_test:N} +% \begin{macro} +% { +% \@@_grab_v_aux_loop:N, +% \@@_grab_v_aux_loop:NN, +% \@@_grab_v_aux_loop_end: +% } +% Check that the opening delimiter is a character, setup category codes, +% then start reading tokens one by one, keeping the delimiter as an argument. +% If the verbatim was not nested, we will be grabbing one character +% at each step. Unfortunately, it can happen that what follows the +% verbatim argument is already tokenized. Thus, we check at each step +% that the next token is indeed a \enquote{nice} +% character, \emph{i.e.}, is not a character with +% category code $1$ (begin-group), $2$ (end-group) +% or $6$ (macro parameter), nor the space character, +% with category code~$10$ and character code~$32$, +% nor a control sequence. +% The partially built argument is stored in \cs{l_@@_v_arg_tl}. +% If we ever meet a token which we cannot grab (non-N-type), +% or which is not a character according to +% \cs{@@_grab_v_token_if_char:NTF}, then we bail out with +% \cs{@@_grab_v_aux_abort:n}. Otherwise, we stop at the first +% character matching the delimiter. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_v_aux_test:N #1 + { + \@@_grab_v_token_if_char:NTF #1 + { + \@@_grab_v_aux_put:N #1 + \@@_grab_v_aux_catcodes: + \@@_grab_v_aux_loop:N #1 + } + { \@@_grab_v_aux_abort:n {#1} #1 } + } +\cs_new_protected:Npn \@@_grab_v_aux_loop:N #1 + { + \peek_N_type:TF + { \@@_grab_v_aux_loop:NN #1 } + { \@@_grab_v_aux_abort:n { } } + } +\cs_new_protected:Npn \@@_grab_v_aux_loop:NN #1#2 + { + \@@_grab_v_token_if_char:NTF #2 + { + \token_if_eq_charcode:NNTF #1 #2 + { \@@_grab_v_aux_loop_end: } + { + \@@_grab_v_aux_put:N #2 + \@@_grab_v_aux_loop:N #1 + } + } + { \@@_grab_v_aux_abort:n {#2} #2 } + } +\cs_new_protected:Npn \@@_grab_v_aux_loop_end: + { + \@@_grab_v_group_end: + \@@_add_arg:x { \tl_tail:N \l_@@_v_arg_tl } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_grab_v_bgroup:} +% \begin{macro}{\@@_grab_v_bgroup_loop:} +% \begin{macro}{\@@_grab_v_bgroup_loop:N} +% \begin{variable}{\l_@@_v_nesting_int} +% If the opening delimiter is a left brace, we keep track of +% how many left and right braces were encountered so far in +% \cs{l_@@_v_nesting_int} (the methods used for optional +% arguments cannot apply here), and stop as soon as it reaches~$0$. +% +% Some care was needed when removing the opening delimiter, which +% has already been assigned category code~$1$: using +% \cs{peek_meaning_remove:NTF} in the \cs{@@_grab_v_aux:w} +% function would break within alignments. Instead, we first +% convert that token to a string, and remove the result as a +% normal undelimited argument. +% \begin{macrocode} +\int_new:N \l_@@_v_nesting_int +\cs_new_protected:Npx \@@_grab_v_bgroup: + { + \exp_not:N \@@_grab_v_aux_catcodes: + \exp_not:n { \int_set:Nn \l_@@_v_nesting_int { 1 } } + \exp_not:N \@@_grab_v_aux_put:N \iow_char:N \{ + \exp_not:N \@@_grab_v_bgroup_loop: + } +\cs_new_protected:Npn \@@_grab_v_bgroup_loop: + { + \peek_N_type:TF + { \@@_grab_v_bgroup_loop:N } + { \@@_grab_v_aux_abort:n { } } + } +\cs_new_protected:Npn \@@_grab_v_bgroup_loop:N #1 + { + \@@_grab_v_token_if_char:NTF #1 + { + \token_if_eq_charcode:NNTF \c_group_end_token #1 + { + \int_decr:N \l_@@_v_nesting_int + \int_compare:nNnTF \l_@@_v_nesting_int > 0 + { + \@@_grab_v_aux_put:N #1 + \@@_grab_v_bgroup_loop: + } + { \@@_grab_v_aux_loop_end: } + } + { + \token_if_eq_charcode:NNT \c_group_begin_token #1 + { \int_incr:N \l_@@_v_nesting_int } + \@@_grab_v_aux_put:N #1 + \@@_grab_v_bgroup_loop: + } + } + { \@@_grab_v_aux_abort:n {#1} #1 } + } +% \end{macrocode} +% \end{variable} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_grab_v_aux_catcodes:} +% \begin{macro}{\@@_grab_v_aux_abort:n} +% In a standalone format, the list of special characters is kept +% as a sequence, \cs{c_@@_special_chars_seq}, and we use +% \tn{dospecials} in package mode. +% The approach for short verbatim arguments is to make the end-line +% character a macro parameter character: this is forbidden by the +% rest of the code. Then the error branch can check what caused the +% bail out and give the appropriate error message. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_v_aux_catcodes: + { +%<*initex> + \seq_map_function:NN + \c_@@_special_chars_seq + \char_set_catcode_other:N +% +%<*package> + \cs_set_eq:NN \do \char_set_catcode_other:N + \dospecials +% + \tex_endlinechar:D = `\^^M \scan_stop: + \bool_if:NTF \l_@@_long_bool + { \char_set_catcode_other:n { \tex_endlinechar:D } } + { \char_set_catcode_parameter:n { \tex_endlinechar:D } } + } +\cs_new_protected:Npn \@@_grab_v_aux_abort:n #1 + { + \@@_grab_v_group_end: + \exp_after:wN \exp_after:wN \exp_after:wN + \peek_meaning_remove:NTF \char_generate:nn { \tex_endlinechar:D } { 6 } + { + \__kernel_msg_error:nnxxx { xparse } { verbatim-newline } + { \@@_environment_or_command: } + { \tl_to_str:N \l_@@_v_arg_tl } + { \tl_to_str:n {#1} } + \@@_add_arg:o \c_novalue_tl + } + { + \__kernel_msg_error:nnxxx { xparse } { verbatim-tokenized } + { \@@_environment_or_command: } + { \tl_to_str:N \l_@@_v_arg_tl } + { \tl_to_str:n {#1} } + \@@_add_arg:o \c_novalue_tl + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_grab_v_aux_put:N} +% Storing one token in the collected argument. Most tokens are +% converted to category code $12$, with the exception of active +% characters, and spaces (not sure what should be done for those). +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_v_aux_put:N #1 + { + \tl_put_right:Nx \l_@@_v_arg_tl + { + \token_if_active:NTF #1 + { \exp_not:N #1 } { \token_to_str:N #1 } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_grab_v_token_if_char:NTF} +% This function assumes that the escape character is printable. +% Then the string representation of control sequences is at least +% two characters, and \cs{str_tail:n} only removes the escape +% character. Macro parameter characters are doubled by +% \cs{tl_to_str:n}, and will also yield a non-empty result, +% hence are not considered as characters. +% \begin{macrocode} +\cs_new_protected:Npn \@@_grab_v_token_if_char:NTF #1 + { \str_if_eq:eeTF { } { \str_tail:n {#1} } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_add_arg:n, \@@_add_arg:V, \@@_add_arg:o, \@@_add_arg:x} +% When an argument is found it is stored, then further arguments are +% grabbed by calling \cs{l_@@_signature_tl}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_add_arg:n #1 + { + \tl_put_right:Nn \l_@@_args_tl { {#1} } + \l_@@_signature_tl \@@_run_code: + } +\cs_generate_variant:Nn \@@_add_arg:n { V , o , x } +% \end{macrocode} +% \end{macro} +% +% \subsection{Grabbing arguments expandably} +% +% \begin{macro}[EXP]{\@@_expandable_grab_D:w} +% \begin{macro}[EXP]{\@@_expandable_grab_D:NNNwNNn} +% \begin{macro}[EXP]{\@@_expandable_grab_D:NNNwNNnnn} +% \begin{macro}[EXP]{\@@_expandable_grab_D:Nw} +% \begin{macro}[EXP]{\@@_expandable_grab_D:nnNNNwNN} +% The first step is to grab the first token or group. The generic grabbers +% \cs{\meta{function}}\verb*| | and \cs{\meta{function}}\verb*| | are just after \cs{q_@@}, we go and find +% them (and use the long one). +% \begin{macrocode} +\cs_new:Npn \@@_expandable_grab_D:w #1 \q_@@ #2#3 + { #2 { \@@_expandable_grab_D:NNNwNNn #1 \q_@@ #2 #3 } } +% \end{macrocode} +% We then wish to test whether |#7|, which we just grabbed, is exactly |#2|. +% A preliminary test is whether their string representations coincide, then +% expand the only grabber function we have, |#1|, once: the two strings below +% are equal if and only if |#7| matches |#2| exactly.\footnote{It is obvious +% that if \texttt{\#7} matches \texttt{\#2} then the strings are equal. We +% must check the converse. The right-hand-side of \cs{str_if_eq:onTF} does +% not end with \texttt{\#3}, implying that the grabber function took +% everything as its arguments. The first brace group can only be empty if +% \texttt{\#7} starts with \texttt{\#2}, otherwise the brace group preceding +% \texttt{\#7} would not vanish. The third brace group is empty, thus the +% \cs{q_@@} that was used by our grabber \texttt{\#1} must be the one +% that we inserted (not some token in \texttt{\#7}), hence the second brace +% group contains the end of \texttt{\#7} followed by \texttt{\#2}. Since this +% is \texttt{\#2} on the right-hand-side, and no brace can be lost there, +% \texttt{\#7} must contain nothing else than its leading \texttt{\#2}.} +% The preliminary test is needed as |#7| could validly contain +% \tn{par} (because a later mandatory argument could be long) and our +% grabber may be short. If +% |#7| does not match |#2|, then the optional argument is missing, we use the +% default |-NoValue-|, and put back the argument |#7| in the input stream. +% +% If it does match, then interesting things need to be done. We will grab the +% argument piece by piece, with the following pattern: +% \begin{quote} +% \meta{grabber} \Arg{tokens} \\ +% ~~\cs{q_nil} \Arg{piece 1} \meta{piece 2} \cs{ERROR} \cs{q_@@}\\ +% ~~\cs{q_nil} \meta{input stream} +% \end{quote} +% The \meta{grabber} will find an opening delimiter in \meta{piece 2}, take +% the \cs{q_@@} as a second delimiter, and find more material delimited +% by the closing delimiter in the \meta{input stream}. We then move the part +% before the opening delimiter from \meta{piece 2} to \meta{piece 1}, and the +% material taken from the \meta{input stream} to the \meta{piece 2}. Thus, +% the argument moves gradually from the \meta{input stream} to the +% \meta{piece 2}, then to the \meta{piece 1} when we have made sure to find +% all opening and closing delimiters. This two-step process ensures that +% nesting works: the number of opening delimiters minus closing delimiters in +% \meta{piece 1} is always equal to the number of closing delimiters in +% \meta{piece 2}. We stop grabbing arguments once the \meta{piece 2} contains +% no opening delimiter any more, hence the balance is reached, and the final +% argument is \meta{piece 1} \meta{piece 2}. +% The indirection via \cs{@@_tmp:w} allows to insert |-NoValue-| expanded. +% \begin{macrocode} +\cs_set_protected:Npn \@@_tmp:w #1 + { + \cs_new:Npn \@@_expandable_grab_D:NNNwNNn ##1##2##3##4 \q_@@ ##5##6##7 + { + \str_if_eq:nnTF {##2} {##7} + { + \str_if_eq:onTF + { ##1 { } { } ##7 ##2 \q_@@ ##3 } + { { } {##2} { } } + } + { \use_ii:nn } + { + ##1 + { \@@_expandable_grab_D:NNNwNNnnn ##1##2##3##4 \q_@@ ##5##6 } + \q_nil { } ##2 \ERROR \q_@@ \ERROR + } + { ##4 {#1} \q_@@ ##5 ##6 {##7} } + } + } +\exp_args:No \@@_tmp:w { \c_novalue_tl } +% \end{macrocode} +% At this stage, |#7| is \cs{q_nil} \Arg{piece 1} \meta{more for piece 1}, +% and we want to concatenate all that, removing \cs{q_nil}, and keeping the +% opening delimiter |#2|. Simply use \cs{use_ii:nn}. Also, |#8| is +% \meta{remainder of piece 2} \cs{ERROR}, and |#9| is \cs{ERROR} \meta{more +% for piece 2}. We concatenate those, replacing the two \cs{ERROR} by the +% closing delimiter |#3|. +% \begin{macrocode} +\cs_new:Npn \@@_expandable_grab_D:NNNwNNnnn #1#2#3#4 \q_@@ #5#6#7#8#9 + { + \exp_args:Nof \@@_expandable_grab_D:nnNNNwNN + { \use_ii:nn #7 #2 } + { \@@_expandable_grab_D:Nw #3 \exp_stop_f: #8 #9 } + #1#2#3 #4 \q_@@ #5 #6 + } +\cs_new:Npn \@@_expandable_grab_D:Nw #1#2 \ERROR \ERROR { #2 #1 } +% \end{macrocode} +% Armed with our two new \meta{pieces}, we are ready to loop. However, we +% must first see if \meta{piece 2} (here |#2|) contains any opening +% delimiter |#4|. Again, we expand |#3|, this time removing its whole output +% with \cs{use_none:nnn}. The test is similar to \cs{tl_if_in:nnTF}. The +% token list is empty if and only if |#2| does not contain the opening +% delimiter. In that case, we are done, and put the argument (from which we +% remove a spurious pair of delimiters coming from how we started the loop). +% Otherwise, we go back to looping with +% \cs{@@_expandable_grab_D:NNNwNNnnn}. The code to deal with brace stripping +% is much the same as for the non-expandable case. +% \begin{macrocode} +\cs_new:Npn \@@_expandable_grab_D:nnNNNwNN #1#2#3#4#5#6 \q_@@ #7#8 + { + \exp_args:No \tl_if_empty:oTF + { #3 { \use_none:nnn } #2 \q_@@ #5 #4 \q_@@ #5 } + { + \tl_if_blank:oTF { \use_none:nn #1#2 } + { \@@_put_arg_expandable:ow { \use_none:nn #1#2 } } + { + \str_if_eq:eeTF + { \exp_not:o { \use_none:nn #1#2 } } + { { \exp_not:o { \use_iii:nnnn #1#2 \q_nil } } } + { \@@_put_arg_expandable:ow { \use_iii:nnn #1#2 } } + { \@@_put_arg_expandable:ow { \use_none:nn #1#2 } } + } + #6 \q_@@ #7 #8 + } + { + #3 + { \@@_expandable_grab_D:NNNwNNnnn #3#4#5#6 \q_@@ #7 #8 } + \q_nil {#1} #2 \ERROR \q_@@ \ERROR + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_expandable_grab_D_alt:w} +% \begin{macro}[EXP]{\@@_expandable_grab_D_alt:NNwNNn} +% \begin{macro}[EXP]{\@@_expandable_grab_D_alt:Nwn} +% When the delimiters are identical, nesting is not possible and a simplified +% approach is used. The test concept here is the same as for the case where +% the delimiters are different but there cannot be any nesting. +% \begin{macrocode} +\cs_new:Npn \@@_expandable_grab_D_alt:w #1 \q_@@ #2#3 + { #2 { \@@_expandable_grab_D_alt:NNwNNn #1 \q_@@ #2 #3 } } +\cs_set_protected:Npn \@@_tmp:w #1 + { + \cs_new:Npn \@@_expandable_grab_D_alt:NNwNNn ##1##2##3 \q_@@ ##4##5##6 + { + \str_if_eq:nnTF {##6} {##2} + { + \str_if_eq:onTF + { ##1 { } ##6 ##2 ##2 } + { { } ##2 } + } + { \use_ii:nn } + { + ##1 + { \@@_expandable_grab_D_alt:NNwn ##4 ##5 ##3 \q_@@ } + ##6 \ERROR + } + { ##3 {#1} \q_@@ ##4 ##5 {##6} } + } + } +\exp_args:No \@@_tmp:w { \c_novalue_tl } +\cs_new:Npn \@@_expandable_grab_D_alt:NNwn #1#2#3 \q_@@ #4 + { + \tl_if_blank:oTF { \use_none:n #4 } + { \@@_put_arg_expandable:ow { \use_none:n #4 } } + { + \str_if_eq:eeTF + { \exp_not:o { \use_none:n #4 } } + { { \exp_not:o { \use_ii:nnn #4 \q_nil } } } + { \@@_put_arg_expandable:ow { \use_ii:nn #4 } } + { \@@_put_arg_expandable:ow { \use_none:n #4 } } + } + #3 \q_@@ #1 #2 + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_expandable_grab_E:w, \@@_expandable_grab_E_long:w} +% \begin{macro}[EXP]{\@@_expandable_grab_E_aux:w} +% \begin{macro}[EXP]{\@@_expandable_grab_E_test:nnw} +% \begin{macro}[EXP]{\@@_expandable_grab_E_loop:nnnNNw} +% \begin{macro}[EXP]{\@@_expandable_grab_E_find:w} +% \begin{macro}[EXP]{\@@_expandable_grab_E_find:nnw} +% \begin{macro}[EXP]{\@@_expandable_grab_E_end:nnw} +% We keep track of long/short by placing the appropriate grabber as +% the third token after \cs{q_@@}; it is eventually removed by the +% \texttt{end:nnw} auxiliary. The \texttt{aux:w} auxiliary will be +% called repeatedly with two arguments: the set +% of pairs \meta{parser} \meta{token}, and the set of arguments found +% so far (initially all |{-NoValue-}|). At each step, grab what +% follows in the input stream then call the \texttt{loop:nnnNNw} +% auxiliary to compare it with each possible embellishment in turn. +% This auxiliary's |#1| is what was found in the input, |#2| collects +% \meta{parser} \meta{token} pairs that did not match, |#3| collects +% the corresponding arguments found previously, |#4| and |#5| is the +% current pair, |#6| is the remaining pairs, |#7| is empty or two +% \cs{q_nil}, and |#8| is the current argument. If none of the pairs +% matched (determined by \cs{quark_if_nil:NTF}) then call the +% \texttt{end} auxiliary to stop looking for embellishments, +% remembering to put what was grabbed in the input back where it +% belongs, and storing the arguments found just before \cs{q_@@}. If +% the current argument |#8| is not |-NoValue-| or if the input |#1| +% does not match |#5| (see \texttt{t}-type arguments below for a +% similar \cs{str_if_eq:onTF} test) then carry on the loop. +% Otherwise, we found a new embellishment: grab the corresponding +% argument in the input using the \texttt{find:w} auxiliary. To avoid +% losing braces around that auxiliary's argument we include a +% space, which will be eliminated in the next loop through +% embellishments. +% \begin{macrocode} +\cs_new:Npn \@@_expandable_grab_E:w #1 \q_@@ #2#3 + { \@@_expandable_grab_E_aux:w #1 \q_@@ #2 #3 #3 } +\cs_new:Npn \@@_expandable_grab_E_long:w #1 \q_@@ #2#3 + { \@@_expandable_grab_E_aux:w #1 \q_@@ #2 #3 #2 } +\cs_new:Npn \@@_expandable_grab_E_aux:w #1 \q_@@ #2#3#4 + { #2 { \@@_expandable_grab_E_test:nnw #1 \q_@@ #2 #3 #4 } } +\cs_new:Npn \@@_expandable_grab_E_test:nnw #1#2#3 \q_@@ #4#5#6#7 + { + \@@_expandable_grab_E_loop:nnnNNw {#7} { } { } + #1 \q_nil \q_nil \q_nil \q_mark #2 \q_nil + #3 \q_@@ #4 #5 #6 + } +\cs_new:Npn \@@_expandable_grab_E_loop:nnnNNw + #1#2#3#4#5#6 \q_nil #7 \q_mark #8 + { + \quark_if_nil:NTF #4 + { \@@_expandable_grab_E_end:nnw {#1} {#3} } + { + \tl_if_novalue:nTF {#8} + { \str_if_eq:onTF { #4 { } #1 #5 } {#5} } + { \use_ii:nn } + { \@@_expandable_grab_E_find:w { #2 #4 #5 #6 } {#3} ~ } + { + \@@_expandable_grab_E_loop:nnnNNw + {#1} { #2 #4 #5 } { #3 {#8} } + #6 \q_nil #7 \q_mark + } + } + } +\cs_new:Npn \@@_expandable_grab_E_find:w #1 \q_@@ #2#3#4 + { #4 { \@@_expandable_grab_E_find:nnw #1 \q_@@ #2 #3 #4 } } +\cs_new:Npn \@@_expandable_grab_E_find:nnw #1#2#3 \q_nil #4 \q_@@ #5#6#7#8 + { \@@_expandable_grab_E_aux:w {#1} { #2 {#8} #3 } #4 \q_@@ #5 #6 #7 } +\cs_new:Npn \@@_expandable_grab_E_end:nnw #1#2#3 \q_@@ #4#5#6 + { #3 #2 \q_@@ #4 #5 {#1} } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_expandable_grab_m:w, \@@_expandable_grab_m_long:w} +% \begin{macro}[EXP]{\@@_expandable_grab_m_aux:wNn} +% The mandatory case is easy: find the auxiliary after the \cs{q_@@}, +% and use it directly to grab the argument, then correctly position +% the argument before \cs{q_@@}. +% \begin{macrocode} +\cs_new:Npn \@@_expandable_grab_m:w #1 \q_@@ #2#3 + { #3 { \@@_expandable_grab_m_aux:wNn #1 \q_@@ #2 #3 } } +\cs_new:Npn \@@_expandable_grab_m_long:w #1 \q_@@ #2#3 + { #2 { \@@_expandable_grab_m_aux:wNn #1 \q_@@ #2 #3 } } +\cs_new:Npn \@@_expandable_grab_m_aux:wNn #1 \q_@@ #2#3#4 + { #1 {#4} \q_@@ #2 #3 } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_expandable_grab_R:w} +% \begin{macro}[EXP]{\@@_expandable_grab_R_aux:NNNwNNn} +% Much the same as for the \texttt{D}-type argument, with only the lead-off +% function varying. +% \begin{macrocode} +\cs_new:Npn \@@_expandable_grab_R:w #1 \q_@@ #2#3 + { #2 { \@@_expandable_grab_R_aux:NNNwNNn #1 \q_@@ #2#3 } } +\cs_set_protected:Npn \@@_tmp:w #1 + { + \cs_new:Npn \@@_expandable_grab_R_aux:NNNwNNn ##1##2##3##4 \q_@@ ##5##6##7 + { + \str_if_eq:nnTF {##7} {##2} + { + \str_if_eq:onTF + { ##1 { } { } ##7 ##2 \q_@@ ##3 } + { { } {##2} { } } + } + { \use_ii:nn } + { + ##1 + { \@@_expandable_grab_D:NNNwNNnnn ##1##2##3##4 \q_@@ ##5##6 } + \q_nil { } ##2 \ERROR \q_@@ \ERROR + } + { + \__kernel_msg_expandable_error:nnff + { xparse } { missing-required } + { \exp_args:Nf \tl_trim_spaces:n { \token_to_str:N ##5 } } + { \tl_to_str:n {##2} } + ##4 {#1} \q_@@ ##5 ##6 {##7} + } + } + } +\exp_args:No \@@_tmp:w { \c_novalue_tl } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_expandable_grab_R_alt:w} +% \begin{macro}[EXP]{\@@_expandable_grab_R_alt_aux:NNwNNn} +% When the delimiters are identical, nesting is not possible and a simplified +% approach is used. The test concept here is the same as for the case where +% the delimiters are different. +% \begin{macrocode} +\cs_new:Npn \@@_expandable_grab_R_alt:w #1 \q_@@ #2#3 + { #2 { \@@_expandable_grab_R_alt_aux:NNnwNn #1 \q_@@ #2#3 } } +\cs_set_protected:Npn \@@_tmp:w #1 + { + \cs_new:Npn \@@_expandable_grab_R_alt_aux:NNwNn ##1##2##3 \q_@@ ##4##5##6 + { + \str_if_eq:nnTF {##6} {##2} + { + \str_if_eq:onTF + { ##1 { } ##6 ##2 ##2 } + { { } ##2 } + } + { \use_ii:nn } + { + ##1 + { \@@_expandable_grab_D_alt:NNwn ##4 ##5 ##3 \q_@@ } + ##6 \ERROR + } + { + \__kernel_msg_expandable_error:nnff + { xparse } { missing-required } + { \exp_args:Nf \tl_trim_spaces:n { \token_to_str:N ##4 } } + { \tl_to_str:n {##2} } + ##3 {#1} \q_@@ ##4 ##5 {##6} + } + } + } +\exp_args:No \@@_tmp:w { \c_novalue_tl } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_expandable_grab_t:w} +% \begin{macro}[EXP]{\@@_expandable_grab_t_aux:NNwn} +% As for a \texttt{D}-type argument, here we compare the grabbed tokens using +% the only parser we have in order to work out if |#2| is exactly equal to +% the output of the grabber. +% \begin{macrocode} +\cs_new:Npn \@@_expandable_grab_t:w #1 \q_@@ #2#3 + { #2 { \@@_expandable_grab_t_aux:NNwn #1 \q_@@ #2 #3 } } +\cs_new:Npn \@@_expandable_grab_t_aux:NNwn #1#2#3 \q_@@ #4#5#6 + { + \str_if_eq:onTF { #1 { } #6 #2 } {#2} + { #3 { \BooleanTrue } \q_@@ #4 #5 } + { #3 { \BooleanFalse } \q_@@ #4 #5 {#6} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\@@_expandable_grab_u:w} +% It turns out there is nothing to do: this is followed by an +% auxiliary named after the function, that does everything. +% \begin{macrocode} +\cs_new_eq:NN \@@_expandable_grab_u:w \prg_do_nothing: +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[EXP] +% {\@@_put_arg_expandable:nw, \@@_put_arg_expandable:ow} +% A useful helper, to store arguments when they are ready. +% \begin{macrocode} +\cs_new:Npn \@@_put_arg_expandable:nw #1#2 \q_@@ { #2 {#1} \q_@@ } +\cs_generate_variant:Nn \@@_put_arg_expandable:nw { o } +% \end{macrocode} +% \end{macro} +% +% \subsection{Argument processors} +% +% \begin{macro}{\@@_bool_reverse:N} +% A simple reversal. +% \begin{macrocode} +\cs_new_protected:Npn \@@_bool_reverse:N #1 + { + \bool_if:NTF #1 + { \tl_set:Nn \ProcessedArgument { \c_false_bool } } + { \tl_set:Nn \ProcessedArgument { \c_true_bool } } + } +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\l_@@_split_list_seq, \l_@@_split_list_tl} +% \begin{macro}{\@@_split_list:nn} +% \begin{macro}{\@@_split_list_multi:nn, \@@_split_list_multi:nV} +% \begin{macro}{\@@_split_list_single:Nn} +% Splitting can take place either at a single token or at a longer +% identifier. To deal with single active tokens, a two-part procedure is +% needed. +% \begin{macrocode} +\seq_new:N \l_@@_split_list_seq +\tl_new:N \l_@@_split_list_tl +\cs_new_protected:Npn \@@_split_list:nn #1#2 + { + \tl_if_single:nTF {#1} + { + \token_if_cs:NTF #1 + { \@@_split_list_multi:nn {#1} {#2} } + { \@@_split_list_single:Nn #1 {#2} } + } + { \@@_split_list_multi:nn {#1} {#2} } + } +\cs_new_protected:Npn \@@_split_list_multi:nn #1#2 + { + \seq_set_split:Nnn \l_@@_split_list_seq {#1} {#2} + \tl_clear:N \ProcessedArgument + \seq_map_inline:Nn \l_@@_split_list_seq + { \tl_put_right:Nn \ProcessedArgument { {##1} } } + } +\cs_generate_variant:Nn \@@_split_list_multi:nn { nV } +\group_begin: +\char_set_catcode_active:N \^^@ +\cs_new_protected:Npn \@@_split_list_single:Nn #1#2 + { + \tl_set:Nn \l_@@_split_list_tl {#2} + \group_begin: + \char_set_lccode:nn { `\^^@ } { `#1 } + \tex_lowercase:D + { + \group_end: + \tl_replace_all:Nnn \l_@@_split_list_tl { ^^@ } + } {#1} + \@@_split_list_multi:nV {#1} \l_@@_split_list_tl + } +\group_end: +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{variable} +% +% \begin{macro}{\@@_split_argument:nnn} +% \begin{macro}{\@@_split_argument_aux:nnnn} +% \begin{macro}[EXP]{\@@_split_argument_aux:n} +% \begin{macro}[rEXP]{\@@_split_argument_aux:wn} +% Splitting to a known number of items is a special version of splitting +% a list, in which the limit is hard-coded and where there will always be +% exactly the correct number of output items. An auxiliary function is +% used to save on working out the token list length several times. +% \begin{macrocode} +\cs_new_protected:Npn \@@_split_argument:nnn #1#2#3 + { + \@@_split_list:nn {#2} {#3} + \exp_args:Nf \@@_split_argument_aux:nnnn + { \tl_count:N \ProcessedArgument } + {#1} {#2} {#3} + } +\cs_new_protected:Npn \@@_split_argument_aux:nnnn #1#2#3#4 + { + \int_compare:nNnF {#1} = { #2 + 1 } + { + \int_compare:nNnTF {#1} > { #2 + 1 } + { + \tl_set:Nx \ProcessedArgument + { + \exp_last_unbraced:NnNo + \@@_split_argument_aux:n + { #2 + 1 } + \use_none_delimit_by_q_stop:w + \ProcessedArgument + \q_stop + } + \__kernel_msg_error:nnxxx { xparse } { split-excess-tokens } + { \tl_to_str:n {#3} } { \int_eval:n { #2 + 1 } } + { \tl_to_str:n {#4} } + } + { + \tl_put_right:Nx \ProcessedArgument + { + \prg_replicate:nn { #2 + 1 - (#1) } + { { \exp_not:V \c_novalue_tl } } + } + } + } + } +% \end{macrocode} +% Auxiliaries to leave exactly the correct number of arguments in +% \cs{ProcessedArgument}. +% \begin{macrocode} +\cs_new:Npn \@@_split_argument_aux:n #1 + { \prg_replicate:nn {#1} { \@@_split_argument_aux:wn } } +\cs_new:Npn \@@_split_argument_aux:wn #1 \use_none_delimit_by_q_stop:w #2 + { + \exp_not:n { {#2} } + #1 + \use_none_delimit_by_q_stop:w + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_trim_spaces:n} +% This one is almost trivial. +% \begin{macrocode} +\cs_new_protected:Npn \@@_trim_spaces:n #1 + { \tl_set:Nx \ProcessedArgument { \tl_trim_spaces:n {#1} } } +% \end{macrocode} +% \end{macro} +% +% \subsection{Access to the argument specification} +% +% \begin{macro}{\@@_get_arg_spec_error:N, \@@_get_arg_spec_error:n} +% \begin{macro}{\@@_get_arg_spec_error_aux:n} +% Provide an informative error when trying to get the argument +% specification of a non-\pkg{xparse} command or environment. +% \begin{macrocode} +\cs_new_protected:Npn \@@_get_arg_spec_error:N #1 + { + \bool_set_false:N \l_@@_environment_bool + \tl_set:Nn \l_@@_fn_tl {#1} + \@@_get_arg_spec_error_aux:n { \cs_if_exist:NTF #1 } + } +\cs_new_protected:Npn \@@_get_arg_spec_error:n #1 + { + \bool_set_true:N \l_@@_environment_bool + \str_set:Nx \l_@@_environment_str {#1} + \@@_get_arg_spec_error_aux:n + { \cs_if_exist:cTF { \l_@@_environment_str } } + } +\cs_new_protected:Npn \@@_get_arg_spec_error_aux:n #1 + { + #1 + { + \__kernel_msg_error:nnx { xparse } { non-xparse } + { \@@_environment_or_command: } + } + { + \__kernel_msg_error:nnx { xparse } { unknown } + { \@@_environment_or_command: } + } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_get_arg_spec:NTF} +% If the command is not an \pkg{xparse} command, complain. If it is, +% its second \enquote{item} is the argument specification. +% \begin{macrocode} +\cs_new_protected:Npn \@@_get_arg_spec:NTF #1#2#3 + { + \@@_cmd_if_xparse:NTF #1 + { + \tl_set:Nx \ArgumentSpecification { \tl_item:Nn #1 { 2 } } + #2 + } + {#3} + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_get_arg_spec:N} +% \begin{macro}{\@@_get_arg_spec:n} +% \begin{variable}{\ArgumentSpecification} +% Recovering the argument specification is now trivial. +% \begin{macrocode} +\cs_new_protected:Npn \@@_get_arg_spec:N #1 + { + \@@_get_arg_spec:NTF #1 { } + { \@@_get_arg_spec_error:N #1 } + } +\cs_new_protected:Npn \@@_get_arg_spec:n #1 + { + \exp_args:Nc \@@_get_arg_spec:NTF + { environment~ \tl_to_str:n {#1} } + { } + { \@@_get_arg_spec_error:n {#1} } + } +\tl_new:N \ArgumentSpecification +% \end{macrocode} +% \end{variable} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_show_arg_spec:N} +% \begin{macro}{\@@_show_arg_spec:n} +% Showing the argument specification simply means finding it and then +% calling the \cs{tl_show:N} function. +% \begin{macrocode} +\cs_new_protected:Npn \@@_show_arg_spec:N #1 + { + \@@_get_arg_spec:NTF #1 + { \tl_show:N \ArgumentSpecification } + { \@@_get_arg_spec_error:N #1 } + } +\cs_new_protected:Npn \@@_show_arg_spec:n #1 + { + \exp_args:Nc \@@_get_arg_spec:NTF + { environment~ \tl_to_str:n {#1} } + { \tl_show:N \ArgumentSpecification } + { \@@_get_arg_spec_error:n {#1} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \subsection{Utilities} +% +% \begin{macro}{\@@_check_definable:nNT, \@@_check_definable_aux:nN} +% Check that a token list is appropriate as a first argument of +% \cs{NewDocumentCommand} and similar functions and otherwise +% produce an error. First trim whitespace to allow for spaces around +% the actual command to be defined. If the result has multiple +% tokens, it is not a valid argument. The single token is a control +% sequence exactly if its string representation has more than one +% character (using \cs{token_to_str:N} rather than \cs{tl_to_str:n} +% to avoid problems with macro parameter characters, and setting +% \cs{tex_escapechar:D} to prevent it from being non-printable). +% Finally, check for an active character: this is done by lowercasing +% the token to fix its character code (arbitrarily to that of~|?|) +% and comparing the result to an active~|?|. Both control sequences +% and active characters are valid arguments, and non-active character +% tokens are not. In all cases, the group opened to keep assignments +% local must be closed. +% \begin{macrocode} +\cs_new_protected:Npn \@@_check_definable:nNT #1 + { \tl_trim_spaces_apply:nN {#1} \@@_check_definable_aux:nN } +\group_begin: + \char_set_catcode_active:n { `? } + \cs_new_protected:Npn \@@_check_definable_aux:nN #1#2 + { + \group_begin: + \tl_if_single_token:nTF {#1} + { + \int_set:Nn \tex_escapechar:D { 92 } + \exp_args:Nx \tl_if_empty:nTF + { \exp_args:No \str_tail:n { \token_to_str:N #1 } } + { + \exp_args:Nx \char_set_lccode:nn + { ` \str_head:n {#1} } { `? } + \tex_lowercase:D { \tl_if_eq:nnTF {#1} } { ? } + { \group_end: \use_iii:nnn } + { \group_end: \use_i:nnn } + } + { \group_end: \use_iii:nnn } + } + { \group_end: \use_ii:nnn } + { + \__kernel_msg_error:nnxx { xparse } { not-definable } + { \tl_to_str:n {#1} } { \token_to_str:N #2 } + } + { + \__kernel_msg_error:nnxx { xparse } { not-one-token } + { \tl_to_str:n {#1} } { \token_to_str:N #2 } + } + } +\group_end: +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_tl_mapthread_function:NNN, \@@_tl_mapthread_function:nnN} +% \begin{macro}{\@@_tl_mapthread_loop:w} +% Analogue of \cs{seq_mapthread_function:NNN} for token lists. +% \begin{macrocode} +\cs_new:Npn \@@_tl_mapthread_function:NNN #1#2#3 + { + \exp_after:wN \exp_after:wN + \exp_after:wN \@@_tl_mapthread_loop:w + \exp_after:wN \exp_after:wN + \exp_after:wN #3 + \exp_after:wN #1 + \exp_after:wN \q_recursion_tail + \exp_after:wN \q_mark + #2 + \q_recursion_tail + \q_recursion_stop + } +\cs_new:Npn \@@_tl_mapthread_function:nnN #1#2#3 + { + \@@_tl_mapthread_loop:w #3 + #1 \q_recursion_tail \q_mark + #2 \q_recursion_tail \q_recursion_stop + } +\cs_new:Npn \@@_tl_mapthread_loop:w #1#2#3 \q_mark #4 + { + \quark_if_recursion_tail_stop:n {#2} + \quark_if_recursion_tail_stop:n {#4} + #1 {#2} {#4} + \@@_tl_mapthread_loop:w #1#3 \q_mark + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_cmd_if_xparse:NTF} +% \begin{macro}{\@@_cmd_if_xparse_aux:N} +% To determine whether the command is an \pkg{xparse} command check +% that its |arg_spec| is empty (this also excludes non-macros) and +% that its |replacement_spec| starts with either \cs{@@_start:nNNnnn} +% (non-expandable command) or \cs{@@_start_expandable:nNNNNn} +% (expandable command) or \cs{@@_start_env:nnnnn} (environment). +% \begin{macrocode} +\cs_new_protected:Npn \@@_cmd_if_xparse:NTF #1 + { + \exp_args:Nf \str_case_e:nnTF + { + \exp_args:Nf \tl_if_empty:nT { \cs_argument_spec:N #1 } + { + \exp_last_unbraced:Nf \@@_cmd_if_xparse_aux:w + { \cs_replacement_spec:N #1 } ~ \q_stop + } + } + { + { \token_to_str:N \@@_start:nNNnnn } { } + { \token_to_str:N \@@_start_expandable:nNNNNn } { } + { \token_to_str:N \@@_start_env:nnnnn } { } + } + } +\cs_new:Npn \@@_cmd_if_xparse_aux:w #1 ~ #2 \q_stop {#1} +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\@@_peek_nonspace:NTF, \@@_peek_nonspace_remove:NTF, \@@_peek_nonspace_aux:nNNTF} +% Collect spaces in a loop, and put the collected spaces back in the +% false branch of a call to \cs{peek_meaning:NTF} or +% \cs{peek_meaning_remove:NTF}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_nonspace:NTF + { \@@_peek_nonspace_aux:nNNTF { } \peek_meaning:NTF } +\cs_new_protected:Npn \@@_peek_nonspace_remove:NTF + { \@@_peek_nonspace_aux:nNNTF { } \peek_meaning_remove:NTF } +\cs_new_protected:Npn \@@_peek_nonspace_aux:nNNTF #1#2#3#4#5 + { + \peek_meaning_remove:NTF \c_space_token + { \@@_peek_nonspace_aux:nNNTF { #1 ~ } #2 #3 {#4} {#5} } + { #2 #3 { #4 } { #5 #1 } } + } +% \end{macro} +% +% \subsection{Messages} +% +% \begin{macro}{\@@_environment_or_command:} +% \begin{variable}{\c_@@_ignore_def_tl} +% Two texts used in several messages. +% \begin{macrocode} +\cs_new:Npn \@@_environment_or_command: + { + \bool_if:NTF \l_@@_environment_bool + { environment ~ ' \l_@@_environment_str ' } + { + command ~ ' + \exp_args:Nf \tl_trim_spaces:n + { \exp_after:wN \token_to_str:N \l_@@_fn_tl } + ' + } + } +\tl_const:Nn \c_@@_ignore_def_tl + { \\ \\ LaTeX~will~ignore~this~entire~definition. } +% \end{macrocode} +% \end{variable} +% \end{macro} +% +% Some messages intended as errors when defining commands/environments. +% \begin{macrocode} +\__kernel_msg_new:nnnn { xparse } { arg-after-body } + { In~the~definition~of~#1,~b~(body)~argument~must~be~last. } + { + The~'body'~argument~type~is~followed~by~'#2'~in~the~argument~ + specification~of~the~#1.~This~is~not~allowed. + \c_@@_ignore_def_tl + } +\__kernel_msg_new:nnnn { xparse } { bad-arg-spec } + { Bad~argument~specification~'#2'~for~#1. } + { + The~argument~specification~provided~was~not~valid:~ + one~or~more~mandatory~pieces~of~information~were~missing. + \c_@@_ignore_def_tl + } +\__kernel_msg_new:nnnn { xparse } { command-already-defined } + { Command~'#1'~already~defined! } + { + You~have~used~#2~ + with~a~command~that~already~has~a~definition. \\ \\ + The~existing~definition~of~'#1'~will~not~be~altered. + } +\__kernel_msg_new:nnnn { xparse } { command-not-yet-defined } + { Command ~'#1'~not~yet~defined! } + { + You~have~used~#2~ + with~a~command~that~was~never~defined. + \c_@@_ignore_def_tl + } +\__kernel_msg_new:nnnn { xparse } { environment-already-defined } + { Environment~'#1'~already~defined! } + { + You~have~used~\NewDocumentEnvironment + with~an~environment~that~already~has~a~definition. \\ \\ + The~existing~definition~of~'#1'~will~not~be~altered. + } +\__kernel_msg_new:nnnn { xparse } { environment-not-yet-defined } + { Environment~'#1'~not~yet~defined! } + { + You~have~used~\RenewDocumentEnvironment + with~an~environment~that~was~never~defined. + \c_@@_ignore_def_tl + } +\__kernel_msg_new:nnnn { xparse } { expandable-ending-optional } + { + Argument~specification~'#2'~for~expandable~command~'#1'~ + ends~with~optional~argument. + } + { + Expandable~commands~must~have~a~final~mandatory~argument~ + (or~no~arguments~at~all).~You~cannot~have~a~terminal~optional~ + argument~with~expandable~commands. + } +\__kernel_msg_new:nnnn { xparse } { inconsistent-long } + { Inconsistent~long~arguments~for~expandable~command~'#1'. } + { + The~arguments~for~an~expandable~command~must~not~involve~short~ + arguments~after~long~arguments.~You~have~tried~to~mix~the~two~types. + } +\__kernel_msg_new:nnnn { xparse } { invalid-command-arg } + { Argument~type~'#2'~not~available~for~#1. } + { + The~letter~'#2'~can~only~be~used~in~environment~argument~ + specifications,~not~for~commands. + \\ \\ + LaTeX~will~ignore~this~entire~definition. + } +\__kernel_msg_new:nnnn { xparse } { invalid-expandable-argument-type } + { Argument~type~'#2'~not~available~for~expandable~command~'#1'. } + { + The~letter~'#2'~specifies~an~argument~type~which~cannot~be~used~ + in~an~expandable~command. + \c_@@_ignore_def_tl + } +\__kernel_msg_new:nnnn { xparse } { invalid-after-optional-expandably } + { + Argument~type~'#2'~not~available~after~optional~argument~ + for~expandable~command~'#1'. + } + { + The~letter~'#2'~specifies~an~argument~type~which~cannot~be~used~ + in~an~expandable~command~after~an~optional~argument. + \c_@@_ignore_def_tl + } +\__kernel_msg_new:nnnn { xparse } { non-trailing-obey-spaces } + { Prefix~'!'~used~before~mandatory~argument~'#2'~of~#1. } + { + The~prefix~'!'~can~only~apply~to~trailing~optional~arguments. + \c_@@_ignore_def_tl + } +\__kernel_msg_new:nnnn { xparse } { not-definable } + { First~argument~of~'#2'~must~be~a~command. } + { + The~first~argument~of~'#2'~should~be~the~document~command~that~will~ + be~defined.~The~provided~argument~'#1'~is~a~character.~Perhaps~a~ + backslash~is~missing? + \c_@@_ignore_def_tl + } +\__kernel_msg_new:nnnn { xparse } { not-one-token } + { First~argument~of~'#2'~must~be~a~command. } + { + The~first~argument~of~'#2'~should~be~the~document~command~that~will~ + be~defined.~The~provided~argument~'#1'~contains~more~than~one~ + token.~Perhaps~a~backslash~is~missing? + \c_@@_ignore_def_tl + } +\__kernel_msg_new:nnnn { xparse } { not-single-char } + { + Argument~delimiter~'#2'~for~the~#1~should~be~ + a~single~character. + } + { + The~argument~specification~provided~was~not~valid:~in~a~place~ + where~a~single~character~is~required,~LaTeX~found~'#2'. + \c_@@_ignore_def_tl + } +\__kernel_msg_new:nnnn { xparse } { processor-in-expandable } + { Argument~processor~'>{#2}'~cannot~be~used~for~the~expandable~command~'#1'. } + { + The~argument~specification~for~#1~contains~a~processor~function:~ + this~is~only~supported~for~standard~robust~commands. + \c_@@_ignore_def_tl + } +\__kernel_msg_new:nnnn { xparse } { too-many-arguments } + { Too~many~arguments~in~argument~specification~'#2'~of~#1. } + { + The~argument~specification~provided~has~more~than~9~arguments.~ + This~cannot~be~implemented. + \c_@@_ignore_def_tl + } +\__kernel_msg_new:nnnn { xparse } { two-markers } + { Two~'#2'~apply~to~the~same~argument~in~argument~specification~of~#1. } + { + The~argument~specification~provided~has~two~markers~'#2'~applying~ + to~the~same~argument;~these~are~redundant. + } +\__kernel_msg_new:nnnn { xparse } { unknown-argument-type } + { Unknown~argument~type~'#2'~for~the~#1. } + { + The~letter~'#2'~does~not~specify~a~known~argument~type. + \c_@@_ignore_def_tl + } +% \end{macrocode} +% +% Errors when using commands/environments. The \texttt{if-boolean} +% message is always used in expandable errors. The +% \texttt{loop-in-defaults} and \texttt{missing-required} messages can +% be expandable or not expandable. +% \begin{macrocode} +\__kernel_msg_new:nnn { xparse } { if-boolean } + { Invalid~use~\iow_char:N\\IfBooleanTF~{#1} } +\__kernel_msg_new:nnnn { xparse } { loop-in-defaults } + { Defaults~of~#1~have~circular~dependency. } + { + The~default~values~of~two~or~more~arguments~of~the~#1~ + depend~on~each~other~in~a~way~that~cannot~be~resolved. + } +\__kernel_msg_new:nnnn { xparse } { missing-required } + { Missing~required~argument~for~#1. } + { + The~current~#1~expects~an~argument~starting~with~'#2'.~ + LaTeX~did~not~find~it,~and~will~insert~a~default~value~to~be~processed. + } +\__kernel_msg_new:nnnn { xparse } { non-xparse } + { \str_upper_case:n #1~not~defined~using~xparse. } + { + You~have~asked~for~the~argument~specification~for~the~#1,~ + but~this~was~not~defined~using~xparse. + } +\__kernel_msg_new:nnnn { xparse } { split-excess-tokens } + { Too~many~'#1'~tokens~when~trying~to~split~argument. } + { + LaTeX~was~asked~to~split~the~input~'#3'~ + at~each~occurrence~of~the~token~'#1',~up~to~a~maximum~of~#2~parts.~ + There~were~too~many~'#1'~tokens. + } +\__kernel_msg_new:nnnn { xparse } { unknown } + { Unknown~document~#1. } + { + You~have~asked~for~the~argument~specification~for~the~#1,~ + but~it~is~not~defined. + } +\__kernel_msg_new:nnnn { xparse } { verbatim-newline } + { Verbatim~argument~of~#1~ended~by~end~of~line. } + { + The~verbatim~argument~of~the~#1~cannot~contain~more~than~one~line,~ + but~the~end~ + of~the~current~line~has~been~reached.~You~may~have~forgotten~the~ + closing~delimiter. + \\ \\ + LaTeX~will~ignore~'#2'. + } +\__kernel_msg_new:nnnn { xparse } { verbatim-tokenized } + { The~verbatim~#1~cannot~be~used~inside~an~argument. } + { + The~#1~takes~a~verbatim~argument.~ + It~may~not~appear~within~the~argument~of~another~function.~ + It~received~an~illegal~token \tl_if_empty:nF {#3} { ~'#3' } . + \\ \\ + LaTeX~will~ignore~'#2'. + } +% \end{macrocode} +% +% Intended more for information. +% \begin{macrocode} +\__kernel_msg_new:nnn { xparse } { define-command } + { + Defining~command~#1~ + with~sig.~'#2'~\msg_line_context:. + } +\__kernel_msg_new:nnn { xparse } { define-environment } + { + Defining~environment~'#1'~ + with~sig.~'#2'~\msg_line_context:. + } +\__kernel_msg_new:nnn { xparse } { redefine-command } + { + Redefining~command~#1~ + with~sig.~'#2'~\msg_line_context:. + } +\__kernel_msg_new:nnn { xparse } { redefine-environment } + { + Redefining~environment~'#1'~ + with~sig.~'#2'~\msg_line_context:. + } +\__kernel_msg_new:nnn { xparse } { optional-mandatory } + { + Since~the~mandatory~argument~'#1'~has~the~same~delimiter~'#2'~ + as~a~previous~optional~argument,~it~will~not~be~possible~to~ + omit~all~optional~arguments~when~calling~this~command. + } +\__kernel_msg_new:nnn { xparse } { unsupported-let } + { + The~command~'#1'~was~undefined~but~not~the~associated~commands~ + '#1~code'~and/or~'#1~defaults'.~Maybe~you~tried~using~ + \iow_char:N\\let.~This~may~lead~to~an~infinite~loop. + } +% \end{macrocode} +% +% \subsection{User functions} +% +% The user functions are more or less just the internal functions +% renamed. +% +% \begin{macro}{\BooleanFalse} +% \begin{macro}{\BooleanTrue} +% Design-space names for the Boolean values. +% \begin{macrocode} +\cs_new_eq:NN \BooleanFalse \c_false_bool +\cs_new_eq:NN \BooleanTrue \c_true_bool +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\NewDocumentCommand} +% \begin{macro}{\RenewDocumentCommand} +% \begin{macro}{\ProvideDocumentCommand} +% \begin{macro}{\DeclareDocumentCommand} +% The user macros are pretty simple wrappers around the internal ones. +% There is however a check that the first argument is a single token, +% possibly surrounded by spaces (hence the strange \cs{use:nnn}), and +% is definable. +% \begin{macrocode} +\cs_new_protected:Npn \NewDocumentCommand #1#2#3 + { + \@@_check_definable:nNT {#1} \NewDocumentCommand + { + \cs_if_exist:NTF #1 + { + \__kernel_msg_error:nnxx { xparse } { command-already-defined } + { \use:nnn \token_to_str:N #1 { } } + { \token_to_str:N \NewDocumentCommand } + } + { \@@_declare_cmd:Nnn #1 {#2} {#3} } + } + } +\cs_new_protected:Npn \RenewDocumentCommand #1#2#3 + { + \@@_check_definable:nNT {#1} \RenewDocumentCommand + { + \cs_if_exist:NTF #1 + { \@@_declare_cmd:Nnn #1 {#2} {#3} } + { + \__kernel_msg_error:nnxx { xparse } { command-not-yet-defined } + { \use:nnn \token_to_str:N #1 { } } + { \token_to_str:N \RenewDocumentCommand } + } + } + } +\cs_new_protected:Npn \ProvideDocumentCommand #1#2#3 + { + \@@_check_definable:nNT {#1} \ProvideDocumentCommand + { \cs_if_exist:NF #1 { \@@_declare_cmd:Nnn #1 {#2} {#3} } } + } +\cs_new_protected:Npn \DeclareDocumentCommand #1#2#3 + { + \@@_check_definable:nNT {#1} \DeclareDocumentCommand + { \@@_declare_cmd:Nnn #1 {#2} {#3} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\NewDocumentEnvironment} +% \begin{macro}{\RenewDocumentEnvironment} +% \begin{macro}{\ProvideDocumentEnvironment} +% \begin{macro}{\DeclareDocumentEnvironment} +% Very similar for environments. +% \begin{macrocode} +\cs_new_protected:Npn \NewDocumentEnvironment #1#2#3#4 + { + \cs_if_exist:cTF {#1} + { \__kernel_msg_error:nnx { xparse } { environment-already-defined } {#1} } + { \@@_declare_env:nnnn {#1} {#2} {#3} {#4} } + } +\cs_new_protected:Npn \RenewDocumentEnvironment #1#2#3#4 + { + \cs_if_exist:cTF {#1} + { \@@_declare_env:nnnn {#1} {#2} {#3} {#4} } + { \__kernel_msg_error:nnx { xparse } { environment-not-yet-defined } {#1} } + } +\cs_new_protected:Npn \ProvideDocumentEnvironment #1#2#3#4 + { \cs_if_exist:cF {#1} { \@@_declare_env:nnnn {#1} {#2} {#3} {#4} } } +\cs_new_protected:Npn \DeclareDocumentEnvironment #1#2#3#4 + { \@@_declare_env:nnnn {#1} {#2} {#3} {#4} } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\NewExpandableDocumentCommand} +% \begin{macro}{\RenewExpandableDocumentCommand} +% \begin{macro}{\ProvideExpandableDocumentCommand} +% \begin{macro}{\DeclareExpandableDocumentCommand} +% The expandable versions are essentially the same as the basic +% functions. The strange \cs{use:nnn} is there in case |#1| is +% surrounded with spaces, as can happen with usual document catcodes +% in \cs{RenewExpandableDocumentCommand} |{| |\!| |}| \ldots{} +% \begin{macrocode} +\cs_new_protected:Npn \NewExpandableDocumentCommand #1#2#3 + { + \@@_check_definable:nNT {#1} \NewExpandableDocumentCommand + { + \cs_if_exist:NTF #1 + { + \__kernel_msg_error:nnxx { xparse } { command-already-defined } + { \use:nnn \token_to_str:N #1 { } } + { \token_to_str:N \NewExpandableDocumentCommand } + } + { \@@_declare_expandable_cmd:Nnn #1 {#2} {#3} } + } + } +\cs_new_protected:Npn \RenewExpandableDocumentCommand #1#2#3 + { + \@@_check_definable:nNT {#1} \RenewExpandableDocumentCommand + { + \cs_if_exist:NTF #1 + { \@@_declare_expandable_cmd:Nnn #1 {#2} {#3} } + { + \__kernel_msg_error:nnxx { xparse } { command-not-yet-defined } + { \use:nnn \token_to_str:N #1 { } } + { \token_to_str:N \RenewExpandableDocumentCommand } + } + } + } +\cs_new_protected:Npn \ProvideExpandableDocumentCommand #1#2#3 + { + \@@_check_definable:nNT {#1} \ProvideExpandableDocumentCommand + { + \cs_if_exist:NF #1 + { \@@_declare_expandable_cmd:Nnn #1 {#2} {#3} } + } + } +\cs_new_protected:Npn \DeclareExpandableDocumentCommand #1#2#3 + { + \@@_check_definable:nNT {#1} \DeclareExpandableDocumentCommand + { \@@_declare_expandable_cmd:Nnn #1 {#2} {#3} } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\IfBooleanT, \IfBooleanF, \IfBooleanTF} +% The logical \meta{true} and \meta{false} statements are just the +% normal \cs{c_true_bool} and \cs{c_false_bool} so \cs{bool_if:NTF} is +% almost enough. However, this code-level function blows up badly +% when passed invalid input. We want \cs{IfBooleanTF} to accept a +% single (non-space) token equal to \cs{c_true_bool} or +% \cs{c_false_bool}, possibly surrounded by spaces. If the input is +% blank or multiple items, jump to the error and pick the false +% branch. If the input, ignoring spaces (we do this by omitting +% braces in the \cs{tl_if_single_token:nF} test), is not a single +% token then jump to the error as well. It is then safe to compare +% the token to the two booleans, picking the appropriate branch. If +% neither matches, we jump to the error as well. +% \begin{macrocode} +\cs_new:Npn \IfBooleanTF #1 + { + \tl_if_single:nF {#1} + { \prg_break:n { \use:n } } + \tl_if_single_token:nF #1 + { \prg_break:n { \use:n } } + \token_if_eq_meaning:NNT #1 \c_true_bool + { \prg_break:n { \use_ii:nnn } } + \token_if_eq_meaning:NNT #1 \c_false_bool + { \prg_break:n { \use_iii:nnn } } + \prg_break:n { \use:n } + \prg_break_point: + { + \__kernel_msg_expandable_error:nnn { xparse } { if-boolean } {#1} + \use_ii:nn + } + } +\cs_new:Npn \IfBooleanT #1#2 { \IfBooleanTF {#1} {#2} { } } +\cs_new:Npn \IfBooleanF #1 { \IfBooleanTF {#1} { } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\IfNoValueT, \IfNoValueF, \IfNoValueTF} +% Simple re-naming. +% \begin{macrocode} +\cs_new_eq:NN \IfNoValueF \tl_if_novalue:nF +\cs_new_eq:NN \IfNoValueT \tl_if_novalue:nT +\cs_new_eq:NN \IfNoValueTF \tl_if_novalue:nTF +% \end{macrocode} +% \end{macro} +% \begin{macro}{\IfValueT, \IfValueF, \IfValueTF} +% Inverted logic. +% \begin{macrocode} +\cs_new:Npn \IfValueF { \tl_if_novalue:nT } +\cs_new:Npn \IfValueT { \tl_if_novalue:nF } +\cs_new:Npn \IfValueTF #1#2#3 { \tl_if_novalue:nTF {#1} {#3} {#2} } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ProcessedArgument} +% Processed arguments are returned using this name, which is reserved +% here although the definition will change. +% \begin{macrocode} +\tl_new:N \ProcessedArgument +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ReverseBoolean, \SplitArgument, \SplitList, \TrimSpaces} +% Simple copies. +% \begin{macrocode} +\cs_new_eq:NN \ReverseBoolean \@@_bool_reverse:N +\cs_new_eq:NN \SplitArgument \@@_split_argument:nnn +\cs_new_eq:NN \SplitList \@@_split_list:nn +\cs_new_eq:NN \TrimSpaces \@@_trim_spaces:n +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\ProcessList} +% To support \cs{SplitList}. +% \begin{macrocode} +\cs_new_eq:NN \ProcessList \tl_map_function:nN +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\GetDocumentCommandArgSpec} +% \begin{macro}{\GetDocumentEnvironmentArgSpec} +% \begin{macro}{\ShowDocumentCommandArgSpec} +% \begin{macro}{\ShowDocumentEnvironmentArgSpec} +% More simple mappings, with a check that the argument is a single +% control sequence or active character. +% \begin{macrocode} +\cs_new_protected:Npn \GetDocumentCommandArgSpec #1 + { + \@@_check_definable:nNT {#1} \GetDocumentCommandArgSpec + { \@@_get_arg_spec:N #1 } + } +\cs_new_eq:NN \GetDocumentEnvironmentArgSpec \@@_get_arg_spec:n +\cs_new_protected:Npn \ShowDocumentCommandArgSpec #1 + { + \@@_check_definable:nNT {#1} \ShowDocumentCommandArgSpec + { \@@_show_arg_spec:N #1 } + } +\cs_new_eq:NN \ShowDocumentEnvironmentArgSpec \@@_show_arg_spec:n +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \subsection{Package options} +% +% \begin{variable}{\l_@@_options_clist} +% \begin{variable}{\l_@@_log_bool} +% Key--value option to log information: done by hand to keep dependencies +% down. +% \begin{macrocode} +\clist_new:N \l_@@_options_clist +\DeclareOption* { \clist_put_right:NV \l_@@_options_clist \CurrentOption } +\ProcessOptions \relax +\keys_define:nn { xparse } + { + log-declarations .bool_set:N = \l_@@_log_bool , + log-declarations .initial:n = false + } +\keys_set:nV { xparse } \l_@@_options_clist +\bool_if:NF \l_@@_log_bool + { + \msg_redirect_module:nnn { LaTeX / xparse } { info } { none } + \msg_redirect_module:nnn { LaTeX / xparse } { warning } { none } + } +% \end{macrocode} +% \end{variable} +% \end{variable} +% +% \begin{macrocode} +% +% \end{macrocode} +% +% \end{implementation} +% +% \PrintIndex diff --git a/macros/latex/contrib/l3packages/xparse.ins b/macros/latex/contrib/l3packages/xparse.ins new file mode 100644 index 0000000000..0fcc01351d --- /dev/null +++ b/macros/latex/contrib/l3packages/xparse.ins @@ -0,0 +1,47 @@ +\iffalse meta-comment + +File: xparse.ins + +Copyright (C) 2000-2012,2016,2017,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 + + http://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 released version of this bundle is available from CTAN. + +\fi + +\input l3docstrip.tex +\askforoverwritefalse + +\preamble + +Copyright (C) 2009-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: + + http://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. + +\endpreamble +% stop docstrip adding \endinput +\postamble +\endpostamble + +\keepsilent + +\generate{\file{xparse.sty} {\from{xparse.dtx} {package}}} + +\endbatchfile diff --git a/macros/latex/contrib/l3packages/xparse.pdf b/macros/latex/contrib/l3packages/xparse.pdf new file mode 100644 index 0000000000..4fb161e35e Binary files /dev/null and b/macros/latex/contrib/l3packages/xparse.pdf differ 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] +%\@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 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 } { } +% } +% \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 \\ + ~~''\\ + ~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 diff --git a/macros/latex/contrib/l3packages/xtemplate.ins b/macros/latex/contrib/l3packages/xtemplate.ins new file mode 100644 index 0000000000..dc7f7e5bea --- /dev/null +++ b/macros/latex/contrib/l3packages/xtemplate.ins @@ -0,0 +1,58 @@ +\iffalse meta-comment + +File: xtemplate.ins + +Copyright (C) 2000-2012,2016,2017,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 + + http://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. + +----------------------------------------------------------------------- + +Any modification of this file should ensure that the copyright and +license information is placed in the derived files. + +\fi + +\input l3docstrip.tex +\askforoverwritefalse + +\preamble + +Copyright (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: + + http://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. + +\endpreamble +% stop docstrip adding \endinput +\postamble +\endpostamble + +\keepsilent + +\generate{\file{xtemplate.sty} {\from{xtemplate.dtx} {package}}} + +\endbatchfile diff --git a/macros/latex/contrib/l3packages/xtemplate.pdf b/macros/latex/contrib/l3packages/xtemplate.pdf new file mode 100644 index 0000000000..0b716f7d70 Binary files /dev/null and b/macros/latex/contrib/l3packages/xtemplate.pdf differ -- cgit v1.2.3