diff options
author | Norbert Preining <norbert@preining.info> | 2024-02-15 03:01:30 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2024-02-15 03:01:30 +0000 |
commit | 5b6839a58ea5468b40ebd52b579e680b8e62ff13 (patch) | |
tree | 1dfc09f53794115129a07bf566b466cc337606f5 /macros/latex | |
parent | a90336c3069c198b41b7b6eb76e3389610ad5eb7 (diff) |
CTAN sync 202402150301
Diffstat (limited to 'macros/latex')
117 files changed, 3453 insertions, 4430 deletions
diff --git a/macros/latex/contrib/eolang/README.md b/macros/latex/contrib/eolang/README.md index 47fa929241..2214b74e38 100644 --- a/macros/latex/contrib/eolang/README.md +++ b/macros/latex/contrib/eolang/README.md @@ -22,7 +22,7 @@ x -> [ \end{document} ``` -Otherwise, you can download [`eolang.sty`](https://yegor256.github.io/eolang.sty) and add to your project. +Otherwise, you can download [`eolang.sty`](https://objectionary.github.io/eolang.sty/eolang.sty) and add to your project. If you want to contribute yourself, make a fork, then create a branch, then run `l3build ctan` in the root directory. diff --git a/macros/latex/contrib/eolang/eolang.1 b/macros/latex/contrib/eolang/eolang.1 index bcac9db30d..8ab1954d87 100644 --- a/macros/latex/contrib/eolang/eolang.1 +++ b/macros/latex/contrib/eolang/eolang.1 @@ -1,4 +1,4 @@ -.TH eolang 1 "2024-01-11" +.TH eolang 1 "2024-02-14" .SH NAME eolang \- Automated Editor of TeX Files that Use "eolang" Package .SH SYNOPSIS diff --git a/macros/latex/contrib/eolang/eolang.dtx b/macros/latex/contrib/eolang/eolang.dtx index 262c10f2ca..97d558ccaa 100644 --- a/macros/latex/contrib/eolang/eolang.dtx +++ b/macros/latex/contrib/eolang/eolang.dtx @@ -50,7 +50,7 @@ %<package>\NeedsTeXFormat{LaTeX2e} %<package>\ProvidesPackage{eolang} %<*package> -[2024-01-11 0.18.0 Formulas and Graphs for EO Programming Language] +[2024-02-14 0.18.1 Formulas and Graphs for EO Programming Language] %</package> %<*driver> \documentclass{ltxdoc} @@ -877,7 +877,7 @@ $macro = $ARGV[0]; open(my $fh, '<', $ARGV[1]); my $tex; { local $/; $tex = <$fh>; } -print "% This file is auto-generated by 0.18.0\n"; +print "% This file is auto-generated by 0.18.1\n"; print '% There are ', length($tex), ' chars in the input: ', $ARGV[1], "\n"; print '% ---', "\n"; @@ -1219,6 +1219,10 @@ sub fmt { $tex =~ s/\|([^\|]+)\|/\\textnormal{\\texttt{\1}}/g; return $tex; } +sub toem { + my ($cm) = @_; + return $cm * 2.8; +} sub vertex { my ($v) = @_; if (index($v, 'v0') == 0) { @@ -1315,7 +1319,7 @@ for (my $c = 0; $c < 0+@cmds; $c++) { $draw = $draw . " (<MF:${to}><B:break-v>)"; if (exists $opts{'break'}) { print tailor($draw, 'F') . ";\n"; - print ' \node[outer sep=.1cm,inner sep=0cm] ' . + print ' \node[outer sep=' . toem(0.1) . 'em,inner sep=0em] ' . 'at (break) (break-v) {$' . vertex($to) . '$};' . "\n"; print ' ' . tailor($draw, 'B'); @@ -1326,10 +1330,10 @@ for (my $c = 0; $c < 0+@cmds; $c++) { my ($from, $to) = split (/=+>/, $head); my $size = () = $head =~ /=/g; if ($from eq '') { - print '\node [phi-arrow, left=' . ($size * 0.6) . 'cm of ' . + print '\node [phi-arrow, left=' . toem($size * 0.6) . 'em of ' . $to . '.center]'; } elsif ($to eq '') { - print '\node [phi-arrow, right=' . ($size * 0.6) . 'cm of ' . + print '\node [phi-arrow, right=' . toem($size * 0.6) . 'em of ' . $from . '.center]'; } else { print '\node [phi-arrow] at ($(' . @@ -1340,7 +1344,7 @@ for (my $c = 0; $c < 0+@cmds; $c++) { my ($v, $marker) = split (/!+/, $head); my $size = () = $head =~ /!/g; print '\node [phi-marker, left=' . - ($size * 0.6) . 'cm of ' . + toem($size * 0.6) . 'em of ' . $v . '.center]{' . fmt($marker) . '}'; } elsif (index($head, '+') >= 0) { my ($v, $suffix) = split (/\+/, $head); @@ -1417,8 +1421,8 @@ for (my $c = 0; $c < 0+@cmds; $c++) { $loc = $loc . 'left'; } print ',' . $loc . '='; - print abs(num($down)) . 'cm and ' . - abs(num($right)) . 'cm of ' . $v . '.center'; + print toem(abs(num($down))) . 'em and ' . + toem(abs(num($right))) . 'em of ' . $v . '.center'; } if (exists $opts{'data'}) { print ',phi-data'; @@ -1521,17 +1525,17 @@ print '\endinput'; % \begin{macrocode} \newenvironment{phicture}% {\noindent\begin{tikzpicture}[ - ->,>=stealth',node distance=0,thick, + ->,>=stealth',node distance=0,line width=.08em, pics/parallel arrow/.style={ code={\draw[-latex,phi-rho] (##1) -- (-##1);}}]}% {\end{tikzpicture}} \tikzstyle{phi-arrow} = [fill=white!80!black, single arrow, - minimum height=0.5cm, minimum width=0.5cm, + minimum height=0.05em, minimum width=0.05em, single arrow head extend=2mm] \tikzstyle{phi-marker} = [inner sep=0pt, minimum height=1.4em, minimum width=1.4em, font={\small\color{white}\ttfamily}, fill=gray] -\tikzstyle{phi-thing} = [thick,inner sep=0pt,minimum height=2.4em, +\tikzstyle{phi-thing} = [inner sep=0pt,minimum height=2.4em, draw,font={\small}] \tikzstyle{phi-object} = [phi-thing,circle] \tikzstyle{phi-data} = [phi-thing,regular polygon, @@ -1593,7 +1597,7 @@ print '\endinput'; \tikzstyle{phi-pi} = [draw,dotted] \tikzstyle{phi-atom} = [phi-object,double] \tikzstyle{phi-box} = [xshift=-5pt,yshift=3pt,draw,fill=white, - rectangle,thin,minimum width=1.2em,anchor=north west, + rectangle,line width=.04em,minimum width=1.2em,anchor=north west, font={\scriptsize}] \tikzstyle{phi-attr} = [midway,sloped,inner sep=0pt, above=2pt,sloped/.append style={transform shape}, diff --git a/macros/latex/contrib/eolang/eolang.pdf b/macros/latex/contrib/eolang/eolang.pdf Binary files differindex 23028cf96a..2bf8e39de4 100644 --- a/macros/latex/contrib/eolang/eolang.pdf +++ b/macros/latex/contrib/eolang/eolang.pdf diff --git a/macros/latex/contrib/eolang/eolang.pl b/macros/latex/contrib/eolang/eolang.pl index 3ac32331a4..48ba53dc5b 100755 --- a/macros/latex/contrib/eolang/eolang.pl +++ b/macros/latex/contrib/eolang/eolang.pl @@ -21,7 +21,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -# 2024-01-11 0.18.0 +# 2024-02-14 0.18.1 package eolang; use warnings; @@ -45,6 +45,8 @@ sub savefile { open(my $f, '>', $path) or error('Cannot open file for writing: ' . $path); print $f $content; close($f); + my $size = -s $path; + info("File saved to '$path' ($size bytes)"); } # Print INFO message to the console. @@ -78,7 +80,7 @@ if (@ARGV+0 eq 0 or exists $args{'--help'} or exists $args{'-?'}) { " --tmpdir=path Temp directory with .tex files ('_eolang' by default)\n\n" . "If any issues, report to GitHub: https://github.com/yegor256/bibcop"); } elsif (exists $args{'--version'} or exists $args{'-v'}) { - info('0.18.0 2024-01-11'); + info('0.18.1 2024-02-14'); } else { my ($src, $target) = grep { not($_ =~ /^-.*$/) } @ARGV; if (not $src) { @@ -146,7 +148,6 @@ if (@ARGV+0 eq 0 or exists $args{'--help'} or exists $args{'-?'}) { } debug('Target: ' . $target); savefile($target, $tex); - info("New TeX file save to: ". $target); } # In order to finish it with success: diff --git a/macros/latex/contrib/huawei/LICENSE.txt b/macros/latex/contrib/huawei/LICENSE.txt index 5744f745f0..1f211e0287 100644 --- a/macros/latex/contrib/huawei/LICENSE.txt +++ b/macros/latex/contrib/huawei/LICENSE.txt @@ -1,6 +1,6 @@ (The MIT License) -Copyright (c) 2021-2023 Yegor Bugayenko +Copyright (c) 2021-2024 Yegor Bugayenko Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal diff --git a/macros/latex/contrib/huawei/README.md b/macros/latex/contrib/huawei/README.md index b7a0640cf2..e2f585d214 100644 --- a/macros/latex/contrib/huawei/README.md +++ b/macros/latex/contrib/huawei/README.md @@ -21,6 +21,8 @@ Hello, world! \end{document} ``` +Otherwise, you can download [`huawei.cls`](https://yegor256.github.io/huawei.cls/huawei.sty) and add to your project. + Read the detailed documentation [in PDF](http://mirrors.ctan.org/macros/latex/contrib/huawei/huawei.pdf). If you need more formatting options, diff --git a/macros/latex/contrib/huawei/huawei.dtx b/macros/latex/contrib/huawei/huawei.dtx index 88af315984..1140958502 100644 --- a/macros/latex/contrib/huawei/huawei.dtx +++ b/macros/latex/contrib/huawei/huawei.dtx @@ -1,7 +1,7 @@ % \iffalse meta-comment % (The MIT License) % -% Copyright (c) 2021-2023 Yegor Bugayenko +% Copyright (c) 2021-2024 Yegor Bugayenko % % Permission is hereby granted, free of charge, to any person obtaining a copy % of this software and associated documentation files (the 'Software'), to deal @@ -50,7 +50,7 @@ %<class>\NeedsTeXFormat{LaTeX2e} %<class>\ProvidesClass{huawei} %<*class> -[2023-09-21 0.18.2 Template for Huawei Documents] +[2024-02-14 0.18.3 Template for Huawei Documents] %</class> %<*driver> \documentclass{ltxdoc} @@ -782,7 +782,7 @@ xscale=#1, inner sep=0pt, outer sep=0pt] % \begin{macrocode} \makeatletter\renewcommand\maketitle{% \vspace*{18pt}% - {\bfseries{\Huge\raggedright\thetitle\par}}% + {\bfseries{\begin{spacing}{.9}\Huge\raggedright\thetitle\par\end{spacing}}}% \ifx\thesubtitle\empty\else% {\color{gray!50!black}\large\raggedright\thesubtitle\par}% \fi% @@ -997,7 +997,7 @@ xscale=#1, inner sep=0pt, outer sep=0pt] \setlength\parindent{0pt}% \setlength\parskip{6pt}% \setlength\columnsep{32pt}% -\def\arraystretch{1.5}% +\renewcommand{\arraystretch}{1}% } % \end{macrocode} diff --git a/macros/latex/contrib/huawei/huawei.ins b/macros/latex/contrib/huawei/huawei.ins index daa75a374e..784d524942 100644 --- a/macros/latex/contrib/huawei/huawei.ins +++ b/macros/latex/contrib/huawei/huawei.ins @@ -1,6 +1,6 @@ %% (The MIT License) %% -%% Copyright (c) 2021-2023 Yegor Bugayenko +%% Copyright (c) 2021-2024 Yegor Bugayenko %% %% Permission is hereby granted, free of charge, to any person obtaining a copy %% of this software and associated documentation files (the 'Software'), to deal @@ -26,7 +26,7 @@ \preamble (The MIT License) -Copyright (c) 2021-2023 Yegor Bugayenko +Copyright (c) 2021-2024 Yegor Bugayenko Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal diff --git a/macros/latex/contrib/huawei/huawei.pdf b/macros/latex/contrib/huawei/huawei.pdf Binary files differindex 827b703b74..c0c742fa02 100644 --- a/macros/latex/contrib/huawei/huawei.pdf +++ b/macros/latex/contrib/huawei/huawei.pdf diff --git a/macros/latex/contrib/jnuexam/jnuexam.cls b/macros/latex/contrib/jnuexam/jnuexam.cls index c70cee0fd9..6fa3b722ca 100644 --- a/macros/latex/contrib/jnuexam/jnuexam.cls +++ b/macros/latex/contrib/jnuexam/jnuexam.cls @@ -6,7 +6,7 @@ % ---------------------------------------------------------------------------- \NeedsTeXFormat{LaTeX2e} -\ProvidesClass{jnuexam}[2023/12/18 v2.1 An exam class for Jinan University] +\ProvidesClass{jnuexam}[2024/02/02 v2.1.1 An exam class for Jinan University] %% 旧版本的 LaTeX 不能识别 2022-11-01 这种日期格式 %\@ifl@t@r\fmtversion{2022-11-01}{}{ @@ -56,7 +56,7 @@ % 在 xeCJK 中已经禁止载入 CJK,但是在新版本 ctex 宏包中却失效了 % 我们假装 CJK 已经载入,再载入 CJKnumb,避免出现 \CJKglue 重复定义的问题 % 注意用 PDFLaTeX 编译时需要用到 CJK,所以只在未定义时才作修改 -\ifdefined\else +\ifdefined\CJKglue \@namedef{ver@CJK.sty}{} %\@namedef{opt@CJK.sty}{} \fi @@ -926,7 +926,7 @@ \newcommand*{\my@alignment@check}[1]{% \ifx\my@let@token #1% \def\my@alignment@offset{5}% - \listbreak + \expandafter\listbreak \fi } diff --git a/macros/latex/contrib/l3kernel/CHANGELOG.md b/macros/latex/contrib/l3kernel/CHANGELOG.md index a8e4cb8d77..472c43a655 100644 --- a/macros/latex/contrib/l3kernel/CHANGELOG.md +++ b/macros/latex/contrib/l3kernel/CHANGELOG.md @@ -7,6 +7,26 @@ this project uses date-based 'snapshot' version identifiers. ## [Unreleased] +## [2024-02-13] + +### Added +- Checking missing `\endgroup` at the end of `\DocInclude` +- Linked storage type for large property lists (issue \#1040, pull \#1059) + +### Changed +- `\meta` now typesets in `\texttt`, along with `\Arg` +- Improved the performance of `\cs_if_exist:NTF`, `\cs_if_free:NTF`, + `\cs_if_exist_use:NTF`, and most notably their `c`-type variants. + +### Fixed +- Inconsistent local/global assignments in `\vcoffin_gset:Nnn` and + `\vcoffin_gset:Nnw` +- Tokenization by `\peek_analysis_map_inline:n` of one additional + character after any space or brace + +### Deprecated +- `\msg_gset:nnn(n)` + ## [2024-01-22] ### Added @@ -1648,7 +1668,8 @@ this project uses date-based 'snapshot' version identifiers. - Step functions have been added for dim variables, e.g. `\dim_step_inline:nnnn` -[Unreleased]: https://github.com/latex3/latex3/compare/2024-01-22...HEAD +[Unreleased]: https://github.com/latex3/latex3/compare/2024-02-13...HEAD +[2024-02-13]: https://github.com/latex3/latex3/compare/2024-01-22...2024-02-13 [2024-01-22]: https://github.com/latex3/latex3/compare/2024-01-04...2024-01-22 [2024-01-04]: https://github.com/latex3/latex3/compare/2023-12-11...2024-01-04 [2023-12-11]: https://github.com/latex3/latex3/compare/2023-12-08...2023-12-11 diff --git a/macros/latex/contrib/l3kernel/README.md b/macros/latex/contrib/l3kernel/README.md index 96b7e958f9..ee146d0405 100644 --- a/macros/latex/contrib/l3kernel/README.md +++ b/macros/latex/contrib/l3kernel/README.md @@ -1,7 +1,7 @@ LaTeX3 Programming Conventions ============================== -Release 2024-01-22 +Release 2024-02-13 Overview -------- diff --git a/macros/latex/contrib/l3kernel/expl3.dtx b/macros/latex/contrib/l3kernel/expl3.dtx index 8fbb777dcb..a2f820f71f 100644 --- a/macros/latex/contrib/l3kernel/expl3.dtx +++ b/macros/latex/contrib/l3kernel/expl3.dtx @@ -24,7 +24,7 @@ % %<*driver|generic|package|2ekernel> %</driver|generic|package|2ekernel> -\def\ExplFileDate{2024-01-22}% +\def\ExplFileDate{2024-02-13}% %<*driver> \documentclass[full]{l3doc} \usepackage{graphicx} @@ -51,7 +51,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/expl3.pdf b/macros/latex/contrib/l3kernel/expl3.pdf Binary files differindex 8f722bc6b4..03b7a491c1 100644 --- a/macros/latex/contrib/l3kernel/expl3.pdf +++ b/macros/latex/contrib/l3kernel/expl3.pdf diff --git a/macros/latex/contrib/l3kernel/interface3.pdf b/macros/latex/contrib/l3kernel/interface3.pdf Binary files differindex 7b5ffdc3e9..dcbc798249 100644 --- a/macros/latex/contrib/l3kernel/interface3.pdf +++ b/macros/latex/contrib/l3kernel/interface3.pdf diff --git a/macros/latex/contrib/l3kernel/interface3.tex b/macros/latex/contrib/l3kernel/interface3.tex index 06b8b3d8a6..c4c45a6bca 100644 --- a/macros/latex/contrib/l3kernel/interface3.tex +++ b/macros/latex/contrib/l3kernel/interface3.tex @@ -42,12 +42,6 @@ for those people who are interested. \documentclass[kernel]{l3doc} -% fix for l3doc class -\ExplSyntaxOn -\DeclareDocumentCommand \meta { m } - { \texttt{ \__codedoc_meta:n {#1} } } -\ExplSyntaxOff - \newif\ifinterface \interfacetrue diff --git a/macros/latex/contrib/l3kernel/l3basics.dtx b/macros/latex/contrib/l3kernel/l3basics.dtx index 0be8bdc8b2..bc79956d05 100644 --- a/macros/latex/contrib/l3kernel/l3basics.dtx +++ b/macros/latex/contrib/l3kernel/l3basics.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % @@ -92,6 +92,9 @@ % to occur within the same function. Indeed, it is often necessary % to start a group within one function and finish it within another, % for example when seeking to use non-standard category codes. +% \begin{texnote} +% These are the \TeX{} primitives \tn{begingroup} and \tn{endgroup}. +% \end{texnote} % \end{function} % % \begin{function}{\group_insert_after:N} @@ -1248,11 +1251,13 @@ % % \begin{function}[EXP]{\if:w, \if_charcode:w, \if_catcode:w} % \begin{syntax} -% "\if:w" <token_1> <token_2> <true code> "\else:" <false code> "\fi:" \\ -% "\if_catcode:w" <token_1> <token_2> <true code> "\else:" <false code> "\fi:" +% "\if:w" <token(s)> <true code> "\else:" <false code> "\fi:" \\ +% "\if_catcode:w" <token(s_> <true code> "\else:" <false code> "\fi:" % \end{syntax} -% These conditionals expand any following tokens until two -% unexpandable tokens are left. If you wish to prevent this expansion, +% These conditionals expand \meta{token(s)} until two +% unexpandable tokens \meta{token_1} and \meta{tokens_2} are left; +% any further tokens become part of the \meta{true code}. +% If you wish to prevent this expansion, % prefix the token in question with "\exp_not:N". "\if_catcode:w" % tests if the category codes of the two tokens are the same whereas % "\if:w" tests if the character codes are @@ -2451,22 +2456,28 @@ % (to be defined) if it does not already exist. % % \begin{macro}[pTF, EXP]{\cs_if_exist:N, \cs_if_exist:c} +% \begin{macro}{\@@_if_exist_c_aux:,\@@_if_exist_c_aux:w} % Two versions for checking existence. For the |N| form we firstly % check for \cs{scan_stop:} and then if it is in the hash % table. There is no problem when inputting something like \cs{else:} % or \cs{fi:} as \TeX{} will only ever skip input in case the token % tested against is \cs{scan_stop:}. +% +% In both the |N| and |c| form we use the way \cs{prg_set_conditional:Npnn} +% optimizes the conditionals to negate the tests using \cs{else:} (the +% \cs{else:} in the top level functions will be removed by the optimization, +% and this usage of \cs{else:} will be fine). % \begin{macrocode} \prg_set_conditional:Npnn \cs_if_exist:N #1 { p , T , F , TF } { \if_meaning:w #1 \scan_stop: - \prg_return_false: + \use_i:nnnn + \else: + \fi: + \if_cs_exist:N #1 + \prg_return_true: \else: - \if_cs_exist:N #1 - \prg_return_true: - \else: - \prg_return_false: - \fi: + \prg_return_false: \fi: } % \end{macrocode} @@ -2477,87 +2488,148 @@ % disturb the scanner. Therefore, we ensure that the second test is % performed after the first one has concluded completely. % \begin{macrocode} -\prg_set_conditional:Npnn \cs_if_exist:c #1 { p , T , F , TF } +\cs_if_exist:NTF \tex_lastnamedcs:D { - \if_cs_exist:w #1 \cs_end: - \exp_after:wN \use_i:nn - \else: - \exp_after:wN \use_ii:nn - \fi: - { - \exp_after:wN \if_meaning:w \cs:w #1 \cs_end: \scan_stop: - \prg_return_false: - \else: - \prg_return_true: - \fi: - } - \prg_return_false: + \prg_set_conditional:Npnn \cs_if_exist:c #1 { p , T , F , TF } + { + \if_cs_exist:w #1 \cs_end: + \@@_if_exist_c_aux: + \prg_return_true: + \else: + \prg_return_false: + \fi: + } + \cs_set:Npn \@@_if_exist_c_aux: + { \fi: \exp_after:wN \if_meaning:w \tex_lastnamedcs:D \scan_stop: \else: } + } + { + \prg_set_conditional:Npnn \cs_if_exist:c #1 { p , T , F , TF } + { + \if_cs_exist:w #1 \cs_end: + \@@_if_exist_c_aux:w + \fi: + \use_none:n {#1} + \if_false: + \prg_return_true: + \else: + \prg_return_false: + \fi: + } + \cs_set:Npn \@@_if_exist_c_aux:w \fi: \use_none:n #1 \if_false: + { \fi: \exp_after:wN \if_meaning:w \cs:w #1 \cs_end: \scan_stop: \else: } } % \end{macrocode} % \end{macro} +% \end{macro} % % \begin{macro}[pTF, EXP]{\cs_if_free:N, \cs_if_free:c} % The logical reversal of the above. % \begin{macrocode} \prg_set_conditional:Npnn \cs_if_free:N #1 { p , T , F , TF } { + \if_cs_exist:N #1 + \else: + \use_none:nnnn + \fi: \if_meaning:w #1 \scan_stop: \prg_return_true: \else: - \if_cs_exist:N #1 - \prg_return_false: - \else: - \prg_return_true: - \fi: + \prg_return_false: \fi: } -\prg_set_conditional:Npnn \cs_if_free:c #1 { p , T , F , TF } +\cs_if_exist:NTF \tex_lastnamedcs:D { - \if_cs_exist:w #1 \cs_end: - \exp_after:wN \use_i:nn - \else: - \exp_after:wN \use_ii:nn - \fi: + \prg_set_conditional:Npnn \cs_if_free:c #1 { p , T , F , TF } + { + \if_cs_exist:w #1 \cs_end: + \@@_if_free_c_aux:w + \fi: + \if_true: + \prg_return_true: + \else: + \prg_return_false: + \fi: + } + \cs_set:Npn \@@_if_free_c_aux:w \fi: \if_true: + { \fi: \exp_after:wN \if_meaning:w \tex_lastnamedcs:D \scan_stop: } + } + { + \prg_set_conditional:Npnn \cs_if_free:c #1 { p , T , F , TF } { - \exp_after:wN \if_meaning:w \cs:w #1 \cs_end: \scan_stop: + \if_cs_exist:w #1 \cs_end: + \@@_if_free_c_aux:w + \fi: + \use_none:n {#1} + \if_true: \prg_return_true: \else: \prg_return_false: \fi: } - { \prg_return_true: } + \cs_set:Npn \@@_if_free_c_aux:w \fi: \use_none:n #1 \if_true: + { \fi: \exp_after:wN \if_meaning:w \cs:w #1 \cs_end: \scan_stop: } } % \end{macrocode} % \end{macro} % % \begin{macro}[EXP,noTF,added=2011-10-10] % {\cs_if_exist_use:N, \cs_if_exist_use:c} +% \begin{macro}{\@@_if_exist_use_aux:w, \@@_if_exist_use_aux:Nnn} % The \cs[index=cs_if_exist_use:N]{cs_if_exist_use:\ldots{}} % functions cannot be implemented % as conditionals because the true branch must leave both the control % sequence itself and the true code in the input stream. % For the \texttt{c} variants, we are careful not to put the control % sequence in the hash table if it does not exist. -% In \LuaTeX{} we could use the \tn{lastnamedcs} primitive. +% If available we use the \tn{lastnamedcs} primitive. % \begin{macrocode} \cs_set:Npn \cs_if_exist_use:NTF #1#2 { \cs_if_exist:NTF #1 { #1 #2 } } \cs_set:Npn \cs_if_exist_use:NF #1 - { \cs_if_exist:NTF #1 { #1 } } + { \cs_if_exist:NTF #1 #1 } \cs_set:Npn \cs_if_exist_use:NT #1 #2 - { \cs_if_exist:NTF #1 { #1 #2 } { } } + { \cs_if_exist:NT #1 { #1 #2 } } \cs_set:Npn \cs_if_exist_use:N #1 - { \cs_if_exist:NTF #1 { #1 } { } } -\cs_set:Npn \cs_if_exist_use:cTF #1#2 - { \cs_if_exist:cTF {#1} { \use:c {#1} #2 } } + { \cs_if_exist:NT #1 #1 } +\cs_if_exist:NTF \tex_lastnamedcs:D + { + \cs_set:Npn \cs_if_exist_use:cTF #1 + { + \if_cs_exist:w #1 \cs_end: + \@@_if_exist_use_aux:w + \fi: + \use_ii:nn + } + \cs_set:Npn \@@_if_exist_use_aux:w \fi: \use_ii:nn + { \fi: \exp_after:wN \@@_if_exist_use_aux:Nnn \tex_lastnamedcs:D } + } + { + \cs_set:Npn \cs_if_exist_use:cTF #1 + { + \if_cs_exist:w #1 \cs_end: + \@@_if_exist_use_aux:w + \fi: + \use_iii:nnn {#1} + } + \cs_set:Npn \@@_if_exist_use_aux:w \fi: \use_iii:nnn #1 + { \fi: \exp_after:wN \@@_if_exist_use_aux:Nnn \cs:w #1 \cs_end: } + } +\cs_set:Npn \@@_if_exist_use_aux:Nnn #1#2 + { + \if_meaning:w #1 \scan_stop: + \exp_after:wN \use_iii:nnn + \fi: + \use_i:nn { #1 #2 } + } \cs_set:Npn \cs_if_exist_use:cF #1 - { \cs_if_exist:cTF {#1} { \use:c {#1} } } + { \cs_if_exist_use:cTF {#1} {} } \cs_set:Npn \cs_if_exist_use:cT #1#2 - { \cs_if_exist:cTF {#1} { \use:c {#1} #2 } { } } + { \cs_if_exist_use:cTF {#1} {#2} {} } \cs_set:Npn \cs_if_exist_use:c #1 - { \cs_if_exist:cTF {#1} { \use:c {#1} } { } } + { \cs_if_exist_use:cTF {#1} {} {} } % \end{macrocode} % \end{macro} +% \end{macro} % % \subsection{Preliminaries for new functions} % @@ -3270,7 +3342,7 @@ \cs_new_protected:Npn \group_show_list: { \@@_group_show:NN \use_none:n 1 } \cs_new_protected:Npn \group_log_list: - { \@@_group_show:NN \int_zero:N 0 } + { \@@_group_show:NN \int_gzero:N 0 } \cs_new_protected:Npn \@@_group_show:NN #1#2 { \use:e @@ -3280,7 +3352,7 @@ \int_set:Nn \tex_errorcontextlines:D { -1 } \exp_not:N \exp_after:wN \scan_stop: \tex_showgroups:D - \int_set:Nn \tex_interactionmode:D + \int_gset:Nn \tex_interactionmode:D { \int_use:N \tex_interactionmode:D } \int_set:Nn \tex_tracingonline:D { \int_use:N \tex_tracingonline:D } diff --git a/macros/latex/contrib/l3kernel/l3bitset.dtx b/macros/latex/contrib/l3kernel/l3bitset.dtx index 498e9954cc..e87a9c9e0f 100644 --- a/macros/latex/contrib/l3kernel/l3bitset.dtx +++ b/macros/latex/contrib/l3kernel/l3bitset.dtx @@ -42,7 +42,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3bootstrap.dtx b/macros/latex/contrib/l3kernel/l3bootstrap.dtx index c1fb85f8c0..b6a747f833 100644 --- a/macros/latex/contrib/l3kernel/l3bootstrap.dtx +++ b/macros/latex/contrib/l3kernel/l3bootstrap.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3box.dtx b/macros/latex/contrib/l3kernel/l3box.dtx index ae3bc283c2..5cb80d888d 100644 --- a/macros/latex/contrib/l3kernel/l3box.dtx +++ b/macros/latex/contrib/l3kernel/l3box.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % @@ -1343,9 +1343,9 @@ { \exp_args:No \@@_log:nNnn { \tex_the:D \tex_interactionmode:D } } \cs_new_protected:Npn \@@_log:nNnn #1#2#3#4 { - \int_set:Nn \tex_interactionmode:D { 0 } + \int_gset:Nn \tex_interactionmode:D { 0 } \@@_show:NNff 0 #2 { \int_eval:n {#3} } { \int_eval:n {#4} } - \int_set:Nn \tex_interactionmode:D {#1} + \int_gset:Nn \tex_interactionmode:D {#1} } \cs_generate_variant:Nn \box_log:Nnn { c } % \end{macrocode} @@ -2279,7 +2279,7 @@ \cs_generate_variant:Nn \box_autosize_to_wd_and_ht:Nnn { c } \cs_new_protected:Npn \box_gautosize_to_wd_and_ht:Nnn #1#2#3 { \@@_autosize:NnnnN #1 {#2} {#3} { \box_ht:N #1 } \hbox_gset:Nn } -\cs_generate_variant:Nn \box_autosize_to_wd_and_ht:Nnn { c } +\cs_generate_variant:Nn \box_gautosize_to_wd_and_ht:Nnn { c } \cs_new_protected:Npn \box_autosize_to_wd_and_ht_plus_dp:Nnn #1#2#3 { \@@_autosize:NnnnN #1 {#2} {#3} { \box_ht:N #1 + \box_dp:N #1 } diff --git a/macros/latex/contrib/l3kernel/l3cctab.dtx b/macros/latex/contrib/l3kernel/l3cctab.dtx index e3285d9bd9..a11b0e31a7 100644 --- a/macros/latex/contrib/l3kernel/l3cctab.dtx +++ b/macros/latex/contrib/l3kernel/l3cctab.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3clist.dtx b/macros/latex/contrib/l3kernel/l3clist.dtx index a8e24c2a30..54b558f7ea 100644 --- a/macros/latex/contrib/l3kernel/l3clist.dtx +++ b/macros/latex/contrib/l3kernel/l3clist.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3coffins.dtx b/macros/latex/contrib/l3kernel/l3coffins.dtx index c6882fe5b3..c8a71eb252 100644 --- a/macros/latex/contrib/l3kernel/l3coffins.dtx +++ b/macros/latex/contrib/l3kernel/l3coffins.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % @@ -128,7 +128,7 @@ % environment in a coffin. % \end{function} % -% \begin{function}[added = 2011-08-17, updated = 2019-01-21] +% \begin{function}[added = 2011-08-17, updated = 2023-02-03] % { % \vcoffin_set:Nnn, \vcoffin_set:cnn, % \vcoffin_gset:Nnn, \vcoffin_gset:cnn @@ -142,7 +142,7 @@ % size of the typeset material. % \end{function} % -% \begin{function}[added = 2011-09-10, updated = 2019-01-21] +% \begin{function}[added = 2011-09-10, updated = 2023-02-03] % { % \vcoffin_set:Nnw, \vcoffin_set:cnw, \vcoffin_set_end:, % \vcoffin_gset:Nnw, \vcoffin_gset:cnw, \vcoffin_gset_end: @@ -702,7 +702,7 @@ % \vcoffin_set:Nnn, \vcoffin_set:cnn, % \vcoffin_gset:Nnn, \vcoffin_gset:cnn % } -% \begin{macro}{\@@_set_vertical:NnnNN} +% \begin{macro}{\@@_set_vertical:NnnNNN} % \begin{macro}{\@@_set_vertical_aux:} % Setting vertical coffins is more complex. First, the material is % typeset with a given width. The default handles and poles are set as @@ -714,17 +714,17 @@ % \begin{macrocode} \cs_new_protected:Npn \vcoffin_set:Nnn #1#2#3 { - \@@_set_vertical:NnnNN #1 {#2} {#3} - \vbox_set:Nn \coffin_reset_poles:N + \@@_set_vertical:NnnNNN #1 {#2} {#3} + \vbox_set:Nn \coffin_reset_poles:N \@@_set_pole:Nnn } \cs_generate_variant:Nn \vcoffin_set:Nnn { c } \cs_new_protected:Npn \vcoffin_gset:Nnn #1#2#3 { - \@@_set_vertical:NnnNN #1 {#2} {#3} - \vbox_gset:Nn \coffin_greset_poles:N + \@@_set_vertical:NnnNNN #1 {#2} {#3} + \vbox_gset:Nn \coffin_greset_poles:N \@@_gset_pole:Nnn } \cs_generate_variant:Nn \vcoffin_gset:Nnn { c } -\cs_new_protected:Npn \@@_set_vertical:NnnNN #1#2#3#4#5 +\cs_new_protected:Npn \@@_set_vertical:NnnNNN #1#2#3#4#5#6 { \@@_if_exist:NT #1 { @@ -736,7 +736,7 @@ } #5 #1 \vbox_set_top:Nn \l_@@_internal_box { \vbox_unpack:N #1 } - \@@_set_pole:Nne #1 { T } + #6 #1 { T } { { 0pt } { @@ -804,25 +804,25 @@ % % \begin{macro} % {\vcoffin_set:Nnw, \vcoffin_set:cnw, \vcoffin_gset:Nnw, \vcoffin_gset:cnw} -% \begin{macro}{\@@_set_vertical:NnNNNNw} +% \begin{macro}{\@@_set_vertical:NnNNNNNw} % \begin{macro}{\vcoffin_set_end:, \vcoffin_gset_end:} % The same for vertical coffins. % \begin{macrocode} \cs_new_protected:Npn \vcoffin_set:Nnw #1#2 { - \@@_set_vertical:NnNNNNw #1 {#2} \vbox_set:Nw + \@@_set_vertical:NnNNNNNw #1 {#2} \vbox_set:Nw \vcoffin_set_end: - \vbox_set_end: \coffin_reset_poles:N + \vbox_set_end: \coffin_reset_poles:N \@@_set_pole:Nnn } \cs_generate_variant:Nn \vcoffin_set:Nnw { c } \cs_new_protected:Npn \vcoffin_gset:Nnw #1#2 { - \@@_set_vertical:NnNNNNw #1 {#2} \vbox_gset:Nw + \@@_set_vertical:NnNNNNNw #1 {#2} \vbox_gset:Nw \vcoffin_gset_end: - \vbox_gset_end: \coffin_greset_poles:N + \vbox_gset_end: \coffin_greset_poles:N \@@_gset_pole:Nnn } \cs_generate_variant:Nn \vcoffin_gset:Nnw { c } -\cs_new_protected:Npn \@@_set_vertical:NnNNNNw #1#2#3#4#5#6 +\cs_new_protected:Npn \@@_set_vertical:NnNNNNNw #1#2#3#4#5#6#7 { \@@_if_exist:NT #1 { @@ -834,7 +834,7 @@ #5 #6 #1 \vbox_set_top:Nn \l_@@_internal_box { \vbox_unpack:N #1 } - \@@_set_pole:Nne #1 { T } + #7 #1 { T } { { 0pt } { @@ -989,7 +989,7 @@ % \coffin_gset_vertical_pole:Nnn, \coffin_gset_vertical_pole:cnn % } % \begin{macro}{\@@_set_vertical_pole:NnnN} -% \begin{macro}{\@@_set_pole:Nnn, \@@_set_pole:Nne} +% \begin{macro}{\@@_set_pole:Nnn, \@@_gset_pole:Nnn} % Setting the pole of a coffin at the user/designer level requires a % bit more care. The idea here is to provide a reasonable interface to % the system, then to do the setting with full expansion. The @@ -1033,10 +1033,14 @@ } \cs_new_protected:Npn \@@_set_pole:Nnn #1#2#3 { - \prop_put:cnn { coffin ~ \@@_to_value:N #1 ~ poles } + \prop_put:cne { coffin ~ \@@_to_value:N #1 ~ poles } + {#2} {#3} + } +\cs_new_protected:Npn \@@_gset_pole:Nnn #1#2#3 + { + \prop_gput:cne { coffin ~ \@@_to_value:N #1 ~ poles } {#2} {#3} } -\cs_generate_variant:Nn \@@_set_pole:Nnn { Nne } % \end{macrocode} % \end{macro} % \end{macro} @@ -2068,7 +2072,7 @@ \tl_if_in:nnTF {#2} { - } { \tl_set:Nn \l_@@_internal_tl { {#2} } } { \tl_set:Nn \l_@@_internal_tl { { #1 - #2 } } } - \exp_last_unbraced:NNo \@@_set_pole:Nne \l_@@_aligned_coffin + \exp_last_unbraced:NNo \@@_set_pole:Nnn \l_@@_aligned_coffin { \l_@@_internal_tl } { { \dim_use:N \l_@@_x_dim } { \dim_use:N \l_@@_y_dim } @@ -2130,11 +2134,11 @@ { \dim_compare:nNnTF {#2} < {#6} { - \@@_set_pole:Nne #9 { T } + \@@_set_pole:Nnn #9 { T } { { 0pt } {#6} { 1000pt } { 0pt } } } { - \@@_set_pole:Nne #9 { T } + \@@_set_pole:Nnn #9 { T } { { 0pt } {#2} { 1000pt } { 0pt } } } } @@ -2142,11 +2146,11 @@ { \dim_compare:nNnTF {#2} < {#6} { - \@@_set_pole:Nne #9 { B } + \@@_set_pole:Nnn #9 { B } { { 0pt } {#2} { 1000pt } { 0pt } } } { - \@@_set_pole:Nne #9 { B } + \@@_set_pole:Nnn #9 { B } { { 0pt } {#6} { 1000pt } { 0pt } } } } diff --git a/macros/latex/contrib/l3kernel/l3color.dtx b/macros/latex/contrib/l3kernel/l3color.dtx index 950bf3d6be..6ecdf216c4 100644 --- a/macros/latex/contrib/l3kernel/l3color.dtx +++ b/macros/latex/contrib/l3kernel/l3color.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3debug.dtx b/macros/latex/contrib/l3kernel/l3debug.dtx index e8f577a4bb..f345c6d303 100644 --- a/macros/latex/contrib/l3kernel/l3debug.dtx +++ b/macros/latex/contrib/l3kernel/l3debug.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % @@ -55,71 +55,9 @@ % % \section{\pkg{l3debug} implementation} % -% \begin{function}{\__kernel_chk_var_local:N, \__kernel_chk_var_global:N} -% \begin{syntax} -% \cs{__kernel_chk_var_local:N} \meta{var} -% \cs{__kernel_chk_var_global:N} \meta{var} -% \end{syntax} -% Applies \cs{__kernel_chk_var_exist:N} \meta{var} as well as -% \cs{__kernel_chk_var_scope:NN} \meta{scope} \meta{var}, where -% \meta{scope} is |l| or~|g|. -% \end{function} -% -% \begin{function}{\__kernel_chk_var_scope:NN} -% \begin{syntax} -% \cs{__kernel_chk_var_scope:NN} \meta{scope} \meta{var} -% \end{syntax} -% Checks the \meta{var} has the correct \meta{scope}, and if not -% raises a kernel-level error. This function is only created if -% debugging is enabled. The \meta{scope} is a single letter |l|, |g|, -% |c| denoting local variables, global variables, or constants. More -% precisely, if the variable name starts with a letter and an -% underscore (normal \pkg{expl3} convention) the function checks that -% this single letter matches the \meta{scope}. Otherwise the function -% cannot know the scope \meta{var} the first time: instead, it defines -% |\__debug_chk_/|\meta{var name} to store that information for the -% next call. Thus, if a given \meta{var} is subject to assignments of -% different scopes a kernel error will result. -% \end{function} -% -% \begin{function} -% { -% \__kernel_chk_cs_exist:N, -% \__kernel_chk_cs_exist:c, -% \__kernel_chk_var_exist:N -% } -% \begin{syntax} -% \cs{__kernel_chk_cs_exist:N} \meta{cs} -% \cs{__kernel_chk_var_exist:N} \meta{var} -% \end{syntax} -% These functions are only created if debugging is enabled. They -% check that their argument is defined according to the criteria for -% \cs{cs_if_exist_p:N}, -% and if not raises a kernel-level error. Error messages are -% different. -% \end{function} -% -% \begin{function}[EXP]{\__kernel_chk_flag_exist:NN} -% \begin{syntax} -% \cs{__kernel_chk_flag_exist:NN} -% \meta{function} \meta{flag} -% \end{syntax} -% This function is only created if debugging is enabled. It checks -% that the \meta{flag} is defined according to the criterion for -% \cs{flag_if_exist_p:N}, and if not raises a kernel-level error and -% calls the function with the argument \cs{l_tmpa_flag} to proceed -% somehow without producing too many errors. -% \end{function} -% -% \begin{function}{\__kernel_debug_log:e} -% \begin{syntax} -% \cs{__kernel_debug_log:e} \Arg{message text} -% \end{syntax} -% If the \texttt{log-functions} option is active, this function writes -% the \meta{message text} to the log file using \cs{iow_log:e}. -% Otherwise, the \meta{message text} is ignored using \cs{use_none:n}. -% This function is only created if debugging is enabled. -% \end{function} +% Internal kernel functions that are only defined here are listed in +% \pkg{l3kernel-functions}, +% see~\ref{sec:l3kernel-functions:l3debug-internals}. % % \begin{macrocode} %<*package> @@ -131,7 +69,7 @@ % % Standard file identification. % \begin{macrocode} -\ProvidesExplFile{l3debug.def}{2024-01-22}{}{L3 Debugging support} +\ProvidesExplFile{l3debug.def}{2024-02-13}{}{L3 Debugging support} % \end{macrocode} % % \begin{variable}{\s_@@_stop} @@ -782,6 +720,8 @@ { \clist_concat:NNN \clist_gconcat:NNN + \prop_concat:NNN + \prop_gconcat:NNN \seq_concat:NNN \seq_gconcat:NNN \str_concat:NNN @@ -836,6 +776,17 @@ \muskip_add:Nn \muskip_sub:Nn \muskip_set_eq:NN + \prop_clear:N + \prop_concat:NNN + \prop_pop:NnN + \prop_pop:NnNT + \prop_pop:NnNF + \prop_pop:NnNTF + \prop_put:Nnn + \prop_put_if_new:Nnn + \prop_put_from_keyval:Nn + \prop_remove:Nn + \prop_set_eq:NN \seq_set_eq:NN \skip_zero:N \skip_set:Nn @@ -905,6 +856,17 @@ \muskip_gadd:Nn \muskip_gsub:Nn \muskip_gset_eq:NN + \prop_gclear:N + \prop_gconcat:NNN + \prop_gpop:NnN + \prop_gpop:NnNT + \prop_gpop:NnNF + \prop_gpop:NnNTF + \prop_gput:Nnn + \prop_gput_if_new:Nnn + \prop_gput_from_keyval:Nn + \prop_gremove:Nn + \prop_gset_eq:NN \seq_gset_eq:NN \skip_gzero:N \skip_gset:Nn @@ -952,6 +914,8 @@ \int_const:Nn \intarray_const_from_clist:Nn \muskip_const:Nn + \prop_const_from_keyval:Nn + \prop_const_linked_from_keyval:Nn \skip_const:Nn \str_const:Nn \tl_const:Nn diff --git a/macros/latex/contrib/l3kernel/l3deprecation.dtx b/macros/latex/contrib/l3kernel/l3deprecation.dtx index 99b52ec961..674ca610bd 100644 --- a/macros/latex/contrib/l3kernel/l3deprecation.dtx +++ b/macros/latex/contrib/l3kernel/l3deprecation.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % @@ -313,6 +313,21 @@ % \end{macro} % \end{macro} % +% \subsection{Deprecated \pkg{l3msg} functions} +% +% \begin{macrocode} +%<@@=msg> +% \end{macrocode} +% +% \begin{macro}[deprecated]{\msg_gset:nnnn, \msg_gset:nnn} +% \begin{macrocode} +\__kernel_patch_deprecation:nnNNpn { 2024-01-17 } { \msg_set:nnnn } +\cs_new_protected:Npn \msg_gset:nnnn { \msg_set:nnnn } +\__kernel_patch_deprecation:nnNNpn { 2024-01-17 } { \msg_set:nnn } +\cs_new_protected:Npn \msg_gset:nnn { \msg_set:nnn } +% \end{macrocode} +% \end{macro} +% % \subsection{Deprecated \pkg{l3pdf} functions} % % \begin{macrocode} diff --git a/macros/latex/contrib/l3kernel/l3doc.dtx b/macros/latex/contrib/l3kernel/l3doc.dtx index fde5ddd103..48e022ae80 100644 --- a/macros/latex/contrib/l3kernel/l3doc.dtx +++ b/macros/latex/contrib/l3kernel/l3doc.dtx @@ -71,7 +71,7 @@ and all files in that bundle must be distributed together. % This isn't included in the typeset documentation because it's a bit % ugly: %<*class> -\ProvidesExplClass{l3doc}{2024-01-22}{} +\ProvidesExplClass{l3doc}{2024-02-13}{} {L3 Experimental documentation class} %</class> % \fi @@ -84,7 +84,7 @@ and all files in that bundle must be distributed together. % require you to do updates, if the class changes.}} % % \author{\Team} -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % \maketitle % \tableofcontents % @@ -1740,7 +1740,7 @@ and all files in that bundle must be distributed together. % A document-level command. % \begin{macrocode} \DeclareDocumentCommand \meta { m } - { \@@_meta:n {#1} } + { \texttt{ \@@_meta:n {#1} } } % \end{macrocode} % \end{macro} % @@ -1760,10 +1760,11 @@ and all files in that bundle must be distributed together. { < \tl_to_str:n {#1} > } \pdfstringdefDisableCommands { - \cs_set_eq:NN \cmd \@@_pdfstring_cmd:w - \cs_set_eq:NN \cs \@@_pdfstring_cs:w - \cs_set_eq:NN \tn \@@_pdfstring_cs:w - \cs_set_eq:NN \meta \@@_pdfstring_meta:w + \cs_set_eq:NN \cmd \@@_pdfstring_cmd:w + \cs_set_eq:NN \cs \@@_pdfstring_cs:w + \cs_set_eq:NN \tn \@@_pdfstring_cs:w + \cs_set_eq:NN \meta \@@_pdfstring_meta:w + \cs_set_eq:NN \@@_meta:n \@@_pdfstring_meta:w } % \end{macrocode} % \end{macro} @@ -1775,10 +1776,10 @@ and all files in that bundle must be distributed together. % Finally, \cs{Arg} is the same as \cs{marg}. % \begin{macrocode} \newcommand\Arg[1] - { \texttt{\char`\{} \meta{#1} \texttt{\char`\}} } + { \texttt{\char`\{} \@@_meta:n {#1} \texttt{\char`\}} } \providecommand\marg[1]{ \Arg{#1} } -\providecommand\oarg[1]{ \texttt[ \meta{#1} \texttt] } -\providecommand\parg[1]{ \texttt( \meta{#1} \texttt) } +\providecommand\oarg[1]{ \texttt[ \@@_meta:n {#1} \texttt] } +\providecommand\parg[1]{ \texttt( \@@_meta:n {#1} \texttt) } % \end{macrocode} % \end{macro} % @@ -3727,10 +3728,22 @@ and all files in that bundle must be distributed together. % % \begin{macro}{\DocInclude} % More or less exactly the same as \tn{include}, but uses -% \tn{DocInput} on a \file{.dtx} file, not \tn{input} on a \file{.tex} -% file. +% \tn{DocInput} on a \file{.fdd} or \file{.dtx} file (without file +% extension), not \tn{input} on a \file{.tex} file. % % \begin{macrocode} +\msg_new:nnn { l3doc } { missing-endgroup } + { + \str_if_eq:VnTF \@currenvir { document } + { + There~are~\int_use:N \tex_currentgrouplevel:D + \c_space_tl unclosed~groups~in~#1. + } + { + The~\@currenvir \c_space_tl environment~on~line~\@currenvline + \c_space_tl doesn't~have~a~matching~\iow_char:N\\end{\@currenvir}. + } + } \NewDocumentCommand \DocInclude { m } { \relax\clearpage @@ -3741,6 +3754,13 @@ and all files in that bundle must be distributed together. \int_compare:nNnTF \@auxout = \@partaux { \@latexerr{\string\include\space cannot~be~nested}\@eha } { \@docinclude {#1} } + % check missing \endgroup, e.g., missing "\end{macro}" in time + \int_compare:nNnF { \tex_currentgrouplevel:D } = { 0 } + { + \int_compare:nNnT { \tex_interactionmode:D } = { 0 } + { \int_gset:Nn \tex_interactionmode:D { 1 } } + \msg_fatal:nne { l3doc } { missing-endgroup } { \currentfile } + } } % \end{macrocode} % @@ -4087,7 +4107,7 @@ and all files in that bundle must be distributed together. \int_set:Nn \l_@@_tmpa_int { \tex_interactionmode:D } \errorstopmode \ClassError { l3doc } { \l_@@_tmpa_tl } { } - \int_set:Nn \tex_interactionmode:D { \l_@@_tmpa_int } + \int_gset:Nn \tex_interactionmode:D { \l_@@_tmpa_int } } } } diff --git a/macros/latex/contrib/l3kernel/l3doc.pdf b/macros/latex/contrib/l3kernel/l3doc.pdf Binary files differindex 352082c2ab..9d301da547 100644 --- a/macros/latex/contrib/l3kernel/l3doc.pdf +++ b/macros/latex/contrib/l3kernel/l3doc.pdf diff --git a/macros/latex/contrib/l3kernel/l3docstrip.dtx b/macros/latex/contrib/l3kernel/l3docstrip.dtx index 9034fb7176..f55be84c36 100644 --- a/macros/latex/contrib/l3kernel/l3docstrip.dtx +++ b/macros/latex/contrib/l3kernel/l3docstrip.dtx @@ -63,7 +63,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3docstrip.pdf b/macros/latex/contrib/l3kernel/l3docstrip.pdf Binary files differindex 2098a2ce21..923689f3d8 100644 --- a/macros/latex/contrib/l3kernel/l3docstrip.pdf +++ b/macros/latex/contrib/l3kernel/l3docstrip.pdf diff --git a/macros/latex/contrib/l3kernel/l3expan.dtx b/macros/latex/contrib/l3kernel/l3expan.dtx index 5ea58b75d5..7527b4091a 100644 --- a/macros/latex/contrib/l3kernel/l3expan.dtx +++ b/macros/latex/contrib/l3kernel/l3expan.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % @@ -571,6 +571,7 @@ % \exp_last_unbraced:Nco, % \exp_last_unbraced:NcV, % \exp_last_unbraced:Nno, +% \exp_last_unbraced:Nnf, % \exp_last_unbraced:Noo, % \exp_last_unbraced:Nfo, % \exp_last_unbraced:NNNo, @@ -1436,6 +1437,7 @@ % \exp_last_unbraced:NNNV, % \exp_last_unbraced:NNNf, % \exp_last_unbraced:Nno, +% \exp_last_unbraced:Nnf, % \exp_last_unbraced:Noo, % \exp_last_unbraced:Nfo, % \exp_last_unbraced:NnNo, @@ -1494,6 +1496,7 @@ \exp:w \exp_end_continue_f:w #4 } \cs_new:Npn \exp_last_unbraced:Nno { \::n \::o_unbraced \::: } +\cs_new:Npn \exp_last_unbraced:Nnf { \::n \::f_unbraced \::: } \cs_new:Npn \exp_last_unbraced:Noo { \::o \::o_unbraced \::: } \cs_new:Npn \exp_last_unbraced:Nfo { \::f \::o_unbraced \::: } \cs_new:Npn \exp_last_unbraced:NnNo { \::n \::N \::o_unbraced \::: } diff --git a/macros/latex/contrib/l3kernel/l3file.dtx b/macros/latex/contrib/l3kernel/l3file.dtx index cc14b48980..091c7b57f0 100644 --- a/macros/latex/contrib/l3kernel/l3file.dtx +++ b/macros/latex/contrib/l3kernel/l3file.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % @@ -718,10 +718,8 @@ % \cs{file_if_exist_p:n} \Arg{file name} % \cs{file_if_exist:nTF} \Arg{file name} \Arg{true code} \Arg{false code} % \end{syntax} -% Expands the argument of the \cs{file name} to give a string, then -% searches for this string using the current \TeX{} search -% path and the additional paths controlled by -% \cs{l_file_search_path_seq}. +% Tests if \meta{file name} is found in the path as detailed for +% \cs{file_if_exist:nTF}. % \end{function} % % \subsection{Information about files and file contents} diff --git a/macros/latex/contrib/l3kernel/l3flag.dtx b/macros/latex/contrib/l3kernel/l3flag.dtx index b0a586d48b..d1325b2c96 100644 --- a/macros/latex/contrib/l3kernel/l3flag.dtx +++ b/macros/latex/contrib/l3kernel/l3flag.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-assign.dtx b/macros/latex/contrib/l3kernel/l3fp-assign.dtx index e2e9937c2c..ca71600b16 100644 --- a/macros/latex/contrib/l3kernel/l3fp-assign.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-assign.dtx @@ -42,7 +42,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % \maketitle % % \begin{documentation} diff --git a/macros/latex/contrib/l3kernel/l3fp-aux.dtx b/macros/latex/contrib/l3kernel/l3fp-aux.dtx index cc54b7e02b..1c8c57a3f9 100644 --- a/macros/latex/contrib/l3kernel/l3fp-aux.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-aux.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-basics.dtx b/macros/latex/contrib/l3kernel/l3fp-basics.dtx index ed13beb5fd..4227b8f6f5 100644 --- a/macros/latex/contrib/l3kernel/l3fp-basics.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-basics.dtx @@ -42,7 +42,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-convert.dtx b/macros/latex/contrib/l3kernel/l3fp-convert.dtx index d5b73e7d97..413d4988a5 100644 --- a/macros/latex/contrib/l3kernel/l3fp-convert.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-convert.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-expo.dtx b/macros/latex/contrib/l3kernel/l3fp-expo.dtx index 2c86293101..0013d859a9 100644 --- a/macros/latex/contrib/l3kernel/l3fp-expo.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-expo.dtx @@ -42,7 +42,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-extended.dtx b/macros/latex/contrib/l3kernel/l3fp-extended.dtx index 0e4e48a0a9..be9cc93107 100644 --- a/macros/latex/contrib/l3kernel/l3fp-extended.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-extended.dtx @@ -42,7 +42,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-functions.dtx b/macros/latex/contrib/l3kernel/l3fp-functions.dtx index 3e423c0908..4024dbacf3 100644 --- a/macros/latex/contrib/l3kernel/l3fp-functions.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-functions.dtx @@ -42,7 +42,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-logic.dtx b/macros/latex/contrib/l3kernel/l3fp-logic.dtx index 13eeed8888..42d88735c4 100644 --- a/macros/latex/contrib/l3kernel/l3fp-logic.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-logic.dtx @@ -42,7 +42,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-parse.dtx b/macros/latex/contrib/l3kernel/l3fp-parse.dtx index ef6c2ad392..2bcd5a2c89 100644 --- a/macros/latex/contrib/l3kernel/l3fp-parse.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-parse.dtx @@ -42,7 +42,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-random.dtx b/macros/latex/contrib/l3kernel/l3fp-random.dtx index 8c68289d60..7d32a59a51 100644 --- a/macros/latex/contrib/l3kernel/l3fp-random.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-random.dtx @@ -42,7 +42,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-round.dtx b/macros/latex/contrib/l3kernel/l3fp-round.dtx index a01b9505da..e6bb4e58c0 100644 --- a/macros/latex/contrib/l3kernel/l3fp-round.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-round.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-symbolic.dtx b/macros/latex/contrib/l3kernel/l3fp-symbolic.dtx index 01e883f136..088c118b9c 100644 --- a/macros/latex/contrib/l3kernel/l3fp-symbolic.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-symbolic.dtx @@ -42,7 +42,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-traps.dtx b/macros/latex/contrib/l3kernel/l3fp-traps.dtx index 6212ff731c..90f33a7aa4 100644 --- a/macros/latex/contrib/l3kernel/l3fp-traps.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-traps.dtx @@ -42,7 +42,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % \maketitle % % \begin{documentation} diff --git a/macros/latex/contrib/l3kernel/l3fp-trig.dtx b/macros/latex/contrib/l3kernel/l3fp-trig.dtx index 5cf22fd7e9..c7d235b991 100644 --- a/macros/latex/contrib/l3kernel/l3fp-trig.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-trig.dtx @@ -42,7 +42,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-types.dtx b/macros/latex/contrib/l3kernel/l3fp-types.dtx index fc6a34f6a1..2843b9131c 100644 --- a/macros/latex/contrib/l3kernel/l3fp-types.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-types.dtx @@ -42,7 +42,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp.dtx b/macros/latex/contrib/l3kernel/l3fp.dtx index bdc1d3fae3..cfd8b118fc 100644 --- a/macros/latex/contrib/l3kernel/l3fp.dtx +++ b/macros/latex/contrib/l3kernel/l3fp.dtx @@ -49,7 +49,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fparray.dtx b/macros/latex/contrib/l3kernel/l3fparray.dtx index 2d8cd62f14..b9b4e4a096 100644 --- a/macros/latex/contrib/l3kernel/l3fparray.dtx +++ b/macros/latex/contrib/l3kernel/l3fparray.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3int.dtx b/macros/latex/contrib/l3kernel/l3int.dtx index 4d5f91cf53..9a6d09f193 100644 --- a/macros/latex/contrib/l3kernel/l3int.dtx +++ b/macros/latex/contrib/l3kernel/l3int.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3intarray.dtx b/macros/latex/contrib/l3kernel/l3intarray.dtx index 118e44ff3b..cc3f7a2c6c 100644 --- a/macros/latex/contrib/l3kernel/l3intarray.dtx +++ b/macros/latex/contrib/l3kernel/l3intarray.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3kernel-functions.dtx b/macros/latex/contrib/l3kernel/l3kernel-functions.dtx index 6b3db5fd7d..c19add5937 100644 --- a/macros/latex/contrib/l3kernel/l3kernel-functions.dtx +++ b/macros/latex/contrib/l3kernel/l3kernel-functions.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % @@ -55,17 +55,77 @@ % % \section{\pkg{l3kernel-functions}: kernel-reserved functions} % -% \subsection{Internal kernel functions} +% \subsection{Internal \pkg{l3debug} kernel functions} +% \label{sec:l3kernel-functions:l3debug-internals} +% +% These function are only created if debugging is enabled, hence they are +% actually defined in \pkg{l3debug}. +% +% \begin{function}{\__kernel_chk_var_local:N, \__kernel_chk_var_global:N} +% \begin{syntax} +% \cs{__kernel_chk_var_local:N} \meta{var} +% \cs{__kernel_chk_var_global:N} \meta{var} +% \end{syntax} +% Applies \cs{__kernel_chk_var_exist:N} \meta{var} as well as +% \cs{__kernel_chk_var_scope:NN} \meta{scope} \meta{var}, where +% \meta{scope} is |l| or~|g|. +% \end{function} % -% \begin{function}{\__kernel_chk_cs_exist:N, \__kernel_chk_cs_exist:c} +% \begin{function}{\__kernel_chk_var_scope:NN} +% \begin{syntax} +% \cs{__kernel_chk_var_scope:NN} \meta{scope} \meta{var} +% \end{syntax} +% Checks the \meta{var} has the correct \meta{scope}, and if not +% raises a kernel-level error. +% The \meta{scope} is a single letter |l|, |g|, +% |c| denoting local variables, global variables, or constants. More +% precisely, if the variable name starts with a letter and an +% underscore (normal \pkg{expl3} convention) the function checks that +% this single letter matches the \meta{scope}. Otherwise the function +% cannot know the scope \meta{var} the first time: instead, it defines +% |\__debug_chk_/|\meta{var name} to store that information for the +% next call. Thus, if a given \meta{var} is subject to assignments of +% different scopes a kernel error will result. +% \end{function} +% +% \begin{function} +% { +% \__kernel_chk_cs_exist:N, +% \__kernel_chk_cs_exist:c, +% \__kernel_chk_var_exist:N +% } % \begin{syntax} % \cs{__kernel_chk_cs_exist:N} \meta{cs} +% \cs{__kernel_chk_var_exist:N} \meta{var} +% \end{syntax} +% Checks that their argument is defined according to the criteria for +% \cs{cs_if_exist_p:N}, +% and if not raises a kernel-level error. Error messages are +% different. +% \end{function} +% +% \begin{function}[EXP]{\__kernel_chk_flag_exist:NN} +% \begin{syntax} +% \cs{__kernel_chk_flag_exist:NN} +% \meta{function} \meta{flag} % \end{syntax} -% This function is only created if debugging is enabled. It checks -% that \meta{cs} exists according to the criteria for -% \cs{cs_if_exist_p:N}, and if not raises a kernel-level error. +% Checks that the \meta{flag} is defined according to the criterion for +% \cs{flag_if_exist_p:N}, and if not raises a kernel-level error and +% calls the function with the argument \cs{l_tmpa_flag} to proceed +% somehow without producing too many errors. % \end{function} % +% \begin{function}{\__kernel_debug_log:e} +% \begin{syntax} +% \cs{__kernel_debug_log:e} \Arg{message text} +% \end{syntax} +% If the \texttt{log-functions} option is active, this function writes +% the \meta{message text} to the log file using \cs{iow_log:e}. +% Otherwise, the \meta{message text} is ignored using \cs{use_none:n}. +% \end{function} +% +% \subsection{Internal kernel functions} +% % \begin{function}{\__kernel_chk_defined:NT} % \begin{syntax} % \cs{__kernel_chk_defined:NT} \meta{variable} \Arg{true code} diff --git a/macros/latex/contrib/l3kernel/l3keys.dtx b/macros/latex/contrib/l3kernel/l3keys.dtx index 54155d6d4e..18b42ae30d 100644 --- a/macros/latex/contrib/l3kernel/l3keys.dtx +++ b/macros/latex/contrib/l3kernel/l3keys.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % @@ -89,7 +89,7 @@ % \end{verbatim} % As illustrated, keys are created inside a \meta{module}: a set of related % keys, typically those for a single module/\LaTeXe{} package. See -% Section~\label{sec:l3keys:subdivision} for suggestions on how to divide +% Section~\ref{sec:l3keys:subdivision} for suggestions on how to divide % large numbers of keys for a single module. % % At a document level, \cs{keys_set:nn} is used within a @@ -3504,7 +3504,7 @@ { \prg_return_true: } { \prg_return_false: } } -\prg_generate_conditional_variant:Nnn \keys_if_exist:nn { ne } { T , F , TF } +\prg_generate_conditional_variant:Nnn \keys_if_exist:nn { ne } { p , T , F , TF } % \end{macrocode} % \end{macro} % diff --git a/macros/latex/contrib/l3kernel/l3legacy.dtx b/macros/latex/contrib/l3kernel/l3legacy.dtx index f8846e7cb8..0a7b8bb9a1 100644 --- a/macros/latex/contrib/l3kernel/l3legacy.dtx +++ b/macros/latex/contrib/l3kernel/l3legacy.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3luatex.dtx b/macros/latex/contrib/l3kernel/l3luatex.dtx index 55f33b80a5..8a18c7fbf1 100644 --- a/macros/latex/contrib/l3kernel/l3luatex.dtx +++ b/macros/latex/contrib/l3kernel/l3luatex.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3msg.dtx b/macros/latex/contrib/l3kernel/l3msg.dtx index dced50e37f..5cbf548e0e 100644 --- a/macros/latex/contrib/l3kernel/l3msg.dtx +++ b/macros/latex/contrib/l3kernel/l3msg.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % @@ -120,7 +120,7 @@ % used. An error is raised if the \meta{message} already exists. % \end{function} % -% \begin{function}{\msg_set:nnnn, \msg_set:nnn, \msg_gset:nnnn, \msg_gset:nnn} +% \begin{function}{\msg_set:nnnn, \msg_set:nnn} % \begin{syntax} % \cs{msg_set:nnnn} \Arg{module} \Arg{message} \Arg{text} \Arg{more text} % \end{syntax} @@ -809,15 +809,17 @@ % \msg_new:nnnn, \msg_new:nnee, \msg_new:nnxx, % \msg_new:nnn, \msg_new:nne, \msg_new:nnx % } -% \begin{macro}{\msg_gset:nnnn, \msg_gset:nnn} % \begin{macro}{\msg_set:nnnn, \msg_set:nnn} % Setting a message simply means saving the appropriate text % into two functions. A sanity check first. % \begin{macrocode} -\cs_new_protected:Npn \msg_new:nnnn #1#2 +\cs_new_protected:Npn \msg_new:nnnn #1#2#3#4 { \@@_chk_free:nn {#1} {#2} - \msg_gset:nnnn {#1} {#2} + \cs_gset:cpn { \c_@@_text_prefix_tl #1 / #2 } + ##1##2##3##4 {#3} + \cs_gset:cpn { \c_@@_more_text_prefix_tl #1 / #2 } + ##1##2##3##4 {#4} } \cs_generate_variant:Nn \msg_new:nnnn { nnee , nnxx } \cs_new_protected:Npn \msg_new:nnn #1#2#3 @@ -832,19 +834,9 @@ } \cs_new_protected:Npn \msg_set:nnn #1#2#3 { \msg_set:nnnn {#1} {#2} {#3} { } } -\cs_new_protected:Npn \msg_gset:nnnn #1#2#3#4 - { - \cs_gset:cpn { \c_@@_text_prefix_tl #1 / #2 } - ##1##2##3##4 {#3} - \cs_gset:cpn { \c_@@_more_text_prefix_tl #1 / #2 } - ##1##2##3##4 {#4} - } -\cs_new_protected:Npn \msg_gset:nnn #1#2#3 - { \msg_gset:nnnn {#1} {#2} {#3} { } } % \end{macrocode} % \end{macro} % \end{macro} -% \end{macro} % % \subsection{Messages: support functions and text} % @@ -2227,6 +2219,14 @@ { internal~structure:\\\\\iow_indent:n {#4} } } } +\msg_new:nnnn { prop } { bad-link } + { Variable~'#1'~is~not~a~valid~(linked)~prop. } + { + \c_@@_coding_error_text_tl + The~variable~'#1'~has~an~incorrect~internal~structure.~ + Its~internal~entry~'#2'~points~to~'#3',~whose~name~is~not~of~the~ + form~'#4~<key>'. + } \msg_new:nnnn { clist } { non-clist } { Variable~'#1'~is~not~a~valid~clist. } { @@ -2236,6 +2236,24 @@ should~be~a~clist~variable,~but~it~includes~empty~or~blank~items~ without~braces. } +\msg_new:nnnn { prop } { misused } + { A~property~list~was~misused. } + { + \c_@@_coding_error_text_tl + A~property~list~variable~was~used~without~an~accessor~function.~ + It~ + \tl_if_empty:nTF {#1} + { is~empty. } + { contains~the~key-value~pairs \use_none:n #1 . } + } +\msg_new:nnnn { prop } { inner-make } + { '#1'~ cannot~ be~ used~ in~ a~ group. } + { + \c_@@_coding_error_text_tl + The~ command~ '#1'~ was~ applied~ to~ the~ property~ list~ + variable~ '#2', but~ the~ storage~ type~ can~ only~ be~ changed~ + at~ the~ outermost~ group~ level. + } % \end{macrocode} % % Some errors only appear in expandable settings, @@ -2247,8 +2265,6 @@ { Erroneous~variable~#1 used! } \msg_new:nnn { seq } { misused } { A~sequence~was~misused. } -\msg_new:nnn { prop } { misused } - { A~property~list~was~misused. } \msg_new:nnn { prg } { negative-replication } { Negative~argument~for~\iow_char:N\\prg_replicate:nn. } \msg_new:nnn { prop } { prop-keyval } @@ -2272,7 +2288,8 @@ { The~integer~array~#1~contains~#2~items: \\ #3 . } \msg_new:nnn { prop } { show } { - The~property~list~#1~ + The~ \str_if_eq:nnF {#3} { flat } { #3~ } + property~list~#1~ \tl_if_empty:nTF {#2} { is~empty \\>~ . } { contains~the~pairs~(without~outer~braces): #2 . } diff --git a/macros/latex/contrib/l3kernel/l3names.dtx b/macros/latex/contrib/l3kernel/l3names.dtx index 66e4056a05..6a1bb766d1 100644 --- a/macros/latex/contrib/l3kernel/l3names.dtx +++ b/macros/latex/contrib/l3kernel/l3names.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3news.pdf b/macros/latex/contrib/l3kernel/l3news.pdf Binary files differindex 4719901866..31473fd53c 100644 --- a/macros/latex/contrib/l3kernel/l3news.pdf +++ b/macros/latex/contrib/l3kernel/l3news.pdf diff --git a/macros/latex/contrib/l3kernel/l3news01.pdf b/macros/latex/contrib/l3kernel/l3news01.pdf Binary files differindex 155535a4fd..1e97f4aaca 100644 --- a/macros/latex/contrib/l3kernel/l3news01.pdf +++ b/macros/latex/contrib/l3kernel/l3news01.pdf diff --git a/macros/latex/contrib/l3kernel/l3news02.pdf b/macros/latex/contrib/l3kernel/l3news02.pdf Binary files differindex 678d28fe51..ed804fc6b3 100644 --- a/macros/latex/contrib/l3kernel/l3news02.pdf +++ b/macros/latex/contrib/l3kernel/l3news02.pdf diff --git a/macros/latex/contrib/l3kernel/l3news03.pdf b/macros/latex/contrib/l3kernel/l3news03.pdf Binary files differindex c4d3285b7e..ffcf4a37f2 100644 --- a/macros/latex/contrib/l3kernel/l3news03.pdf +++ b/macros/latex/contrib/l3kernel/l3news03.pdf diff --git a/macros/latex/contrib/l3kernel/l3news04.pdf b/macros/latex/contrib/l3kernel/l3news04.pdf Binary files differindex 996d2face0..971bea5082 100644 --- a/macros/latex/contrib/l3kernel/l3news04.pdf +++ b/macros/latex/contrib/l3kernel/l3news04.pdf diff --git a/macros/latex/contrib/l3kernel/l3news05.pdf b/macros/latex/contrib/l3kernel/l3news05.pdf Binary files differindex daea9c248a..bbdc0373a5 100644 --- a/macros/latex/contrib/l3kernel/l3news05.pdf +++ b/macros/latex/contrib/l3kernel/l3news05.pdf diff --git a/macros/latex/contrib/l3kernel/l3news06.pdf b/macros/latex/contrib/l3kernel/l3news06.pdf Binary files differindex 8193ffab13..3a46d423fa 100644 --- a/macros/latex/contrib/l3kernel/l3news06.pdf +++ b/macros/latex/contrib/l3kernel/l3news06.pdf diff --git a/macros/latex/contrib/l3kernel/l3news07.pdf b/macros/latex/contrib/l3kernel/l3news07.pdf Binary files differindex 9492361a4a..6c9632fdf4 100644 --- a/macros/latex/contrib/l3kernel/l3news07.pdf +++ b/macros/latex/contrib/l3kernel/l3news07.pdf diff --git a/macros/latex/contrib/l3kernel/l3news08.pdf b/macros/latex/contrib/l3kernel/l3news08.pdf Binary files differindex 62d965b72f..5ec72ff809 100644 --- a/macros/latex/contrib/l3kernel/l3news08.pdf +++ b/macros/latex/contrib/l3kernel/l3news08.pdf diff --git a/macros/latex/contrib/l3kernel/l3news09.pdf b/macros/latex/contrib/l3kernel/l3news09.pdf Binary files differindex 988790dadf..a2ea7832c0 100644 --- a/macros/latex/contrib/l3kernel/l3news09.pdf +++ b/macros/latex/contrib/l3kernel/l3news09.pdf diff --git a/macros/latex/contrib/l3kernel/l3news10.pdf b/macros/latex/contrib/l3kernel/l3news10.pdf Binary files differindex 6c13a5cbb1..113dfe2f19 100644 --- a/macros/latex/contrib/l3kernel/l3news10.pdf +++ b/macros/latex/contrib/l3kernel/l3news10.pdf diff --git a/macros/latex/contrib/l3kernel/l3news11.pdf b/macros/latex/contrib/l3kernel/l3news11.pdf Binary files differindex d78d434fa7..74d21a3c62 100644 --- a/macros/latex/contrib/l3kernel/l3news11.pdf +++ b/macros/latex/contrib/l3kernel/l3news11.pdf diff --git a/macros/latex/contrib/l3kernel/l3news12.pdf b/macros/latex/contrib/l3kernel/l3news12.pdf Binary files differindex 6f9eac3b37..ccbd0305ff 100644 --- a/macros/latex/contrib/l3kernel/l3news12.pdf +++ b/macros/latex/contrib/l3kernel/l3news12.pdf diff --git a/macros/latex/contrib/l3kernel/l3pdf.dtx b/macros/latex/contrib/l3kernel/l3pdf.dtx index d1e2a7e185..0490af4c46 100644 --- a/macros/latex/contrib/l3kernel/l3pdf.dtx +++ b/macros/latex/contrib/l3kernel/l3pdf.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3prefixes.csv b/macros/latex/contrib/l3kernel/l3prefixes.csv index e3512672ff..286be82652 100644 --- a/macros/latex/contrib/l3kernel/l3prefixes.csv +++ b/macros/latex/contrib/l3kernel/l3prefixes.csv @@ -12,8 +12,8 @@ alloc,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https ampersand,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12, apfs,apfontspec,Qing Lee,https://github.com/CTeX-org/apfontspec,https://github.com/CTeX-org/apfontspec.git,https://github.com/CTeX-org/apfontspec/issues,2020-05-17,2020-05-17, arch,archaeologie,Lukas C. Bossert,http://www.biblatex-archaeologie.de,https://github.com/LukasCBossert/biblatex-archaeologie.git,https://github.com/LukasCBossert/biblatex-archaeologie/issues,2017-03-24,2017-03-24, -arsenal,arsenal,Boris Veytsman,https://github.com/borisveytsman/arsenal,https://github.com/borisveytsman/arsenal,https://github.com/borisveytsman/arsenal/issues,2023-09-04,2023-09-04, array,hobby,Andrew Stacey,https://github.com/loopspace/hobby,https://github.com/loopspace/hobby,https://github.com/loopspace/hobby/issues,2013-03-16,2020-10-29, +arsenal,arsenal,Boris Veytsman,https://github.com/borisveytsman/arsenal,https://github.com/borisveytsman/arsenal,https://github.com/borisveytsman/arsenal/issues,2023-09-04,2023-09-04, atsign,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12, avm,langsci-avm,Felix Kopecky,https://ctan.org/pkg/langsci-avm,https://github.com/langsci/langsci-avm,https://github.com/langsci/langsci-avm/issues,2020-03-11,2020-03-11, babellatin,babel-latin,Keno Wehr,https://ctan.org/pkg/babel-latin,https://github.com/wehro/babel-latin,https://github.com/wehro/babel-latin/issues,2021-08-23,2021-08-23, @@ -59,6 +59,7 @@ ctuthesis,ctuthesis,Tom Hejda,https://github.com/tohecz/ctuthesis,https://github debug,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2017-07-15,2017-07-15, denisbdoc,denisbdoc,Denis Bitouzé,https://github.com/dbitouze/denisbdoc,git@github.com:dbitouze/denisbdoc.git,,2020-05-13,2020-05-13, deriv,derivative,Simon Jensen,,,,2019-07-24,2019-07-24, +didec,didec,Thomas F. Sturm,https://github.com/T-F-S/didec,https://github.com/T-F-S/didec.git,https://github.com/T-F-S/didec/issues,2024-02-02,2024-02-02, diffcoeff,diffcoeff,Andrew Parsloe,,,,2019-08-26,2019-08-26, dim,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27, document,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2019-06-02,2019-06-02, @@ -131,7 +132,7 @@ keys,"l3kernel,l3keys2e,ltkeys",The LaTeX Project,https://www.latex-project.org/ keyval,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27, kgl,kantlipsum,Enrico Gregorio,,,,2013-03-16,2013-03-16, kivitendo,"kiviletter, kivitables",Marei Peischl for Kivitendo,https://www.kivitendo.de/,https://github.com/kivitendo/kivitendo-erp,https://forum.kivitendo.de/,2021-05-28,2021-05-28, -langsci,langscibook ,Language Science Press,https://langsci-press.org,https://github.com/langsci/langscibook,https://github.com/langsci/langscibook/issues,2021-07-20,2021-07-21, +langsci,langscibook,Language Science Press,https://langsci-press.org,https://github.com/langsci/langscibook,https://github.com/langsci/langscibook/issues,2021-07-20,2021-07-21, left,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12, lltxmath,lualatex-math,Philipp Stephani,https://github.com/phst/lualatex-math,https://github.com/phst/lualatex-math.git,https://github.com/phst/lualatex-math/issues,2012-11-07,2012-11-07, log,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12, @@ -204,7 +205,7 @@ prop,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https: property,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2021-01-20,2021-03-03, pseudo,pseudo,Magnus Lie Hetland,https://github.com/mlhetland/pseudo.sty,https://github.com/mlhetland/pseudo.sty.git,https://github.com/mlhetland/pseudo.sty/issues,2019-06-24,2019-06-24, ptex,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2015-07-28,2015-07-28, -ptxcd,ptxcd, Marei Peischl,,,,2020-07-27,2020-07-27,Used for specific corporate design templates +ptxcd,ptxcd,Marei Peischl,,,,2020-07-27,2020-07-27,Used for specific corporate design templates qrbill,qrbill,Marei Peischl,https://github.com/peiTeX/qrbill,https://github.com/peiTeX/qrbill.git,https://github.com/peiTeX/qrbill/issues,2020-06-27,2020-06-27, quark,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27, rainbow,beamertheme-rainbow,samcarter,https://github.com/samcarter/beamertheme-rainbow,https://github.com/samcarter/beamertheme-rainbow,https://github.com/samcarter/beamertheme-rainbow/issues,2023-07-04,2023-07-04, @@ -250,7 +251,7 @@ template,latex2e,The LaTeX Project,https://www.latex-project.org/latex3.html,htt term,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12, tex,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2012-09-27, text,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2020-01-15,2020-01-15, -tikzfill,tikzfill,Thomas F. Sturm,https://github.com/T-F-S/tikzfill,https://github.com/T-F-S/tikzfill.git,https://github.com/T-F-S/tikzfill/issues,2022-07-19,2022-07-19, +tikzfill,tikzfill,Thomas F. Sturm,https://github.com/T-F-S/tikzfill,https://github.com/T-F-S/tikzfill.git,https://github.com/T-F-S/tikzfill/issues,2022-07-19,2022-07-19, tikzlings,tikzlings,samcarter,https://github.com/samcarter/tikzlings,git@github.com:samcarter/tikzlings.git,https://github.com/samcarter/tikzlings/issues,2023-02-17,2023-02-17, tikzsymbols,tikzsymbols,Ben Vitecek,https://github.com/Vidabe/tikzsymbols,https://github.com/Vidabe/tikzsymbols.git,https://github.com/Vidabe/tikzsymbols/issues,2018-09-26,2018-09-26, tilde,l3kernel,The LaTeX Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-05-12,2018-05-12, diff --git a/macros/latex/contrib/l3kernel/l3prefixes.pdf b/macros/latex/contrib/l3kernel/l3prefixes.pdf Binary files differindex ea2bf15903..2bd39c3787 100644 --- a/macros/latex/contrib/l3kernel/l3prefixes.pdf +++ b/macros/latex/contrib/l3kernel/l3prefixes.pdf diff --git a/macros/latex/contrib/l3kernel/l3prg.dtx b/macros/latex/contrib/l3kernel/l3prg.dtx index 0b134d713f..5b7a36238b 100644 --- a/macros/latex/contrib/l3kernel/l3prg.dtx +++ b/macros/latex/contrib/l3kernel/l3prg.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3prop.dtx b/macros/latex/contrib/l3kernel/l3prop.dtx index f6190ebee7..8281611924 100644 --- a/macros/latex/contrib/l3kernel/l3prop.dtx +++ b/macros/latex/contrib/l3kernel/l3prop.dtx @@ -43,19 +43,19 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % % \begin{documentation} % -% \pkg{expl3} implements a property list data type, which contain -% an unordered list of entries each of which consists of a \meta{key} and -% an associated \meta{value}. The \meta{key} and \meta{value} may both -% be any balanced text, and the \meta{key} is processed using -% \cs{tl_to_str:n}, meaning that category codes are ignored. It is possible to -% map functions to property lists such that the function is applied to every -% key--value pair within the list. +% \pkg{expl3} implements a \enquote{property list} data type, which contain +% an unordered list of entries each of which consists of a \meta{key} (string) +% and an associated \meta{value} (token list). The \meta{key} and \meta{value} +% may both be given as any balanced text, and the \meta{key} is processed using +% \cs{tl_to_str:n}, meaning that category codes are ignored. Entries can be +% manipulated individually, as well as collectively by applying a function to +% every key--value pair within the list. % % Each entry in a property list must have a unique \meta{key}: if an entry is % added to a property list which already contains the \meta{key} then the new @@ -63,8 +63,41 @@ % string basis, using the same method as \cs{str_if_eq:nnTF}. % % Property lists are intended for storing key-based information for use within -% code. This is in contrast to key--value lists, which are a form of -% \emph{input} parsed by the \pkg{l3keys} module. +% code. They can be converted from and to key--value lists, which are a form of +% \emph{input} parsed by the \pkg{l3keys} module. If a key--value list contains +% a \meta{key} multiple times, only the last \meta{value} associated to it will +% be kept in the conversion to a property list. +% +% Internally, property lists can use two distinct implementations with different +% data storage, which are decided when declaring the property list variable +% using \cs{prop_new:N} (\enquote{flat} storage) or \cs{prop_new_linked:N} +% (\enquote{linked} storage). After a property list is declared with +% \cs{prop_new:N} or \cs{prop_new_linked:N}, the type of internal data storage +% can be changed by \cs{prop_make_flat:N} or \cs{prop_make_linked:N}, but only +% at the outermost group level. All other \pkg{l3prop} functions transparently +% manipulate either storage method and convert as needed. +% \begin{itemize} +% \item +% The (default) \enquote{flat} storage method is suited for a relatively +% small number of entries, or when the property list is likely to be +% manipulated (copied, mapped) as a whole rather than entry-wise. It is +% significantly faster for \cs{prop_set_eq:NN}, and only slightly faster for +% \cs{prop_clear:N}, \cs{prop_concat:NNN}, and mapping functions +% \cs[no-index]{prop_map_\ldots{}}. +% +% \item +% The \enquote{linked} storage method is meant for property lists with a +% large numbers of entries. It takes up more of \TeX{}'s memory during a run, but is +% significantly faster (for long lists) when accessing or modifying +% individual entries using functions such as \cs{prop_if_in:Nn}, +% \cs{prop_item:Nn}, \cs{prop_put:Nnn}, \cs{prop_get:NnN}, +% \cs{prop_pop:NnN}, \cs{prop_remove:Nn}, as it takes a constant +% time for these operations (rather than the number of items for a +% \enquote{flat} property list). A technical drawback is that +% memory is permanently used\footnote{Until the end of the run, that +% is.} by \meta{keys} stored in a \enquote{linked} property list, +% even after they are removed and the property list is deleted. +% \end{itemize} % % \section{Creating and initialising property lists} % @@ -72,9 +105,20 @@ % \begin{syntax} % \cs{prop_new:N} \meta{property list} % \end{syntax} -% Creates a new \meta{property list} or raises an error if the name is -% already taken. The declaration is global. The \meta{property list} -% initially contains no entries. +% Creates a new \enquote{flat} \meta{property list} or raises an error if the +% name is already taken. The declaration is global. The \meta{property list} +% initially contains no entries. See also \cs{prop_new_linked:N}. +% \end{function} +% +% \begin{function}[added = 2024-02-12]{\prop_new_linked:N, \prop_new_linked:c} +% \begin{syntax} +% \cs{prop_new_linked:N} \meta{property list} +% \end{syntax} +% Creates a new \enquote{linked} \meta{property list} or raises an error if +% the name is already taken. The declaration is global. The \meta{property +% list} initially contains no entries. The internal data storage differs from +% that produced by \cs{prop_new:N} and it is optimized for property lists with +% a large number of entries. % \end{function} % % \begin{function} @@ -95,8 +139,28 @@ % \end{syntax} % Ensures that the \meta{property list} exists globally by applying % \cs{prop_new:N} if necessary, then applies -% \cs[index=prop_clear:N]{prop_(g)clear:N} to leave -% the list empty. +% \cs[index=prop_clear:N]{prop_(g)clear:N} to leave the list empty. +% \begin{texnote} +% If the property list exists and is of \enquote{linked} type, it +% is cleared but not made into a flat property list. +% \end{texnote} +% \end{function} +% +% \begin{function}[added = 2024-02-12] +% { +% \prop_clear_new_linked:N, \prop_clear_new_linked:c, +% \prop_gclear_new_linked:N, \prop_gclear_new_linked:c +% } +% \begin{syntax} +% \cs{prop_clear_new_linked:N} \meta{property list} +% \end{syntax} +% Ensures that the \meta{property list} exists globally by applying +% \cs{prop_new_linked:N} if necessary, then applies +% \cs[index=prop_clear:N]{prop_(g)clear:N} to leave the list empty. +% \begin{texnote} +% If the property list exists and is of \enquote{flat} type, it +% is cleared but not made into a linked property list. +% \end{texnote} % \end{function} % % \begin{function} @@ -107,8 +171,8 @@ % \begin{syntax} % \cs{prop_set_eq:NN} \meta{property list_1} \meta{property list_2} % \end{syntax} -% Sets the content of \meta{property list_1} equal to that of -% \meta{property list_2}. +% Sets the content of \meta{property list_1} equal to that of \meta{property +% list_2}. This converts as needed between the two storage types. % \end{function} % % \begin{function}[added = 2017-11-28, updated = 2021-11-07] @@ -125,6 +189,9 @@ % \end{syntax} % Sets \meta{property list} to contain key--value pairs given in the second % argument. If duplicate keys appear only the last of the values is kept. +% In contrast to most keyval lists (\emph{e.g.}~those in \pkg{l3keys}), each +% key here \emph{must} be followed with an \texttt{=} sign even to specify an +% empty \meta{value}. % % Spaces are trimmed around every \meta{key} and every \meta{value}, % and if the result of trimming spaces consists of a single brace @@ -133,10 +200,6 @@ % signs. The \meta{key} is then processed by \cs{tl_to_str:n}. % This function correctly detects the |=| and |,| signs provided they % have the standard category code~$12$ or they are active. -% -% Notice that in contrast to most keyval lists (\emph{e.g.}~those in -% \pkg{l3keys}), each key here \emph{must} be followed with an \texttt{=} -% sign. % \end{function} % % \begin{function}[added = 2017-11-28, updated = 2021-11-07] @@ -148,17 +211,55 @@ % \meta{key2} |=| \meta{value2} |,| \ldots{} % \} % \end{syntax} -% Creates a new constant \meta{property list} or raises an error if the +% Creates a new constant \enquote{flat} \meta{property list} or raises +% an error if the % name is already taken. The \meta{property list} is set globally to % contain key--value pairs given in the second argument, processed in % the way described for \cs{prop_set_from_keyval:Nn}. If duplicate % keys appear only the last of the values is kept. % This function correctly detects the |=| and |,| signs provided they % have the standard category code~$12$ or they are active. +% \end{function} +% +% \begin{function}[added = 2024-02-12] +% {\prop_const_linked_from_keyval:Nn, \prop_const_linked_from_keyval:cn} +% \begin{syntax} +% \cs{prop_const_linked_from_keyval:Nn} \meta{prop~var} +% \{ +% \meta{key1} |=| \meta{value1} |,| +% \meta{key2} |=| \meta{value2} |,| \ldots{} +% \} +% \end{syntax} +% Creates a new constant \enquote{linked} \meta{prop~var} or raises an +% error if the +% name is already taken. The \meta{prop~var} is set globally to +% contain key--value pairs given in the second argument, processed in +% the way described for \cs{prop_set_from_keyval:Nn}. If duplicate +% keys appear only the last of the values is kept. +% This function correctly detects the |=| and |,| signs provided they +% have the standard category code~$12$ or they are active. +% \end{function} % -% Notice that in contrast to most keyval lists (\emph{e.g.}~those in -% \pkg{l3keys}), each key here \emph{must} be followed with an \texttt{=} -% sign. +% \begin{function}[added = 2024-02-12]{\prop_make_flat:N, \prop_make_flat:c} +% \begin{syntax} +% \cs{prop_make_flat:N} \meta{property list} +% \end{syntax} +% Changes the internal storage type of the \meta{property list} to be +% the same \enquote{flat} storage as \cs{prop_new:N}. The key--value +% pairs of the \meta{property list} are preserved by the change. If +% the property list was already flat then nothing is done. This +% function can only be used at the outermost group level. +% \end{function} +% +% \begin{function}[added = 2024-02-12]{\prop_make_linked:N, \prop_make_linked:c} +% \begin{syntax} +% \cs{prop_make_linked:N} \meta{property list} +% \end{syntax} +% Changes the internal storage type of the \meta{property list} to be +% the same \enquote{linked} storage as \cs{prop_new_linked:N}. The +% key--value pairs of the \meta{property list} are preserved by the +% change. If the property list was already linked then nothing is +% done. This function can only be used at the outermost group level. % \end{function} % % \section{Adding and updating property list entries} @@ -171,22 +272,20 @@ % \prop_put:Nen, \prop_put:NeV, \prop_put:Nev, \prop_put:Nee, % \prop_put:Nno, \prop_put:Non, \prop_put:Noo, % \prop_put:cnn, \prop_put:cnV, \prop_put:cnv, \prop_put:cne, -% \prop_put:cno, % \prop_put:cVn, \prop_put:cVV, \prop_put:cVv, \prop_put:cVe, % \prop_put:cvn, \prop_put:cvV, \prop_put:cvv, \prop_put:cve, % \prop_put:cen, \prop_put:ceV, \prop_put:cev, \prop_put:cee, -% \prop_put:con, \prop_put:coo, +% \prop_put:cno, \prop_put:con, \prop_put:coo, % \prop_gput:Nnn, \prop_gput:NnV, \prop_gput:Nnv, \prop_gput:Nne, % \prop_gput:NVn, \prop_gput:NVV, \prop_gput:NVv, \prop_gput:NVe, % \prop_gput:Nvn, \prop_gput:NvV, \prop_gput:Nvv, \prop_gput:Nve, % \prop_gput:Nen, \prop_gput:NeV, \prop_gput:Nev, \prop_gput:Nee, % \prop_gput:Nno, \prop_gput:Non, \prop_gput:Noo, % \prop_gput:cnn, \prop_gput:cnV, \prop_gput:cnv, \prop_gput:cne, -% \prop_gput:cno, % \prop_gput:cVn, \prop_gput:cVV, \prop_gput:cVv, \prop_gput:cVe, % \prop_gput:cvn, \prop_gput:cvV, \prop_gput:cvv, \prop_gput:cve, % \prop_gput:cen, \prop_gput:ceV, \prop_gput:cev, \prop_gput:cee, -% \prop_gput:con, \prop_gput:coo +% \prop_gput:cno, \prop_gput:con, \prop_gput:coo % } % \begin{syntax} % \cs{prop_put:Nnn} \meta{property list} \Arg{key} \Arg{value} @@ -227,6 +326,7 @@ % \meta{property list_3}, and saves the result in \meta{property list_1}. If a % key appears in both \meta{property list_2} and \meta{property list_3} then the % last value, namely the value in \meta{property list_3} is kept. +% This converts as needed between the two storage types. % \end{function} % % \begin{function}[added = 2021-05-16, updated = 2021-11-07] @@ -319,7 +419,7 @@ % the \meta{token list variable} is local. See also \cs{prop_gpop:NnNTF}. % \end{function} % -% \begin{function}[added = 2014-07-17, EXP] +% \begin{function}[EXP, added = 2014-07-17] % { % \prop_item:Nn, \prop_item:NV, \prop_item:Ne, \prop_item:No, % \prop_item:cn, \prop_item:cV, \prop_item:ce, \prop_item:co @@ -331,8 +431,11 @@ % the \meta{property list}. If the \meta{key} is missing, this has % an empty expansion. % \begin{texnote} -% This function is slower than the non-expandable analogue -% \cs{prop_get:NnN}. +% For \enquote{flat} property lists, this expandable function iterates +% through every key--value pair and is therefore slower than a +% non-expandable approach based on \cs{prop_get:NnN}. +% (For \enquote{linked} property lists both functions are fast.) +% % The result is returned within the \tn{unexpanded} % primitive (\cs{exp_not:n}), which means that the \meta{value} % does not expand further when appearing in an \texttt{e}-type @@ -354,7 +457,7 @@ % \end{syntax} % Expands to the \meta{property list} in a key--value notation. Keep in mind % that a \meta{property list} is \emph{unordered}, while key--value interfaces -% don't necessarily are, so this can't be used for arbitrary interfaces. +% are not necessarily, so this cannot be used for arbitrary interfaces. % \begin{texnote} % The result is returned within the \tn{unexpanded} primitive % (\cs{exp_not:n}), which means that the key--value list does not expand @@ -402,7 +505,7 @@ % Tests if the \meta{property list} is empty (containing no entries). % \end{function} % -% \begin{function}[updated = 2011-09-15, EXP, pTF] +% \begin{function}[EXP, pTF, updated = 2011-09-15] % { % \prop_if_in:Nn, \prop_if_in:NV, \prop_if_in:Ne, \prop_if_in:No, % \prop_if_in:cn, \prop_if_in:cV, \prop_if_in:ce, \prop_if_in:co @@ -414,9 +517,10 @@ % Tests if the \meta{key} is present in the \meta{property list}, % making the comparison using the method described by \cs{str_if_eq:nnTF}. % \begin{texnote} -% This function iterates through every key--value pair in the -% \meta{property list} and is therefore slower than using the -% non-expandable \cs{prop_get:NnNTF}. +% For \enquote{flat} property lists, this expandable function iterates +% through every key--value pair and is therefore slower than a +% non-expandable approach based on \cs{prop_get:NnNTF}. +% (For \enquote{linked} property lists both functions are fast.) % \end{texnote} % \end{function} % @@ -424,11 +528,11 @@ % % The functions in this section combine tests for the presence of a key % in a property list with recovery of the associated valued. This makes them -% useful for cases where different cases follow dependent on the presence +% useful for cases where different code follows depending on the presence % or absence of a key in a property list. They offer increased readability % and performance over separate testing and recovery phases. % -% \begin{function}[updated = 2012-05-19, TF] +% \begin{function}[TF, updated = 2012-05-19] % { % \prop_get:NnN, \prop_get:NVN, \prop_get:NvN, \prop_get:NeN, % \prop_get:NoN, @@ -458,7 +562,8 @@ % \prop_pop:coN % } % \begin{syntax} -% \cs{prop_pop:NnNTF} \meta{property list} \Arg{key} \meta{token list variable} \Arg{true code} \Arg{false code} +% \cs{prop_pop:NnNTF} \meta{property list} \Arg{key} \meta{token list variable} \\ +% ~~\Arg{true code} \Arg{false code} % \end{syntax} % If the \meta{key} is not present in the \meta{property list}, leaves % the \meta{false code} in the input stream. The value of the @@ -479,7 +584,8 @@ % \prop_gpop:coN % } % \begin{syntax} -% \cs{prop_gpop:NnNTF} \meta{property list} \Arg{key} \meta{token list variable} \Arg{true code} \Arg{false code} +% \cs{prop_gpop:NnNTF} \meta{property list} \Arg{key} \meta{token list variable} \\ +% ~~\Arg{true code} \Arg{false code} % \end{syntax} % If the \meta{key} is not present in the \meta{property list}, leaves % the \meta{false code} in the input stream. The value of the @@ -498,7 +604,7 @@ % local assignments made by the \meta{function} or \meta{code} discussed % below remain in effect after the loop. % -% \begin{function}[updated = 2013-01-08, rEXP] +% \begin{function}[rEXP, updated = 2013-01-08] % {\prop_map_function:NN, \prop_map_function:cN} % \begin{syntax} % \cs{prop_map_function:NN} \meta{property list} \meta{function} @@ -509,7 +615,7 @@ % The order in which \meta{entries} are returned is not defined and % should not be relied upon. % To pass further arguments to the \meta{function}, see -% \cs{prop_map_tokens:Nn}. +% \cs{prop_map_inline:Nn} (non-expandable) or \cs{prop_map_tokens:Nn}. % \end{function} % % \begin{function}[updated = 2013-01-08] @@ -543,7 +649,7 @@ % arguments. For that specific task, \cs{prop_item:Nn} is faster. % \end{function} % -% \begin{function}[updated = 2012-06-29, rEXP]{\prop_map_break:} +% \begin{function}[rEXP, updated = 2012-06-29]{\prop_map_break:} % \begin{syntax} % \cs{prop_map_break:} % \end{syntax} @@ -570,7 +676,7 @@ % \end{texnote} % \end{function} % -% \begin{function}[updated = 2012-06-29, rEXP]{\prop_map_break:n} +% \begin{function}[rEXP, updated = 2012-06-29]{\prop_map_break:n} % \begin{syntax} % \cs{prop_map_break:n} \Arg{code} % \end{syntax} @@ -604,27 +710,34 @@ % \begin{syntax} % \cs{prop_show:N} \meta{property list} % \end{syntax} -% Displays the entries in the \meta{property list} in the terminal. +% Displays the entries in the \meta{property list} in the terminal, +% and specifies its storage type. % \end{function} % % \begin{function}[added = 2014-08-12, updated = 2021-04-29]{\prop_log:N, \prop_log:c} % \begin{syntax} % \cs{prop_log:N} \meta{property list} % \end{syntax} -% Writes the entries in the \meta{property list} in the log file. +% Writes the entries in the \meta{property list} in the log file, +% and specifies its storage type. % \end{function} % % \section{Scratch property lists} % +% There is no need to include both flat and linked property lists as +% scratch variables. We arbitrarily pick the older implementation. +% % \begin{variable}[added = 2012-06-23]{\l_tmpa_prop, \l_tmpb_prop} -% Scratch property lists for local assignment. These are never used by +% Scratch \enquote{flat} property lists for local assignment. +% These are never used by % the kernel code, and so are safe for use with any \LaTeX3-defined % function. However, they may be overwritten by other non-kernel % code and so should only be used for short-term storage. % \end{variable} % % \begin{variable}[added = 2012-06-23]{\g_tmpa_prop, \g_tmpb_prop} -% Scratch property lists for global assignment. These are never used by +% Scratch \enquote{flat} property lists for global assignment. +% These are never used by % the kernel code, and so are safe for use with any \LaTeX3-defined % function. However, they may be overwritten by other non-kernel % code and so should only be used for short-term storage. @@ -652,97 +765,98 @@ %<@@=prop> % \end{macrocode} % -% A property list is a macro whose top-level expansion is of the form +% With the (default) flat data storage, a property list is a macro whose +% top-level expansion is of the form % \begin{quote} -% \cs{s_@@} +% \cs{s_@@} \cs{@@_chk:w} % \cs{@@_pair:wn} \meta{key_1} \cs{s_@@} \Arg{value_1} \\ % \ldots{} \\ % \cs{@@_pair:wn} \meta{key_n} \cs{s_@@} \Arg{value_n} \\ % \end{quote} -% where \cs{s_@@} is a scan mark -% (equal to \cs{scan_stop:}), and \cs{@@_pair:wn} can be used to map -% through the property list. -% -% \begin{variable}{\s_@@} -% The internal token used at the beginning of property lists. This is -% also used after each \meta{key} (see \cs{@@_pair:wn}). -% \end{variable} -% -% \begin{variable}{\@@_pair:wn} -% \begin{syntax} -% \cs{@@_pair:wn} \meta{key} \cs{s_@@} \Arg{item} -% \end{syntax} -% The internal token used to begin each key--value pair in the -% property list. If expanded outside of a mapping or manipulation -% function, an error is raised. The definition should always be -% set globally. -% \end{variable} -% -% \begin{variable}{\l_@@_internal_tl} -% Token list used to store new key--value pairs to be inserted by -% functions of the \cs{prop_put:Nnn} family. -% \end{variable} -% -% \begin{function}[updated = 2013-01-08]{\@@_split:NnTF} -% \begin{syntax} -% \cs{@@_split:NnTF} \meta{property list} \Arg{key} \Arg{true code} \Arg{false code} -% \end{syntax} -% Splits the \meta{property list} at the \meta{key}, giving three -% token lists: the \meta{extract} of \meta{property list} before the -% \meta{key}, the \meta{value} associated with the \meta{key} and the -% \meta{extract} of the \meta{property list} after the \meta{value}. -% Both \meta{extracts} retain the internal structure of a property -% list, and the concatenation of the two \meta{extracts} is a -% property list. -% If the \meta{key} is present in the \meta{property list} then the -% \meta{true code} is left in the input stream, with |#1|, |#2|, and -% |#3| replaced by the first \meta{extract}, the \meta{value}, and the -% second extract. -% If the \meta{key} is not present in the \meta{property list} then -% the \meta{false code} is left in the input stream, with no trailing -% material. -% Both \meta{true code} and \meta{false code} are used in the -% replacement text of a macro defined internally, hence macro -% parameter characters should be doubled, except |#1|, |#2|, and |#3| -% which stand in the \meta{true code} for the three extracts from the -% property list. -% The \meta{key} comparison takes place as described for \cs{str_if_eq:nn}. -% \end{function} +% where \cs{s_@@} is a scan mark (equal to \cs{scan_stop:}), \cs{@@_chk:w} +% produces a suitable error if the property list is used directly in the input +% stream, and \cs{@@_pair:wn} can be used to map through the property list. +% +% With the linked data storage, each property list entry +% \meta{key_i}--\meta{value_i} is stored into a token list +% \cs[no-index]{@@~\meta{prefix}~\meta{key_i}}. The \meta{prefix} is one or +% more characters (no spaces), constructed automatically only once, when the +% property list is initially declared. The control sequence name does not +% conform to standard naming for variables because (1) this is an internal +% control sequence, not really a \pkg{expl3} variable; (2) keeping track of the +% scope |l| or~|g| throughout all functions would be a pretty big mess, +% especially if users accidentally mix local and global use (we would have to +% always check for such mistakes, rather than only checking when suitable debug +% options are set); (3) shorter control sequence names use less memory and are +% quicker in case of hash collisions, which may matter since we are using many +% control sequences. +% +% We need to enable mapping through such a property list, but without storing a +% list of all entries anywhere: this is achieved by making each of these token +% lists also store a pointer to the next entry. To enable efficient deletion, +% the token lists also store a pointer to the previous entry. This means we +% have a doubly-linked list. To avoid having to special-case the two ends of +% the doubly-linked list when deleting entries, we include as a zeroth entry in +% the doubly-linked list the property list variable itself, and we include as an +% $(n+1)$-th entry in the doubly-linked list an end-pointer +% \cs{@@~\meta{prefix}} (no trailing space, so it differs from an empty key). +% The space before \meta{prefix} ensures there is no collision with +% other \pkg{l3prop} internal functions, even if we have very many +% linked property lists being defined. +% +% The property list variable itself is a token list of the form +% \begin{quote} +% \cs{@@_flatten:w} \cs{@@~\meta{prefix}} \cs{s_@@} \Arg{prefix} +% \cs[no-index]{@@~\meta{prefix}~\meta{key_1}} +% \end{quote} +% Here, \cs{@@_flatten:w} serves as an efficiently recognized marker, and when +% \texttt{f}-expanded it is tasked with fully unpacking the property list into +% the same form as the default data storage so as to ease conversion. The +% \meta{prefix} is used when looking up an entry. The token list +% \cs{@@~\meta{prefix}} (see below) contains a pointer to the last key to help +% insert a new entry. The pointer to \meta{key_1} is needed to start a mapping. +% The token list labeled by \meta{key_i} is of the form +% \begin{quote} +% \cs{use_none:n} \cs[no-index]{@@~\meta{prefix}~\meta{key$_{i-1}$}} +% \cs{@@_pair:wn} \meta{key_i} \cs{s_@@} \Arg{value_i} +% \cs[no-index]{@@~\meta{prefix}~\meta{key$_{i+1}$}} +% \end{quote} +% where the pointer to \meta{key$_{i-1}$} is needed when deleting the +% \meta{key_i}. Expanding this will run \cs{@@_pair:wn} on the +% \meta{key_i}--\meta{value_i} pair (for speed, \meta{key_i} is kept as explicit +% tokens rather than slowly extracting it from a control sequence name), then +% move on to the next key, thus mapping through the whole list. The mapping is +% ended upon expanding \cs{@@~\meta{prefix}}, which is the token list +% \begin{quote} +% \cs{use_none:n} \cs[no-index]{@@~\meta{prefix}~\meta{key_n}} +% \end{quote} +% Let us think about deleting the \meta{key_i}. We need to update the +% \meta{key$_{i-1}$} and \meta{key$_{i+1}$} to point to each other +% instead of \meta{key_i}. To edit the corresponding token lists, it is +% important that \cs[no-index]{@@~\meta{prefix}~\meta{key_i}} be at the +% \enquote{same place} in the token lists also in the boundary cases +% $i=1$ or $i=n$, namely as the second token, or as the second argument +% after \cs{s_@@}. % -% \begin{macro}{\s_@@} -% A private scan mark is used as a marker after each key, and at the -% very beginning of the property list. -% \begin{macrocode} -\scan_new:N \s_@@ -% \end{macrocode} -% \end{macro} +% \subsection{Internal auxiliaries} % -% \begin{macro}{\@@_pair:wn} -% The delimiter is always defined, but when misused simply triggers an -% error and removes its argument. +% \begin{macro}{\@@_tmp:w} +% Scratch macro, defined as needed, for instance to save \cs{@@_pair:wn} when +% concatenating. % \begin{macrocode} -\cs_new:Npn \@@_pair:wn #1 \s_@@ #2 - { \msg_expandable_error:nn { prop } { misused } } +\cs_new_eq:NN \@@_tmp:w ? % \end{macrocode} % \end{macro} % % \begin{variable}{\l_@@_internal_tl} -% Token list used to store the new key--value pair inserted by -% \cs{prop_put:Nnn} and friends. +% Token list used in various places: for the prefix; when converting from flat +% to linked props; and to store the new key--value pair inserted by +% \cs{prop_put:Nnn}. % \begin{macrocode} \tl_new:N \l_@@_internal_tl % \end{macrocode} % \end{variable} % -% \begin{variable}[tested = m3prop004]{\c_empty_prop} -% An empty prop. -% \begin{macrocode} -\tl_const:Nn \c_empty_prop { \s_@@ } -% \end{macrocode} -% \end{variable} -% -% \subsection{Internal auxiliaries} -% % \begin{variable}{\s_@@_mark,\s_@@_stop} % Internal scan marks. % \begin{macrocode} @@ -769,10 +883,160 @@ % \end{macro} % \end{macro} % +% \subsection{Structure of a property list} +% +% \begin{macro}{\s_@@} +% A private scan mark is used as a marker after each key, and at the +% very beginning of the property list. +% \begin{macrocode} +\scan_new:N \s_@@ +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_chk:w, \@@_chk_loop:nw, \@@_chk_get:nw} +% This removes the flat property list from the input stream and complains +% about a bad use of a property list. Since property lists do not have an +% end-marker, we slowly peek ahead in a loop. Speed does not matter since +% this is for an error situation. While \cs{@@_pair:wn} does not keep a fixed +% definition, it always includes the internal \cs{s_@@} in its argument +% specification, so that there is no risk of accidentally picking up a public +% token instead of \cs{@@_pair:wn} when doing a meaning test. We collect the +% keys and values to produce a more useful error message. +% \begin{macrocode} +\cs_new_protected:Npn \@@_chk:w { \@@_chk_loop:nw { } } +\cs_new_protected:Npn \@@_chk_loop:nw #1 + { + \peek_meaning:NTF \@@_pair:wn + { \@@_chk_get:nw {#1} } + { \msg_error:nne { prop } { misused } {#1} } + } +\cs_new_protected:Npn \@@_chk_get:nw #1 \@@_pair:wn #2 \s_@@ #3 + { \@@_chk_loop:nw { #1 , ~ {#2} = { \tl_to_str:n {#3} } } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_pair:wn} +% Used as \cs{@@_pair:wn} \meta{key} \cs{s_@@} \Arg{item} for both storage +% types, this internal token starts each key--value pair in the property list. +% This default definition is changed globally by any mapping function, so +% there is not much point trying to make it an error. Instead, the error is +% produced by \cs{@@_chk:w}. +% \begin{macrocode} +\cs_new:Npn \@@_pair:wn #1 \s_@@ #2 { } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_flatten:w} +% We implement here the fact that \texttt{f}-expanding a linked property list +% gives a flat property list. +% Leaving a linked property list in the input stream will turn it into a flat +% property list so that the error implemented by \cs{@@_chk:w} will correctly +% be triggered. +% \begin{macrocode} +\cs_new_protected:Npn \@@_flatten:w #1 \s_@@ #2#3 + { \use:e { \@@_flatten_aux:N #3 } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[rEXP] +% {\@@_flatten:N, \@@_flatten_aux:w, \@@_flatten_aux:N, \@@_flatten_loop:w} +% The main function \cs{@@_flatten:N} receives a linked property list and +% flattens it. The auxiliary \cs{@@_flatten_aux:N} receives a pointer to the +% first key and flattens the linked property list into a flat property list. +% This is only restricted-expandable as it involves mapping through all of the +% property list's entries starting from \meta{key_1}. The looping function +% \cs{@@_flatten_loop:w} removes \cs{use_none:n} and a backwards pointer~|#2|, +% leaves the key--value pair for \cs{use:e} to receive, and calls itself +% again after expanding the next key's token list. Its argument |#3| is +% empty, except at the end where it is the \cs{use_none:nnnn} appearing in the +% definition of~\cs{@@_flatten_aux:N}, which ends the loop. +% \begin{macrocode} +\cs_new:Npn \@@_flatten:N #1 + { \exp_after:wN \@@_flatten_aux:w #1 } +\cs_new:Npn \@@_flatten_aux:w #1 \s_@@ #2 { \@@_flatten_aux:N } +\cs_new:Npn \@@_flatten_aux:N #1 + { + \s_@@ \@@_chk:w + \exp_after:wN \@@_flatten_loop:w #1 + \use_none:nnnn \@@_pair:wn \s_@@ { } + } +\cs_new:Npn \@@_flatten_loop:w #1#2#3 \@@_pair:wn #4 \s_@@ #5 + { + #3 + \exp_not:n { \@@_pair:wn #4 \s_@@ {#5} } + \exp_after:wN \@@_flatten_loop:w + } +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\g_@@_prefix_int, \c_@@_basis_int} +% Used to assign prefixes for each linked property list. It is converted to +% base \cs{c_@@_basis_int}, then each digit is converted to a character, +% starting at |!| (the character after space). +% \begin{macrocode} +\int_new:N \g_@@_prefix_int +\int_const:Nn \c_@@_basis_int { \c_max_char_int - `\! } +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\@@_next_prefix:, \@@_to_prefix:n} +% Store in \cs{l_@@_internal_tl} the conversion of \cs{g_@@_prefix_int} to +% characters, and increment this integer for use in the next linked property +% list. No need to optimize since this is only used when declaring the +% property list the first time. +% The aim here is to make this string as short as we can, given the +% range of distinct characters available. This speeds up the work of +% \cs{cs:w} \ldots{} \cs{cs_end:} that looks up keys in the hash table. +% \begin{macrocode} +\cs_new_protected:Npn \@@_next_prefix: + { + \tl_set:Ne \l_@@_internal_tl + { \@@_to_prefix:n { \g_@@_prefix_int } } + \int_gincr:N \g_@@_prefix_int + } +\cs_new:Npn \@@_to_prefix:n #1 + { + \int_compare:nNnTF {#1} > \c_@@_basis_int + { + \exp_args:Nf \@@_to_prefix:n + { \int_div_truncate:nn {#1} \c_@@_basis_int } + \exp_args:Nf \@@_to_prefix:n + { \int_mod:nn {#1} \c_@@_basis_int } + } + { \char_generate:nn { `\! + #1 } { 12 } } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_if_flat:NTF, \@@_if_flat_aux:w} +% We could either test for the presence of \cs{@@_chk:w} (flat +% property list) or of \cs{@@_flatten:w} (linked property list). We +% make the second choice; this way props that are accidentally +% \tn{relax} are treated as they were before. The auxiliary receives +% \cs{use_i:nn} or \cs{use_ii:nn} as~|#3|. +% \begin{macrocode} +\cs_new:Npn \@@_if_flat:NTF #1 + { + \exp_after:wN \@@_if_flat_aux:w #1 + \s_@@_mark \use_ii:nn + \@@_flatten:w \s_@@_mark \use_i:nn \s_@@_stop + } +\cs_new:Npn \@@_if_flat_aux:w + #1 \@@_flatten:w #2 \s_@@_mark #3 #4 \s_@@_stop {#3} +% \end{macrocode} +% \end{macro} +% % \subsection{Allocation and initialisation} % +% \begin{variable}[tested = m3prop004]{\c_empty_prop} +% An empty flat prop. +% \begin{macrocode} +\tl_const:Nn \c_empty_prop { \s_@@ \@@_chk:w } +% \end{macrocode} +% \end{variable} +% % \begin{macro}[tested = m3prop001]{\prop_new:N, \prop_new:c} -% Property lists are initialized with the value \cs{c_empty_prop}. +% Flat property lists are initialized with the value \cs{c_empty_prop}. % \begin{macrocode} \cs_new_protected:Npn \prop_new:N #1 { @@ -783,23 +1047,107 @@ % \end{macrocode} % \end{macro} % +% \begin{macro}[tested = m3prop001]{\prop_new_linked:N, \prop_new_linked:c} +% \begin{macro}{\@@_new_linked:N} +% The auxiliary is used in \cs{prop_make_linked:N}. +% For linked property lists, get a new prefix in \cs{l_@@_internal_tl}, then +% use it to set up the internal structure: the last token in~|#1| is usually a +% pointer to the first key, which is here the end-pointer. That end-pointer +% has a pointer to the previous key (usually the last key), which is the +% variable~|#1| itself that begins the doubly-linked list. +% \begin{macrocode} +\cs_new_protected:Npn \prop_new_linked:N #1 + { + \__kernel_chk_if_free_cs:N #1 + \@@_new_linked:N #1 + } +\cs_new_protected:Npn \@@_new_linked:N #1 + { + \@@_next_prefix: + \cs_gset_nopar:Npe #1 + { + \@@_flatten:w + \exp_not:c { @@ ~ \l_@@_internal_tl } + \s_@@ { \l_@@_internal_tl } + \exp_not:c { @@ ~ \l_@@_internal_tl } + } + \cs_gset_nopar:cpe { @@ ~ \l_@@_internal_tl } + { + \exp_not:N \use_none:n + \exp_not:N #1 + } + } +\cs_generate_variant:Nn \prop_new_linked:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% % \begin{macro}[tested = m3prop001]{\prop_clear:N, \prop_clear:c} % \begin{macro}[tested = m3prop001]{\prop_gclear:N, \prop_gclear:c} -% The same idea for clearing. +% \begin{macro}{\@@_clear:NNN, \@@_clear:wNNN, \@@_clear_loop:Nw} +% Clearing a flat property list is like declaring it anew, simply setting it +% equal to \cs{c_empty_prop}. For linked property lists we must clear all of +% the variables storing individual keys, which requires a loop. At each step +% of the loop, \cs{@@_clear_loop:Nw} receives +% \cs[index=cs_set_eq:NN]{cs_(g)set_eq:NN}, \cs{use_none:n}, the backwards +% pointer, an empty~|#4| (except at the end of the loop), and the key--value +% pair |#5=#6| which we disregard. The looping auxiliary undefines the +% previous key's token list (this includes the main token list, but that is +% fine because it is restored at the end) and calls itself after expanding the +% next key's token list. The loop ends when |#4| is \cs{use_none:nnnn}. +% After the loop, \cs{@@_clear:wNNN} correctly sets up the main variable~|#6| +% and the end-pointer~|#1|. Importantly, this is done using +% \cs[index=cs_set_nopar:Npe]{cs_(g)set_nopar:Npe} and \cs{exp_not:n} because +% the almost-equivalent \cs{tl_set:Nn} would complain in debug mode about the +% fact that the main variable is undefined at this stage. Importantly, +% \cs{@@_clear_entries:NN} is used in the implementation of +% \cs{prop_set_eq:NN}. % \begin{macrocode} -\cs_new_protected:Npn \prop_clear:N #1 - { \prop_set_eq:NN #1 \c_empty_prop } +\cs_new_protected:Npn \prop_clear:N + { \@@_clear:NNN \cs_set_eq:NN \cs_set_nopar:Npe } \cs_generate_variant:Nn \prop_clear:N { c } -\cs_new_protected:Npn \prop_gclear:N #1 - { \prop_gset_eq:NN #1 \c_empty_prop } +\cs_new_protected:Npn \prop_gclear:N + { \@@_clear:NNN \cs_gset_eq:NN \cs_gset_nopar:Npe } \cs_generate_variant:Nn \prop_gclear:N { c } +\cs_new_protected:Npn \@@_clear:NNN #1#2#3 + { + \@@_if_flat:NTF #3 + { #1 #3 \c_empty_prop } + { \exp_after:wN \@@_clear:wNNN #3 #1 #2 #3 } + } +\cs_new_protected:Npn \@@_clear:wNNN + \@@_flatten:w #1 \s_@@ #2#3#4#5#6 + { + \@@_clear_entries:NN #4 #3 + #5 #6 { \exp_not:n { \@@_flatten:w #1 \s_@@ {#2} #1 } } + #5 #1 { \exp_not:n { \use_none:n #6 } } + } +\cs_new_protected:Npn \@@_clear_entries:NN #1#2 + { + \exp_after:wN \@@_clear_loop:Nw \exp_after:wN #1 #2 + \use_none:nnnn \@@_pair:wn \s_@@ { } + } +\cs_new_protected:Npn \@@_clear_loop:Nw + #1#2#3#4 \@@_pair:wn #5 \s_@@ #6 + { + #1 #3 \tex_undefined:D + #4 + \exp_after:wN \@@_clear_loop:Nw + \exp_after:wN #1 + } % \end{macrocode} % \end{macro} % \end{macro} +% \end{macro} % -% \begin{macro}[tested = m3prop001]{\prop_clear_new:N, \prop_clear_new:c} -% \begin{macro}[tested = m3prop001]{\prop_gclear_new:N, \prop_gclear_new:c} -% Once again a simple variation of the token list functions. +% \begin{macro}[tested = m3prop001] +% { +% \prop_clear_new:N, \prop_clear_new:c, +% \prop_gclear_new:N, \prop_gclear_new:c, +% \prop_clear_new_linked:N, \prop_clear_new_linked:c, +% \prop_gclear_new_linked:N, \prop_gclear_new_linked:c +% } +% A simple variation of the token list functions. % \begin{macrocode} \cs_new_protected:Npn \prop_clear_new:N #1 { \prop_if_exist:NTF #1 { \prop_clear:N #1 } { \prop_new:N #1 } } @@ -807,26 +1155,176 @@ \cs_new_protected:Npn \prop_gclear_new:N #1 { \prop_if_exist:NTF #1 { \prop_gclear:N #1 } { \prop_new:N #1 } } \cs_generate_variant:Nn \prop_gclear_new:N { c } +\cs_new_protected:Npn \prop_clear_new_linked:N #1 + { \prop_if_exist:NTF #1 { \prop_clear:N #1 } { \prop_new_linked:N #1 } } +\cs_generate_variant:Nn \prop_clear_new_linked:N { c } +\cs_new_protected:Npn \prop_gclear_new_linked:N #1 + { \prop_if_exist:NTF #1 { \prop_gclear:N #1 } { \prop_new_linked:N #1 } } +\cs_generate_variant:Nn \prop_gclear_new_linked:N { c } % \end{macrocode} % \end{macro} -% \end{macro} % % \begin{macro}[tested = m3prop001] % {\prop_set_eq:NN, \prop_set_eq:cN, \prop_set_eq:Nc, \prop_set_eq:cc} % \begin{macro}[tested = m3prop001] % {\prop_gset_eq:NN, \prop_gset_eq:cN, \prop_gset_eq:Nc, \prop_gset_eq:cc} -% These are simply copies from the token list functions. +% \begin{macro} +% { +% \@@_set_eq:NNNN, \@@_set_eq:wNNNN, \@@_set_eq:nNnNN, +% \@@_set_eq_loop:NNnw, \@@_set_eq_end:w +% } +% If both variables are accidentally the same variable (or equal flat property +% lists, as it turns out) we do nothing, otherwise the following code would +% lose all entries. If the target variable~|#3| is a flat prop, either copy +% directly or flatten before copying. If it is a linked prop, we must clear +% it, then go through the entries in~|#4| to add them to~|#3|. +% \begin{macrocode} +\cs_new_protected:Npn \prop_set_eq:NN + { \@@_set_eq:NNNN \cs_set_eq:NN \cs_set_nopar:Npe } +\cs_generate_variant:Nn \prop_set_eq:NN { Nc , cN , cc } +\cs_new_protected:Npn \prop_gset_eq:NN + { \@@_set_eq:NNNN \cs_gset_eq:NN \cs_gset_nopar:Npe } +\cs_generate_variant:Nn \prop_gset_eq:NN { Nc , cN , cc } +\cs_new_protected:Npn \@@_set_eq:NNNN #1#2#3#4 + { + \cs_if_eq:NNF #3#4 + { + \@@_if_flat:NTF #3 + { + \@@_if_flat:NTF #4 + { #1 #3 #4 } + { #2 #3 { \@@_flatten:N #4 } } + } + { \exp_after:wN \@@_set_eq:wNNNN #3 #1#2#3#4 } + } + } +\cs_new_protected:Npn \@@_set_eq:wNNNN + \@@_flatten:w #1 \s_@@ #2#3#4#5#6#7 + { + \@@_clear_entries:NN #4 #3 + \exp_args:Nf \@@_set_eq:nNnNN {#7} #1 {#2} #5 #6 + } +% \end{macrocode} +% We have used that |f|-expanding either type of prop gives a flat prop. At +% this stage \cs{@@_set_eq:nNnNN} receives the second variable as a flat prop, +% the end-pointer, the prefix, the suitable +% \cs[index=cs_set_nopar:Npe]{cs_(g)set_nopar:Npe} assignment, and the first +% variable itself. Remove the leading \cs{s_@@} and \cs{@@_chk:w} with +% \cs{use_i:nnn}, then start the loop. +% \begin{macrocode} +\cs_new_protected:Npn \@@_set_eq:nNnNN #1#2#3#4#5 + { + \use_i:nnn + { + \@@_set_eq_loop:NNnw #5 #4 {#3} + \@@_flatten:w #2 \s_@@ {#3} + } + #1 + \use_none:n \@@_pair:wn ? \s_@@ + } +% \end{macrocode} +% The looping function receives the current pointer~|#1| (initially the +% variable itself), the defining function~|#2| and the prefix~|#3|, then a +% partial definition~|#4| (which in later stages includes the backwards +% pointer), followed by the current value as \cs{s_@@} |{#5}|. It seeks the +% next key~|#7| to construct in \cs{l_@@_internal_tl} the next pointer +% \cs[no-index]{@@~\meta{prefix}~\meta{next key}} (the +% argument~|#6| is empty, except at the end of the loop, where it is +% \cs{use_none:n} in such a way as to delete the \meta{space} and \meta{next +% key}). Then the token list (current pointer)~|#1| is set-up to contain the +% partial definition and current value, as well as the newly constructed next +% pointer. After a line responsible for correctly ending the loop with +% \cs{@@_set_eq_end:w}, we loop, setting up the next definition, which starts +% with \cs{use_none:n} and a backwards pointer to~|#1| followed by the +% \meta{next key}~|#7| and so on. % \begin{macrocode} -\cs_new_eq:NN \prop_set_eq:NN \tl_set_eq:NN -\cs_new_eq:NN \prop_set_eq:Nc \tl_set_eq:Nc -\cs_new_eq:NN \prop_set_eq:cN \tl_set_eq:cN -\cs_new_eq:NN \prop_set_eq:cc \tl_set_eq:cc -\cs_new_eq:NN \prop_gset_eq:NN \tl_gset_eq:NN -\cs_new_eq:NN \prop_gset_eq:Nc \tl_gset_eq:Nc -\cs_new_eq:NN \prop_gset_eq:cN \tl_gset_eq:cN -\cs_new_eq:NN \prop_gset_eq:cc \tl_gset_eq:cc +\cs_new_protected:Npn \@@_set_eq_loop:NNnw + #1#2#3#4 \s_@@ #5#6 \@@_pair:wn #7 \s_@@ + { + \tl_set:Ne \l_@@_internal_tl { \exp_not:c { @@ ~ #3 #6 ~ #7 } } + #2 #1 { \exp_not:n { #4 \s_@@ {#5} } \exp_not:o \l_@@_internal_tl } + \use_none:n #6 \@@_set_eq_end:w + \exp_after:wN \@@_set_eq_loop:NNnw \l_@@_internal_tl #2 {#3} + \use_none:n #1 \@@_pair:wn #7 \s_@@ + } +% \end{macrocode} +% The end-code picks up what is needed to correctly assign the last token +% list (the end pointer), which is simply \cs{use_none:n} +% \cs[no-index]{@@_\meta{prefix}\meta{space}\meta{key_n}}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_set_eq_end:w + \exp_after:wN \@@_set_eq_loop:NNnw #1#2#3 + \use_none:n #4#5 \s_@@ + { + \exp_after:wN #2 \l_@@_internal_tl { \exp_not:n { \use_none:n #4 } } + } +% \end{macrocode} +% \end{macro} +% \end{macro} +% \end{macro} +% +% \begin{macro}{\prop_make_flat:N, \prop_make_flat:c \@@_make_flat:Nn} +% The only interesting case is when given a linked prop. Clear the +% linked property list using \cs{@@_clear:wNNN} with local assignments +% (it does not matter since we are at the outermost group level, and +% \cs{cs_set_eq:NN} is very slightly faster than its global version. +% Then store the contents (expanded preventively by \cs{exp_args:NNf}) +% with an assignment \cs{cs_set_nopar:Npe} that does not perform +% \pkg{l3debug} checks. +% \begin{macrocode} +\cs_new_protected:Npn \prop_make_flat:N #1 + { + \int_compare:nNnTF { \tex_currentgrouplevel:D } = 0 + { + \@@_if_flat:NTF #1 { } + { \exp_args:NNf \@@_make_flat:Nn #1 {#1} } + } + { + \msg_error:nnee { prop } { inner-make } + { \token_to_str:N \prop_make_flat:N } { \token_to_str:N #1 } + } + } +\cs_generate_variant:Nn \prop_make_flat:N { c } +\cs_new_protected:Npn \@@_make_flat:Nn #1#2 + { + \exp_after:wN \@@_clear:wNNN #1 \cs_set_eq:NN \cs_set_nopar:Npe #1 + \cs_set_nopar:Npe #1 { \exp_not:n {#2} } + } % \end{macrocode} % \end{macro} +% +% \begin{macro}{\prop_make_linked:N, \prop_make_linked:c, \@@_make_linked:Nn} +% The only interesting case is when given a flat prop. We expand the +% contents for later use. Then \cs{@@_new_linked:N} disregards that +% previous value of~|#1| and initializes the linked prop. We can then +% use an auxiliary \cs{@@_set_eq:wNNNN} underlying +% \cs{prop_set_eq:NN}, with the prop contents saved as +% \cs{l_@@_internal_tl}. That step is a bit unsafe, as +% \cs{l_@@_internal_tl} (really, a flat prop here) is used within +% \cs{@@_set_eq:wNNNN} itself, but it is in fact expanded early enough +% to be ok. +% \begin{macrocode} +\cs_new_protected:Npn \prop_make_linked:N #1 + { + \int_compare:nNnTF { \tex_currentgrouplevel:D } = 0 + { + \@@_if_flat:NTF #1 + { \exp_args:NNo \@@_make_linked:Nn #1 {#1} } { } + } + { + \msg_error:nnee { prop } { inner-make } + { \token_to_str:N \prop_make_linked:N } { \token_to_str:N #1 } + } + } +\cs_generate_variant:Nn \prop_make_linked:N { c } +\cs_new_protected:Npn \@@_make_linked:Nn #1#2 + { + \@@_new_linked:N #1 + \tl_set:Nn \l_@@_internal_tl {#2} + \exp_after:wN \@@_set_eq:wNNNN #1 + \cs_set_eq:NN \cs_set_nopar:Npe #1 \l_@@_internal_tl + } +% \end{macrocode} % \end{macro} % % \begin{variable}[tested = m3prop004]{\l_tmpa_prop, \l_tmpb_prop} @@ -841,156 +1339,223 @@ % \end{variable} % \end{variable} % -% \begin{variable}{\l_@@_internal_prop} -% Property list used by \cs{prop_concat:NNN}, -% \cs{prop_set_from_keyval:Nn} and others. -% \begin{macrocode} -\prop_new:N \l_@@_internal_prop -% \end{macrocode} -% \end{variable} -% % \begin{macro} % { % \prop_concat:NNN, \prop_concat:ccc, -% \prop_gconcat:NNN, \prop_gconcat:ccc, \@@_concat:NNNN +% \prop_gconcat:NNN, \prop_gconcat:ccc, +% \@@_concat:NNNNN, \@@_concat:nNNN % } -% Combine two property lists. We cannot use a simple -% \cs{tl_concat:NNN} because there may be some duplicate keys between -% the two property lists. +% The basic strategy is to copy the first variable into the target, +% then loop through the second variable, calling +% \cs[index=prop_put:Nnn]{prop_(g)put:Nnn} on each item. To avoid +% running the \pkg{l3debug} scope checks on each of these steps, we +% use the auxiliaries that underly \cs{prop_set_eq:NN} and +% \cs{prop_put:Nnn}, whose syntax is a bit unwieldy. +% We work directly with the target prop |#3| as a scratch space, +% because copying over from a temporary variable to |#3| would be slow +% in the linked case. If |#5| is |#3| itself we have to be careful +% not to lose the data, and we even take the opportunity to skip the +% copying step completely. To keep the correct version of the +% duplicate keys we use the code underlying \cs{prop_put_if_new:Nnn}, +% which involves passing \cs{use_none:nnn} to the auxiliary instead of +% nothing. +% There is no need to check for the case where |#3| is equal to~|#4| +% because in that case \cs[index=prop_set_eq:NN]{prop_(g)set_eq:NN} +% |#3| |#4| (or rather the underlying auxiliary) is correctly set up +% to do no needless work. % \begin{macrocode} \cs_new_protected:Npn \prop_concat:NNN - { \@@_concat:NNNN \prop_set_eq:NN } + { \@@_concat:NNNNN \cs_set_eq:NN \cs_set_nopar:Npe } \cs_generate_variant:Nn \prop_concat:NNN { ccc } \cs_new_protected:Npn \prop_gconcat:NNN - { \@@_concat:NNNN \prop_gset_eq:NN } + { \@@_concat:NNNNN \cs_gset_eq:NN \cs_gset_nopar:Npe } \cs_generate_variant:Nn \prop_gconcat:NNN { ccc } -\cs_new_protected:Npn \@@_concat:NNNN #1#2#3#4 +\cs_new_protected:Npn \@@_concat:NNNNN #1#2#3#4#5 { - \prop_set_eq:NN \l_@@_internal_prop #3 - \prop_map_inline:Nn #4 { \prop_put:Nnn \l_@@_internal_prop {##1} {##2} } - #1 #2 \l_@@_internal_prop + \cs_if_eq:NNTF #3 #5 + { \@@_concat:nNNN \use_none:nnn #2 #3 #4 } + { + \@@_set_eq:NNNN #1 #2 #3 #4 + \@@_concat:nNNN { } #2 #3 #5 + } + } +\cs_new_protected:Npn \@@_concat:nNNN #1#2#3#4 + { + \cs_gset_eq:NN \@@_tmp:w \@@_pair:wn + \cs_gset_protected:Npn \@@_pair:wn ##1 \s_@@ + { \@@_put:nNNnn {#1} #2 #3 {##1} } + \exp_last_unbraced:Nf \use_none:nn #4 + \cs_gset_eq:NN \@@_pair:wn \@@_tmp:w } % \end{macrocode} % \end{macro} % -% \begin{macro}{\prop_set_from_keyval:Nn, \prop_set_from_keyval:cn} -% \begin{macro}{\prop_gset_from_keyval:Nn, \prop_gset_from_keyval:cn} -% \begin{macro}{\prop_const_from_keyval:Nn, \prop_const_from_keyval:cn} -% \begin{macro}{\prop_put_from_keyval:Nn, \prop_put_from_keyval:cn} -% \begin{macro}{\prop_gput_from_keyval:Nn, \prop_gput_from_keyval:cn} -% \begin{macro}{\@@_missing_eq:n} -% To avoid tracking throughout the loop the variable name and whether -% the assignment is local/global, do everything in a scratch variable -% and empty it afterwards to avoid wasting memory. Loop through items -% separated by commas, with \cs{prg_do_nothing:} to avoid losing -% braces. After checking for termination, split the item at the first -% and then at the second |=| (which ought to be the first of the -% trailing~|=| that we added). For both splits trim spaces and call a -% function (first \cs{@@_from_keyval_key:w} then -% \cs{@@_from_keyval_value:w}), followed by the trimmed material, -% \cs{s_@@_mark}, the subsequent part of the item, and the trailing |=|'s -% and \cs{s_@@_stop}. After finding the \meta{key} just store it after -% \cs{s_@@_stop}. After finding the \meta{value} ignore completely empty -% items (both trailing~|=| were used as delimiters and all parts are -% empty); if the remaining part~|#2| consists exactly of the second -% trailing~|=| (namely there was exactly one |=|~in the item) then -% output one key--value pair for the property list; otherwise complain -% about a missing or extra~|=|. +% \begin{macro} +% { +% \prop_put_from_keyval:Nn, \prop_put_from_keyval:cn, +% \prop_gput_from_keyval:Nn, \prop_gput_from_keyval:cn, +% \@@_from_keyval:nn, \@@_from_keyval:Nnn, +% \@@_missing_eq:n +% } +% The core is a call to \cs{keyval_parse:nnn}, with an error message +% \cs{@@_missing_eq:n} for entries without~|=|, and a call to (essentially) +% \cs[index=prop_put:Nnn]{prop_(g)put:Nnn} for valid key--value pairs. +% To avoid repeated scope checks (and errors) when \pkg{l3debug} is +% active, we instead use the auxiliary underlying \cs{prop_put:Nnn}. +% Because blank keys are valid here, in contrast to \pkg{l3keys}, we set and +% restore \cs{l__kernel_keyval_allow_blank_keys_bool}. +% The key--value argument may be quite large so we avoid reading it until it +% is really necessary. +% \begin{macrocode} +\cs_new_protected:Npn \prop_put_from_keyval:Nn #1 + { \@@_from_keyval:nn { \@@_put:nNNnn { } \cs_set_nopar:Npe #1 } } +\cs_generate_variant:Nn \prop_put_from_keyval:Nn { c } +\cs_new_protected:Npn \prop_gput_from_keyval:Nn #1 + { \@@_from_keyval:nn { \@@_put:nNNnn { } \cs_gset_nopar:Npe #1 } } +\cs_generate_variant:Nn \prop_gput_from_keyval:Nn { c } +\cs_new_protected:Npn \@@_from_keyval:nn + { + \bool_if:NTF \l__kernel_keyval_allow_blank_keys_bool + { \@@_from_keyval:Nnn \c_true_bool } + { \@@_from_keyval:Nnn \c_false_bool } + } +\cs_new_protected:Npn \@@_from_keyval:Nnn #1#2#3 + { + \bool_set_eq:NN \l__kernel_keyval_allow_blank_keys_bool \c_true_bool + \keyval_parse:nnn \@@_missing_eq:n {#2} {#3} + \bool_set_eq:NN \l__kernel_keyval_allow_blank_keys_bool #1 + } +\cs_new_protected:Npn \@@_missing_eq:n + { \msg_error:nnn { prop } { prop-keyval } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \prop_set_from_keyval:Nn, \prop_set_from_keyval:cn, +% \prop_gset_from_keyval:Nn, \prop_gset_from_keyval:cn, +% } +% Just empty the prop (with the auxiliary underlying +% \cs{prop_clear:N} to avoid \pkg{l3debug} problems) and push +% key--value entries using +% \cs[index=prop_put_from_keyval:Nn]{prop_(g)put_from_keyval:Nn}. % \begin{macrocode} \cs_new_protected:Npn \prop_set_from_keyval:Nn #1 { - \prop_clear:N #1 + \@@_clear:NNN \cs_set_eq:NN \cs_set_nopar:Npe #1 \prop_put_from_keyval:Nn #1 } \cs_generate_variant:Nn \prop_set_from_keyval:Nn { c } \cs_new_protected:Npn \prop_gset_from_keyval:Nn #1 { - \prop_gclear:N #1 + \@@_clear:NNN \cs_gset_eq:NN \cs_gset_nopar:Npe #1 \prop_gput_from_keyval:Nn #1 } \cs_generate_variant:Nn \prop_gset_from_keyval:Nn { c } -\cs_new_protected:Npn \prop_const_from_keyval:Nn #1#2 +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \prop_const_from_keyval:Nn, \prop_const_from_keyval:cn, +% \prop_const_linked_from_keyval:Nn, \prop_const_linked_from_keyval:cn +% } +% For both flat and linked constant props, we create |#1| then use the +% same auxiliary as for \cs{prop_gput_from_keyval:Nn}. It is most +% natural to use the already packaged \cs{prop_gput:Nnn}, but that +% would mean doing an assignment on a supposedly constant property +% list. To avoid errors when \pkg{l3debug} is activated, we use the +% auxiliary underlying \cs{prop_gput:Nnn}. +% \begin{macrocode} +\cs_new_protected:Npn \prop_const_from_keyval:Nn #1 { - \prop_set_from_keyval:Nn \l_@@_internal_prop {#2} - \tl_const:Ne #1 { \exp_not:o \l_@@_internal_prop } - \prop_clear:N \l_@@_internal_prop + \prop_new:N #1 + \@@_from_keyval:nn { \@@_put:nNNnn { } \cs_gset_nopar:Npe #1 } } \cs_generate_variant:Nn \prop_const_from_keyval:Nn { c } -\cs_new_protected:Npn \prop_put_from_keyval:Nn - { - \bool_if:NTF \l__kernel_keyval_allow_blank_keys_bool - { \@@_keyval_parse:NNNn \c_true_bool } - { \@@_keyval_parse:NNNn \c_false_bool } - \prop_put:Nnn - } -\cs_generate_variant:Nn \prop_put_from_keyval:Nn { c } -\cs_new_protected:Npn \prop_gput_from_keyval:Nn - { - \bool_if:NTF \l__kernel_keyval_allow_blank_keys_bool - { \@@_keyval_parse:NNNn \c_true_bool } - { \@@_keyval_parse:NNNn \c_false_bool } - \prop_gput:Nnn - } -\cs_generate_variant:Nn \prop_gput_from_keyval:Nn { c } -\cs_new_protected:Npn \@@_missing_eq:n - { \msg_error:nnn { prop } { prop-keyval } } -\cs_new_protected:Npn \@@_keyval_parse:NNNn #1#2#3#4 +\cs_new_protected:Npn \prop_const_linked_from_keyval:Nn #1 { - \bool_set_eq:NN \l__kernel_keyval_allow_blank_keys_bool \c_true_bool - \keyval_parse:nnn \@@_missing_eq:n { #2 #3 } {#4} - \bool_set_eq:NN \l__kernel_keyval_allow_blank_keys_bool #1 + \prop_new_linked:N #1 + \@@_from_keyval:nn { \@@_put:nNNnn { } \cs_gset_nopar:Npe #1 } } +\cs_generate_variant:Nn \prop_const_linked_from_keyval:Nn { c } % \end{macrocode} % \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} % % \subsection{Accessing data in property lists} % -% \begin{macro}{\@@_split:NnTF} -% \begin{macro}{\@@_split_aux:NnTF} -% \begin{macro}[EXP]{\@@_split_aux:w} -% This function is used by most of the module, and hence must be fast. -% It receives a \meta{property list}, a \meta{key}, a \meta{true code} -% and a \meta{false code}. The aim is to split the \meta{property -% list} at the given \meta{key} into the \meta{extract_1} before the -% key--value pair, the \meta{value} associated with the \meta{key} and -% the \meta{extract_2} after the key--value pair. This is done using -% a delimited function, whose definition is as follows, where the -% \meta{key} is turned into a string. +% Accessing/deleting/adding entries is mostly done by \cs{@@_split:NnTFn}, which +% must be fast because it is used in many \pkg{l3prop} functions. Its syntax is +% as follows. +% \begin{quote} +% \cs{@@_split:NnTFn} \meta{property list} \Arg{key} \\ +% ~~\Arg{true code} \Arg{false code} \Arg{link code} +% \end{quote} +% If the \meta{property list} uses the linked data storage, then it runs +% the \meta{link code}, otherwise it does as follows. +% +% It splits the \meta{property list} at the \meta{key}, giving three +% token lists: the \meta{entries before} the \meta{key}, the +% \meta{value} associated with the \meta{key} and the \meta{entries +% after} the \meta{key}. Both the \meta{entries before} and the +% \meta{entries after} can be empty or consist of some number of +% consecutive entries \cs{@@_pair:wn} \meta{key_i} \cs{s_@@} +% \Arg{value_i}. If the \meta{key} is present in the \meta{property +% list} then the \meta{true code} is left in the input stream, with +% |#2|, |#3|, and |#4| replaced by the \meta{entries before}, +% \meta{value}, and \meta{entries after}. If the \meta{key} is not +% present in the \meta{property list} then the \meta{false code} is left +% in the input stream. Only the \meta{true code} is used in the +% replacement text of a macro defined internally, which requires +% |##|~doubling. +% +% \begin{macro}{\@@_split:NnTFn} +% \begin{macro}{\@@_split_aux:NnTFn} +% \begin{macro}{\@@_split_aux:w} +% The aim is to split the \meta{property list} at the given \meta{key} +% into the \meta{extract_1} before the key--value pair, the +% \meta{value} associated with the \meta{key} and the \meta{extract_2} +% after the key--value pair. This is done using a delimited function, +% whose definition is as follows, where the \meta{key} is turned into +% a string. % \begin{quote} -% \cs{cs_set:Npn} \cs{@@_split_aux:w} |#1| \\ -% \quad \cs{@@_pair:wn} \meta{key} \cs{s_@@} |#2| \\ -% \quad |#3| \cs{s_@@_mark} |#4| |#5| \cs{s_@@_stop} \\ -% \quad |{| |#4| \Arg{true code} \Arg{false code} |}| +% \cs{cs_set:Npn} \cs{@@_split_aux:w} |#1| \cs{@@_chk:w} |#2| \\ +% \quad \cs{@@_pair:wn} \meta{key} \cs{s_@@} |#3| \\ +% \quad |#4| \cs{s_@@_mark} |#5| |#6| \cs{s_@@_stop} \\ +% \quad |{| |#5| \Arg{true code} |}| % \end{quote} % % If the \meta{key} is present in the property list, -% \cs{@@_split_aux:w}'s |#1| is the part before the \meta{key}, |#2| -% is the \meta{value}, |#3| is the part after the \meta{key}, |#4| is -% \cs{use_i:nn}, and |#5| is additional tokens that we do not care +% \cs{@@_split_aux:w}'s |#2| is the part before the \meta{key}, |#3| +% is the \meta{value}, |#4| is the part after the \meta{key}, |#5| is +% \cs{use_i:nnn}, and |#6| is additional tokens that we do not care % about. The \meta{true code} is left in the input stream, and can -% use the parameters |#1|, |#2|, |#3| for the three parts of the +% use the parameters |#2|, |#3|, |#4| for the three parts of the % property list as desired. Namely, the original property list is in -% this case |#1| \cs{@@_pair:wn} \meta{key} \cs{s_@@} |{#2}| |#3|. +% this case \cs{s_@@} \cs{@@_chk:w} |#2| \cs{@@_pair:wn} \meta{key} +% \cs{s_@@} |{#3}| |#4|. % % If the \meta{key} is not there, then the \meta{function} is -% \cs{use_ii:nn}, which keeps the \meta{false code}. +% \cs{use_ii:nnn}, which keeps the \meta{false code}. If the property +% list uses the doubly-linked list storage, then the argument +% delimited by \cs{@@_chk:w} includes the whole property list, |#2|, +% |#3|, |#4| are empty, and |#5| is \cs{use_iii:nnn}. In all three +% cases, the appopriate code among \meta{true code}, \meta{false +% code}, and \meta{linked code} is run. % \begin{macrocode} -\cs_new_protected:Npn \@@_split:NnTF #1#2 - { \exp_args:NNo \@@_split_aux:NnTF #1 { \tl_to_str:n {#2} } } -\cs_new_protected:Npn \@@_split_aux:NnTF #1#2#3#4 +\cs_new_protected:Npn \@@_split:NnTFn #1#2 + { \exp_args:NNo \@@_split_aux:NnTFn #1 { \tl_to_str:n {#2} } } +\cs_new_protected:Npn \@@_split_aux:NnTFn #1#2#3 { - \cs_set:Npn \@@_split_aux:w ##1 - \@@_pair:wn #2 \s_@@ ##2 ##3 \s_@@_mark ##4 ##5 \s_@@_stop - { ##4 {#3} {#4} } - \exp_after:wN \@@_split_aux:w #1 \s_@@_mark \use_i:nn - \@@_pair:wn #2 \s_@@ { } \s_@@_mark \use_ii:nn \s_@@_stop + \cs_set:Npn \@@_split_aux:w ##1 \@@_chk:w ##2 + \@@_pair:wn #2 \s_@@ ##3 ##4 \s_@@_mark ##5 ##6 \s_@@_stop + { ##5 {#3} } + \exp_after:wN \@@_split_aux:w #1 \s_@@_mark \use_i:nnn + \@@_pair:wn #2 \s_@@ { } \s_@@_mark \use_ii:nnn + \@@_chk:w + \@@_pair:wn #2 \s_@@ { } \s_@@_mark \use_iii:nnn + \s_@@_stop } -\cs_new:Npn \@@_split_aux:w { } % \end{macrocode} % \end{macro} % \end{macro} @@ -998,37 +1563,13 @@ % % \begin{macro}[tested = m3prop002] % { -% \prop_remove:Nn, \prop_remove:NV, \prop_remove:Ne, -% \prop_remove:cn, \prop_remove:cV, \prop_remove:ce -% } -% \begin{macro}[tested = m3prop002] -% { -% \prop_gremove:Nn, \prop_gremove:NV, \prop_gremove:Ne, -% \prop_gremove:cn, \prop_gremove:cV, \prop_gremove:ce +% \prop_get:NnN, \prop_get:NVN, \prop_get:NvN, \prop_get:NeN, +% \prop_get:NoN, \prop_get:NxN, +% \prop_get:cnN, \prop_get:cVN, \prop_get:cvN, \prop_get:ceN, +% \prop_get:coN, \prop_get:cxN, +% \prop_get:cnc % } -% Deleting from a property starts by splitting the list. -% If the key is present in the property list, the returned value is ignored. -% If the key is missing, nothing happens. -% \begin{macrocode} -\cs_new_protected:Npn \prop_remove:Nn #1#2 - { - \@@_split:NnTF #1 {#2} - { \tl_set:Nn #1 { ##1 ##3 } } - { } - } -\cs_new_protected:Npn \prop_gremove:Nn #1#2 - { - \@@_split:NnTF #1 {#2} - { \tl_gset:Nn #1 { ##1 ##3 } } - { } - } -\cs_generate_variant:Nn \prop_remove:Nn { NV , Ne , c , cV , ce } -\cs_generate_variant:Nn \prop_gremove:Nn { NV , Ne , c , cV , ce } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}[tested = m3prop002] +% \begin{macro}[TF, tested = m3prop004] % { % \prop_get:NnN, \prop_get:NVN, \prop_get:NvN, \prop_get:NeN, % \prop_get:NoN, \prop_get:NxN, @@ -1036,64 +1577,75 @@ % \prop_get:coN, \prop_get:cxN, % \prop_get:cnc % } -% Getting an item from a list is very easy: after splitting, -% if the key is in the property list, just set the token list variable -% to the return value, otherwise to \cs{q_no_value}. +% \begin{macro}{\@@_get:NnnTF} +% \begin{macro}[EXP]{\@@_get_linked:w, \@@_get_linked_aux:w} +% Here we implement both \cs{prop_get:NnN} and its branching version through +% \cs{@@_get:NnnTF}. It receives the prop and key, followed by an assignment +% used when the value is found, \meta{true code} to run after the assignment, +% and some fall-back \meta{false code} for absent values. It relies on +% \cs{@@_split:NnTFn}. For a flat prop, the first four arguments of +% \cs{@@_split:NnTFn} are used, and run either the assignment~|#3{##3}| and +% \meta{true code}~|#4|, or the \meta{false code}~|#5|. % \begin{macrocode} \cs_new_protected:Npn \prop_get:NnN #1#2#3 { - \@@_split:NnTF #1 {#2} - { \tl_set:Nn #3 {##2} } - { \tl_set:Nn #3 { \q_no_value } } + \@@_get:NnnTF #1 {#2} + { \tl_set:Nn #3 } { } { \tl_set:Nn #3 { \q_no_value } } } \cs_generate_variant:Nn \prop_get:NnN { NV , Nv , Ne , c , cV , cv , ce } \cs_generate_variant:Nn \prop_get:NnN { No , Nx , co , cx } \cs_generate_variant:Nn \prop_get:NnN { cnc } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}[tested = m3prop002] -% { -% \prop_pop:NnN, \prop_pop:NVN, -% \prop_pop:NoN, -% \prop_pop:cnN, \prop_pop:cVN, -% \prop_pop:coN, -% } -% \begin{macro}[tested = m3prop002] -% { -% \prop_gpop:NnN, \prop_gpop:NVN, -% \prop_gpop:NoN, -% \prop_gpop:cnN, \prop_gpop:cVN, -% \prop_gpop:coN, -% } -% Popping a value also starts by doing the split. -% If the key is present, save the value in the token list and update the -% property list as when deleting. -% If the key is missing, save \cs{q_no_value} in the token list. -% \begin{macrocode} -\cs_new_protected:Npn \prop_pop:NnN #1#2#3 +\prg_new_protected_conditional:Npnn \prop_get:NnN #1#2#3 { T , F , TF } { - \@@_split:NnTF #1 {#2} - { - \tl_set:Nn #3 {##2} - \tl_set:Nn #1 { ##1 ##3 } - } - { \tl_set:Nn #3 { \q_no_value } } + \@@_get:NnnTF #1 {#2} + { \tl_set:Nn #3 } \prg_return_true: \prg_return_false: } -\cs_new_protected:Npn \prop_gpop:NnN #1#2#3 +\prg_generate_conditional_variant:Nnn \prop_get:NnN + { NV , Nv , Ne , c , cV , cv , ce } { T , F , TF } +\prg_generate_conditional_variant:Nnn \prop_get:NnN + { No , Nx , co , cx } { T , F , TF } +\prg_generate_conditional_variant:Nnn \prop_get:NnN + { cnc } { T , F , TF } +\cs_new_protected:Npn \@@_get:NnnTF #1#2#3#4#5 { - \@@_split:NnTF #1 {#2} - { - \tl_set:Nn #3 {##2} - \tl_gset:Nn #1 { ##1 ##3 } - } - { \tl_set:Nn #3 { \q_no_value } } + \@@_split:NnTFn #1 {#2} + { #3 {##3} #4 } + {#5} + { \exp_after:wN \@@_get_linked:w #1 {#2} {#3} {#4} {#5} } } -\cs_generate_variant:Nn \prop_pop:NnN { NV , No } -\cs_generate_variant:Nn \prop_pop:NnN { c , cV , co } -\cs_generate_variant:Nn \prop_gpop:NnN { NV , No } -\cs_generate_variant:Nn \prop_gpop:NnN { c , cV , co } % \end{macrocode} +% For a linked prop we must work a bit: \cs{@@_get_linked:w} is followed by +% the expansion of the prop, then by four brace groups: the key~|#4|, the +% assignment code~|#5|, \meta{true code}~|#6|, and \meta{false code}~|#7|. If +% the key is present, its value is stored in the token list +% \cs[no-index]{@@_}|#2~#4|. If that token list exists, +% \cs{@@_get_linked_aux:w} gets called followed by the expansion of that token +% list and we grab as~|#2| the value associated to that key, which we feed to +% the assignment code and follow-up code. If the key is absent the token list +% can be \tn{undefined} or \tn{relax}. In both cases \cs{@@_get_linked_aux:w} +% finds an empty brace group as~|#2|, \cs{use_none:n} as~|#4| and the +% \meta{false code} as~|#5|. +% Note that we made \cs{@@_get_linked:w} and subsequent auxiliaries +% expandable, because they are also used in \cs{prop_item:Nn}. +% \begin{macrocode} +\cs_new:Npn \@@_get_linked:w + \@@_flatten:w #1 \s_@@ #2#3#4#5#6#7 + { + \if_cs_exist:w @@ ~ #2 ~ \tl_to_str:n {#4} \cs_end: + \exp_after:wN \exp_after:wN \exp_after:wN \@@_get_linked_aux:w + \cs:w @@ ~ #2 ~ \tl_to_str:n {#4} \exp_after:wN \cs_end: + \else: + \exp_after:wN \@@_get_linked_aux:w + \fi: + \s_@@_mark {#5} {#6} + \s_@@ { } \s_@@_mark \use_none:n {#7} + \s_@@_stop + } +\cs_new:Npn \@@_get_linked_aux:w + #1 \s_@@ #2 #3 \s_@@_mark #4 #5 #6 \s_@@_stop { #4 {#2} #5 } +% \end{macrocode} +% \end{macro} +% \end{macro} % \end{macro} % \end{macro} % @@ -1103,7 +1655,7 @@ % \prop_item:cn, \prop_item:cV, \prop_item:co, \prop_item:ce % } % \begin{macro}[EXP]{\@@_item:nnn} -% Getting the value corresponding to a key in a property list in an +% Getting the value corresponding to a key in a flat property list in an % expandable fashion simply uses \cs{prop_map_tokens:Nn} to go through % the property list. The auxiliary \cs{@@_item:nnn} receives the % search string~|#1|, the key~|#2| and the value~|#3| and returns as @@ -1111,8 +1663,15 @@ % \begin{macrocode} \cs_new:Npn \prop_item:Nn #1#2 { - \exp_args:NNo \prop_map_tokens:Nn #1 - { \exp_after:wN \@@_item:nnn \exp_after:wN { \tl_to_str:n {#2} } } + \@@_if_flat:NTF #1 + { + \exp_args:NNo \prop_map_tokens:Nn #1 + { + \exp_after:wN \@@_item:nnn + \exp_after:wN { \tl_to_str:n {#2} } + } + } + { \exp_after:wN \@@_get_linked:w #1 {#2} \use:n { } { } } } \cs_new:Npn \@@_item:nnn #1#2#3 { @@ -1124,161 +1683,259 @@ % \end{macro} % \end{macro} % -% \begin{macro}[EXP]{\prop_count:N, \prop_count:c} -% \begin{macro}[EXP]{\@@_count:nn} -% Counting the key--value pairs in a property list is done using the -% same approach as for other count functions: turn each entry into a -% \texttt{+1} then use integer evaluation to actually do the -% mathematics. +% \subsection{Removing data from property lists} +% +% \begin{macro} +% { +% \@@_pop:NnNNnTF, +% \@@_pop_linked:wnNNnTF, \@@_pop_linked:NNNn, +% \@@_pop_linked:w, +% \@@_pop_linked_prev:w, \@@_pop_linked_next:w +% } +% This auxiliary is used by both the \cs[no-index]{prop_pop} family and +% the \cs[no-index]{prop_remove} family of functions. +% It receives a \meta{prop} and a \Arg{key}, three assignment functions +% (\cs{tl_set:Nn} \cs{cs_set_eq:NN} \cs{cs_set_nopar:Npe} or their global +% versions), then \Arg{code} \Arg{true code} \Arg{false code}. +% +% For a flat prop, split it. If the \meta{key} is there, reconstruct the rest +% of the prop from the two extracts |##2| |##4| and assign using +% \cs[index=tl_set:Nn]{tl_(g)set:Nn}, then run \meta{code} \Arg{value} with +% the \meta{value} found, and run the \meta{true code}. If the \meta{key} is +% absent, run the \meta{false code}. +% +% For a linked prop, the removal is done by \cs{@@_pop_linked:wnNNnTF}, which +% removes the key--value pair from the doubly-linked list and runs its last +% three arguments \Arg{code} \Arg{true code} \Arg{false code} depending on +% whether the key--value is found, in the same way as for flat props. % \begin{macrocode} -\cs_new:Npn \prop_count:N #1 +\cs_new_protected:Npn \@@_pop:NnNNnTF #1#2#3#4#5#6#7 { - \int_eval:n + \@@_split:NnTFn #1 {#2} { - 0 - \prop_map_function:NN #1 \@@_count:nn + #4 #1 { \exp_not:n { \s_@@ \@@_chk:w ##2 ##4 } } + #5 {##3} + #6 + } + {#7} + { + \exp_after:wN \@@_pop_linked:wnNNnTF #1 {#2} + #3 #4 {#5} {#6} {#7} } } -\cs_new:Npn \@@_count:nn #1#2 { + 1 } -\cs_generate_variant:Nn \prop_count:N { c } % \end{macrocode} -% \end{macro} +% The next auxiliary \cs{@@_pop_linked:wnNNnTF}, together with the |NNNn| +% auxiliary, checks if the key is present in the \meta{linked prop}, then the +% corresponding value (if present) is passed as a braced argument to the +% \meta{code} and the \meta{true code} or \meta{false code} is run as +% appropriate. Before that, there are also three assignments: the token lists +% for the previous key and next key are made to point to each other, cf.\ +% \cs{@@_pop_linked:w}, and the token list for the given key is made +% undefined. +% \begin{macrocode} +\cs_new_protected:Npn \@@_pop_linked:wnNNnTF + \@@_flatten:w #1 \s_@@ #2#3#4#5#6#7 + { + \if_cs_exist:w @@ ~ #2 ~ \tl_to_str:n {#4} \cs_end: + \exp_after:wN \@@_pop_linked:NNNn + \cs:w @@ ~ #2 ~ \tl_to_str:n {#4} \cs_end: + #5 #6 {#7} + \else: + \exp_after:wN \use_iii:nnn + \fi: + \use_i:nn + } +\cs_new_protected:Npn \@@_pop_linked:NNNn #1#2#3#4 + { + \if_meaning:w \scan_stop: #1 + \exp_after:wN \exp_after:wN \exp_after:wN \use_iii:nnn + \else: + \exp_after:wN \@@_pop_linked:w #1 #1 #2 #3 {#4} + \fi: + } +\cs_new_protected:Npn \@@_pop_linked:w + \use_none:n #1#2 \s_@@ #3#4#5#6#7#8 + { + #6 #5 \tex_undefined:D + #7 #1 + { + \exp_after:wN \@@_pop_linked_prev:w #1 + \exp_not:N #4 + } + #7 #4 + { + \exp_not:n { \use_none:n #1 } + \exp_not:f { \exp_after:wN \@@_pop_linked_next:w #4 } + } + #8 {#3} + } +\cs_new:Npn \@@_pop_linked_prev:w #1 \s_@@ #2#3 + { \exp_not:n { #1 \s_@@ {#2} } } +\cs_new:Npn \@@_pop_linked_next:w \use_none:n #1 { \exp_stop_f: } +% \end{macrocode} % \end{macro} % -% \begin{macro}[EXP]{\prop_to_keyval:N} -% \begin{macro}[EXP] -% {\@@_to_keyval_exp_after:wN, \@@_to_keyval:nn, \@@_to_keyval:nnw} -% Each property name and value pair will be returned in the form -% \verb*| |\marg{name}\verb*|= |\marg{value}. As one of the main use cases for -% this macro is to pass the \meta{property list} on to a key--value parser, we -% have to make sure that the behaviour is as good as possible. Using a space -% before the opening brace we get the correct brace stripping behaviour for -% most of the key--value parsers available in \LaTeX. -% Iterate over the -% \meta{property list} and remove the leading comma afterwards. Only the value -% has to be protected in \cs{__kernel_exp_not:w} as the property name is -% always a string. After the loop the leading comma is removed by -% \cs{use_none:n} and afterwards \cs{__kernel_exp_not:w} eventually finds the -% opening brace of its argument. +% \begin{macro}[tested = m3prop002] +% { +% \prop_remove:Nn, \prop_remove:NV, \prop_remove:Ne, +% \prop_remove:cn, \prop_remove:cV, \prop_remove:ce +% } +% \begin{macro}[tested = m3prop002] +% { +% \prop_gremove:Nn, \prop_gremove:NV, \prop_gremove:Ne, +% \prop_gremove:cn, \prop_gremove:cV, \prop_gremove:ce +% } +% Deleting from a property relies on \cs{@@_pop:NnNNnTF}. The three +% assignment functions are suitably local or global. The last three arguments +% are \cs{use_none:n} and two empty brace groups: if the key is found we get +% \cs{use_none:n} \Arg{key} \meta{empty}, which expands to nothing, and +% otherwise we just get \meta{empty}. The auxiliary takes care of actually +% removing the entry from the prop. % \begin{macrocode} -\cs_new:Npn \prop_to_keyval:N #1 +\cs_new_protected:Npn \prop_remove:Nn #1#2 { - \__kernel_exp_not:w - \prop_if_empty:NTF #1 - { {} } - { - \exp_after:wN \exp_after:wN \exp_after:wN - { - \tex_expanded:D - { - \__kernel_exp_not:w { \use_none:n } - \prop_map_function:NN #1 \@@_to_keyval:nn - } - } - } + \@@_pop:NnNNnTF #1 {#2} + \cs_set_eq:NN \cs_set_nopar:Npe + \use_none:n { } { } } -\cs_new:Npn \@@_to_keyval:nn #1#2 - { , ~ {#1} =~ { \__kernel_exp_not:w {#2} } } +\cs_new_protected:Npn \prop_gremove:Nn #1#2 + { + \@@_pop:NnNNnTF #1 {#2} + \cs_gset_eq:NN \cs_gset_nopar:Npe + \use_none:n { } { } + } +\cs_generate_variant:Nn \prop_remove:Nn { NV , Ne , c , cV , ce } +\cs_generate_variant:Nn \prop_gremove:Nn { NV , Ne , c , cV , ce } % \end{macrocode} % \end{macro} % \end{macro} % +% \begin{macro}[tested = m3prop002] +% { +% \prop_pop:NnN, \prop_pop:NVN, \prop_pop:NoN, +% \prop_pop:cnN, \prop_pop:cVN, \prop_pop:coN, +% \prop_gpop:NnN, \prop_gpop:NVN, \prop_gpop:NoN, +% \prop_gpop:cnN, \prop_gpop:cVN, \prop_gpop:coN, +% } % \begin{macro}[TF, tested = m3prop004] % { -% \prop_pop:NnN, \prop_pop:NVN, -% \prop_pop:cnN, \prop_pop:cVN, -% \prop_gpop:NnN, \prop_gpop:NVN, -% \prop_gpop:NoN, -% \prop_gpop:cnN, \prop_gpop:cVN, +% \prop_pop:NnN, \prop_pop:NVN, \prop_pop:NoN, +% \prop_pop:cnN, \prop_pop:cVN, \prop_pop:coN, +% \prop_gpop:NnN, \prop_gpop:NVN, \prop_gpop:NoN, +% \prop_gpop:cnN, \prop_gpop:cVN, \prop_gpop:coN, % } -% Popping an item from a property list, keeping track of whether -% the key was present or not, is implemented as a conditional. -% If the key was missing, neither the property list, nor the token -% list are altered. Otherwise, \cs{prg_return_true:} is used after -% the assignments. +% Popping a value is almost the same, but the value found is kept. +% For the non-branching version, we additionally set the target token list to +% \cs{q_no_value}, while for the branching version we must produce +% \cs{prg_return_true:} or \cs{prg_return_false:}. % \begin{macrocode} +\cs_new_protected:Npn \prop_pop:NnN #1#2#3 + { + \@@_pop:NnNNnTF #1 {#2} + \cs_set_eq:NN \cs_set_nopar:Npe + { \tl_set:Nn #3 } { } { \tl_set:Nn #3 { \q_no_value } } + } +\cs_new_protected:Npn \prop_gpop:NnN #1#2#3 + { + \@@_pop:NnNNnTF #1 {#2} + \cs_gset_eq:NN \cs_gset_nopar:Npe + { \tl_set:Nn #3 } { } { \tl_set:Nn #3 { \q_no_value } } + } +\cs_generate_variant:Nn \prop_pop:NnN { NV , No } +\cs_generate_variant:Nn \prop_pop:NnN { c , cV , co } +\cs_generate_variant:Nn \prop_gpop:NnN { NV , No } +\cs_generate_variant:Nn \prop_gpop:NnN { c , cV , co } \prg_new_protected_conditional:Npnn \prop_pop:NnN #1#2#3 { T , F , TF } { - \@@_split:NnTF #1 {#2} - { - \tl_set:Nn #3 {##2} - \tl_set:Nn #1 { ##1 ##3 } - \prg_return_true: - } - { \prg_return_false: } + \@@_pop:NnNNnTF #1 {#2} + \cs_set_eq:NN \cs_set_nopar:Npe + { \tl_set:Nn #3 } \prg_return_true: \prg_return_false: } \prg_new_protected_conditional:Npnn \prop_gpop:NnN #1#2#3 { T , F , TF } { - \@@_split:NnTF #1 {#2} - { - \tl_set:Nn #3 {##2} - \tl_gset:Nn #1 { ##1 ##3 } - \prg_return_true: - } - { \prg_return_false: } + \@@_pop:NnNNnTF #1 {#2} + \cs_gset_eq:NN \cs_gset_nopar:Npe + { \tl_set:Nn #3 } \prg_return_true: \prg_return_false: } -\prg_generate_conditional_variant:Nnn \prop_pop:NnN { NV , c , cV } { T , F , TF } -\prg_generate_conditional_variant:Nnn \prop_gpop:NnN { NV , c , cV } { T , F , TF } +\prg_generate_conditional_variant:Nnn \prop_pop:NnN + { NV , No , c , cV , co } { T , F , TF } +\prg_generate_conditional_variant:Nnn \prop_gpop:NnN + { NV , No , c , cV , co } { T , F , TF } % \end{macrocode} % \end{macro} +% \end{macro} +% +% \subsection{Adding data to property lists} % % \begin{macro}[tested = m3prop002] % { % \prop_put:Nnn, \prop_put:NnV, \prop_put:Nnv, \prop_put:Nne, % \prop_put:NVn, \prop_put:NVV, \prop_put:NVv, \prop_put:NVe, -% \prop_put:Nvn, \prop_put:NvV, \prop_put:Nvv, \prop_put:Nve, +% \prop_put:Nvn, \prop_put:NvV, \prop_put:Nvv, \prop_put:Nve, % \prop_put:Nen, \prop_put:NeV, \prop_put:Nev, \prop_put:Nee, -% \prop_put:Nno, \prop_put:Nnx, \prop_put:NVx, -% \prop_put:Non, \prop_put:Noo, \prop_put:NxV, \prop_put:Nxx, +% \prop_put:Nno, \prop_put:Non, \prop_put:Noo, +% \prop_put:Nnx, \prop_put:NVx, \prop_put:NxV, \prop_put:Nxx, % \prop_put:cnn, \prop_put:cnV, \prop_put:cnv, \prop_put:cne, -% \prop_put:cno, \prop_put:cnx, % \prop_put:cVn, \prop_put:cVV, \prop_put:cVv, \prop_put:cVe, -% \prop_put:cvn, \prop_put:cvV, \prop_put:cvv, \prop_put:cve, +% \prop_put:cvn, \prop_put:cvV, \prop_put:cvv, \prop_put:cve, % \prop_put:cen, \prop_put:ceV, \prop_put:cev, \prop_put:cee, -% \prop_put:con, \prop_put:coo, \prop_put:cxV, \prop_put:cxx +% \prop_put:cno, \prop_put:con, \prop_put:coo, +% \prop_put:cnx, \prop_put:cVx, \prop_put:cxV, \prop_put:cxx % } % \begin{macro}[tested = m3prop002] % { % \prop_gput:Nnn, \prop_gput:NnV, \prop_gput:Nnv, \prop_gput:Nne, % \prop_gput:NVn, \prop_gput:NVV, \prop_gput:NVv, \prop_gput:NVe, -% \prop_gput:Nvn, \prop_gput:NvV, \prop_gput:Nvv, \prop_gput:Nve, +% \prop_gput:Nvn, \prop_gput:NvV, \prop_gput:Nvv, \prop_gput:Nve, % \prop_gput:Nen, \prop_gput:NeV, \prop_gput:Nev, \prop_gput:Nee, % \prop_gput:Nno, \prop_gput:Non, \prop_gput:Noo, -% \prop_gput:Nnx, \prop_gput:NVx, \prop_gput:Nxn, \prop_gput:Nxx, +% \prop_gput:Nnx, \prop_gput:NVx, \prop_gput:NxV, \prop_gput:Nxx, % \prop_gput:cnn, \prop_gput:cnV, \prop_gput:cnv, \prop_gput:cne, -% \prop_gput:cno, \prop_gput:cnx, % \prop_gput:cVn, \prop_gput:cVV, \prop_gput:cVv, \prop_gput:cVe, -% \prop_gput:cvn, \prop_gput:cvV, \prop_gput:cvv, \prop_gput:cve, +% \prop_gput:cvn, \prop_gput:cvV, \prop_gput:cvv, \prop_gput:cve, % \prop_gput:cen, \prop_gput:ceV, \prop_gput:cev, \prop_gput:cee, -% \prop_gput:cno, \prop_gput:con, \prop_gput:cxn, \prop_gput:cxx +% \prop_gput:cno, \prop_gput:con, \prop_gput:coo, +% \prop_gput:cnx, \prop_gput:cVx, \prop_gput:cxV, \prop_gput:cxx +% } +% \begin{macro}[tested = m3prop002] +% { +% \prop_put_if_new:Nnn, \prop_put_if_new:NVn, \prop_put_if_new:NnV, +% \prop_put_if_new:cnn, \prop_put_if_new:cVn, \prop_put_if_new:cnV, +% \prop_gput_if_new:Nnn, \prop_gput_if_new:NVn, \prop_gput_if_new:NnV, +% \prop_gput_if_new:cnn, \prop_gput_if_new:cVn, \prop_gput_if_new:cnV % } -% \begin{macro}{\@@_put:NNnn} -% Since the branches of \cs{@@_split:NnTF} are used as the replacement -% text of an internal macro, and since the \meta{key} and new -% \meta{value} may contain arbitrary tokens, it is not safe to include -% them in the argument of \cs{@@_split:NnTF}. We thus start by -% storing in \cs{l_@@_internal_tl} tokens which (after -% \texttt{e}-expansion) encode the key--value pair. This variable can -% safely be used in \cs{@@_split:NnTF}. If the \meta{key} was absent, -% append the new key--value to the list. -% Otherwise concatenate the extracts |##1| -% and |##3| with the new key--value pair \cs{l_@@_internal_tl}. The -% updated entry is placed at the same spot as the original \meta{key} -% in the property list, preserving the order of entries. +% \begin{macro}[tested = m3prop002] +% { +% \@@_put:NNNnn, \@@_put_linked:wnNN, +% \@@_put_linked:NNNN, \@@_put_linked_old:w, +% \@@_put_linked_new:w +% } +% All of the \cs[no-index]{prop_(g)put(_if_new):Nnn} functions are +% based on the same auxiliary, which receives \meta{code} and an +% \enquote{assignment}, followed by \meta{prop} \Arg{key} \Arg{new +% value}. The +% assignment \cs[index=cs_set_nopar:Npe]{cs_(g)set_nopar:Npe} is the +% primitive assignment without any checking: in the case of linked +% props it is applied to individual +% pieces of the linked prop, which are typically not yet defined. +% Debugging the scope of the variable is done at a higher level by +% letting \pkg{l3debug} change \cs{prop_put:Nnn} and friends. This +% allows other \pkg{l3prop} commands to directly call the underlying +% auxiliary to skip this checking step and avoid getting multiple +% error messages for the same error. +% The \meta{code} (empty for |put| and \cs{use_none:nnn} for +% |put_if_new|) is placed before the assignment in cases where the key +% is already present, in order to suppress the assignment in the +% |put_if_new| case. % \begin{macrocode} -\cs_new_protected:Npn \prop_put:Nnn { \@@_put:NNnn \__kernel_tl_set:Nx } -\cs_new_protected:Npn \prop_gput:Nnn { \@@_put:NNnn \__kernel_tl_gset:Nx } -\cs_new_protected:Npn \@@_put:NNnn #1#2#3#4 - { - \tl_set:Nn \l_@@_internal_tl - { - \exp_not:N \@@_pair:wn \tl_to_str:n {#3} - \s_@@ { \exp_not:n {#4} } - } - \@@_split:NnTF #2 {#3} - { #1 #2 { \exp_not:n {##1} \l_@@_internal_tl \exp_not:n {##3} } } - { #1 #2 { \exp_not:o {#2} \l_@@_internal_tl } } - } +\cs_new_protected:Npn \prop_put:Nnn + { \@@_put:nNNnn { } \cs_set_nopar:Npe } +\cs_new_protected:Npn \prop_gput:Nnn + { \@@_put:nNNnn { } \cs_gset_nopar:Npe } +\cs_new_protected:Npn \prop_put_if_new:Nnn + { \@@_put:nNNnn \use_none:nnn \cs_set_nopar:Npe } +\cs_new_protected:Npn \prop_gput_if_new:Nnn + { \@@_put:nNNnn \use_none:nnn \cs_gset_nopar:Npe } \cs_generate_variant:Nn \prop_put:Nnn { NnV , Nnv , Nne , NV , NVV , NVv , NVe , @@ -1307,50 +1964,104 @@ } \cs_generate_variant:Nn \prop_gput:Nnn { cno , co , coo , cnx , cVx , cxV , cxx } +\cs_generate_variant:Nn \prop_put_if_new:Nnn + { NnV , NV , cnV , cV } +\cs_generate_variant:Nn \prop_gput_if_new:Nnn + { NnV , NV , cnV , cV } % \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}[tested = m3prop002] -% { -% \prop_put_if_new:Nnn, \prop_put_if_new:NVn, \prop_put_if_new:NnV, -% \prop_put_if_new:cnn, \prop_put_if_new:cVn, \prop_put_if_new:cnV -% } -% \begin{macro}[tested = m3prop002] -% { -% \prop_gput_if_new:Nnn, \prop_gput_if_new:NVn, \prop_gput_if_new:NnV, -% \prop_gput_if_new:cnn, \prop_gput_if_new:cVn, \prop_gput_if_new:cnV -% } -% \begin{macro}{\@@_put_if_new:NNnn} -% Adding conditionally also splits. If the key is already present, -% the three brace groups given by \cs{@@_split:NnTF} are removed. -% If the key is new, then the value is added, being careful to -% convert the key to a string using \cs{tl_to_str:n}. +% Since the true branch of \cs{@@_split:NnTFn} is used as the +% replacement text of an internal macro, and since the \meta{key} and +% new \meta{value} may contain arbitrary tokens, it is not safe to +% include them in the argument of \cs{@@_split:NnTFn}. We thus start +% by storing in \cs{l_@@_internal_tl} tokens which (after +% \texttt{x}-expansion) encode the key--value pair. This variable can +% safely be used in \cs{@@_split:NnTFn}. For a flat prop, if the +% \meta{key} was absent, append the new key--value to the list; +% otherwise concatenate the extracts |##2| and |##4| with the new +% key--value pair \cs{l_@@_internal_tl}. The updated entry is placed +% at the same spot as the original \meta{key} in the property list, +% preserving the order of entries. For a linked prop, call +% \cs{@@_put_linked:wnNN}, which constructs the control sequence in +% which we will place the new value. If it matches \cs{scan_stop:} +% then the key was not yet there and we add it using +% \cs{@@_put_linked_new:w}, otherwise it was already there and we use +% \cs{@@_put_linked_old:w}. % \begin{macrocode} -\cs_new_protected:Npn \prop_put_if_new:Nnn - { \@@_put_if_new:NNnn \__kernel_tl_set:Nx } -\cs_new_protected:Npn \prop_gput_if_new:Nnn - { \@@_put_if_new:NNnn \__kernel_tl_gset:Nx } -\cs_new_protected:Npn \@@_put_if_new:NNnn #1#2#3#4 +\cs_new_protected:Npn \@@_put:nNNnn #1#2#3#4#5 { \tl_set:Nn \l_@@_internal_tl { - \exp_not:N \@@_pair:wn \tl_to_str:n {#3} - \s_@@ \exp_not:n { {#4} } + \exp_not:N \@@_pair:wn \tl_to_str:n {#4} + \s_@@ { \exp_not:n {#5} } + } + \@@_split:NnTFn #3 {#4} + { + #1 #2 #3 + { + \s_@@ \@@_chk:w \exp_not:n {##2} + \l_@@_internal_tl \exp_not:n {##4} + } + } + { #2 #3 { \exp_not:o {#3} \l_@@_internal_tl } } + { \exp_after:wN \@@_put_linked:wnnN #3 {#4} {#1} #2 } + } +\cs_new_protected:Npn \@@_put_linked:wnnN + \@@_flatten:w #1 \s_@@ #2#3#4 + { + \exp_after:wN \@@_put_linked:NNnN + \cs:w @@ ~ #2 ~ \tl_to_str:n {#4} \cs_end: + #1 + } +\cs_new_protected:Npn \@@_put_linked:NNnN #1#2#3#4 + { + \if_meaning:w \scan_stop: #1 + \exp_after:wN \@@_put_linked_new:w #2 #1 #2 #4 + \else: + \exp_after:wN \@@_put_linked_old:w #1 { #3 #4 #1 } + \fi: + } +% \end{macrocode} +% To add a new entry, \cs{@@_put_linked_new:w} receives the expansion +% of the end-pointer, namely \cs{use_none:n} \meta{last key pointer}, +% followed by the new key pointer~|#2|, the end pointer~|#3|, and an +% assignment function~|#4|. Set up the doubly-linked list in the +% order |#1|, |#2|, |#3|, placing the key--value pair +% \cs{l_@@_internal_tl} in~|#2|. To replace an old entry, +% \cs{@@_put_linked_old:w} receives the expansion of that entry, and +% it reassigns it (|#5|) using the assignment~|#6|, by simply +% replacing the payload |#2| \cs{s_@@} |#3| by \cs{l_@@_internal_tl}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_put_linked_new:w + \use_none:n #1#2#3#4 + { + #4 #1 + { + \exp_after:wN \@@_pop_linked_prev:w #1 + \exp_not:N #2 + } + #4 #2 + { + \exp_not:n { \use_none:n #1 } + \l_@@_internal_tl + \exp_not:N #3 + } + #4 #3 { \exp_not:n { \use_none:n #2 } } + } +\cs_new_protected:Npn \@@_put_linked_old:w + \use_none:n #1#2 \s_@@ #3#4#5 + { + #5 + { + \exp_not:n { \use_none:n #1 } + \l_@@_internal_tl + \exp_not:N #4 } - \@@_split:NnTF #2 {#3} - { } - { #1 #2 { \exp_not:o {#2} \l_@@_internal_tl } } } -\cs_generate_variant:Nn \prop_put_if_new:Nnn - { NnV , NV , cnV , cV } -\cs_generate_variant:Nn \prop_gput_if_new:Nnn - { NnV , NV , cnV , cV } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} +% \end{macro} % % \subsection{Property list conditionals} % @@ -1365,46 +2076,82 @@ % \end{macro} % % \begin{macro}[pTF, tested = m3prop003]{\prop_if_empty:N, \prop_if_empty:c} -% Same test as for token lists. +% \begin{macro}{\@@_if_empty_return:w} +% A flat property list is empty if it matches \cs{c_empty_prop}. A linked +% property list is empty if its second token (the end pointer) and last token +% (the first key pointer) are equal. There cannot be false positives because +% the end pointer takes the form \cs{use_none:n} \meta{pointer} while the +% other pointers have more elaborate structure. The subtle code branch here +% is when a non-empty flat property list is given: then \cs{@@_if_empty:w} +% reads the whole property list as~|#1| and |#2|, |#3|, |#4| are |2|, |3|, +% |4|, respectively. % \begin{macrocode} \prg_new_conditional:Npnn \prop_if_empty:N #1 { p , T , F , TF } { - \tl_if_eq:NNTF #1 \c_empty_prop - \prg_return_true: \prg_return_false: + \if_meaning:w #1 \c_empty_prop + \prg_return_true: + \else: + \exp_after:wN \@@_if_empty_return:w #1 + \@@_flatten:w 2 \s_@@ 34 \s_@@_stop + \fi: + } +\cs_new:Npn \@@_if_empty_return:w + #1 \@@_flatten:w #2 \s_@@ #3#4#5 \s_@@_stop + { + \if_meaning:w #2 #4 + \prg_return_true: + \else: + \prg_return_false: + \fi: } \prg_generate_conditional_variant:Nnn \prop_if_empty:N { c } { p , T , F , TF } % \end{macrocode} % \end{macro} +% \end{macro} % % \begin{macro}[pTF, tested = m3prop003] % { % \prop_if_in:Nn, \prop_if_in:NV, \prop_if_in:Ne, \prop_if_in:No, % \prop_if_in:cn, \prop_if_in:cV, \prop_if_in:ce, \prop_if_in:co % } -% \begin{macro}[EXP]{\@@_if_in:nnn} -% Testing expandably if a key is in a property list -% requires to go through the key--value pairs one by one. -% This is rather slow, and a faster test would be +% \begin{macro}[EXP]{\@@_if_in_flat:nnn} +% For a linked prop, use \cs{@@_get_linked:w} to look up whether the control +% sequence constructed from the prefix and the sought-after key exists; this +% auxiliary calls \cs{use_none:n} \Arg{value} \cs{prg_return_true:} if the key +% is found, and otherwise \cs{prg_return_false:}. For a flat prop, testing +% expandably if a key is there requires to go through the key--value pairs one +% by one. This is rather slow, and a faster test would be % \begin{verbatim} % \prg_new_protected_conditional:Npnn \prop_if_in:Nn #1 #2 % { -% \@@_split:NnTF #1 {#2} +% \@@_split:NnTFn #1 {#2} % { \prg_return_true: } % { \prg_return_false: } +% { ... } % } % \end{verbatim} -% but \cs{@@_split:NnTF} is non-expandable. +% but \cs{@@_split:NnTFn} is non-expandable. % Instead, we use \cs{prop_map_tokens:Nn} to compare the search key to % each key in turn using \cs{str_if_eq:ee}, which is expandable. % \begin{macrocode} \prg_new_conditional:Npnn \prop_if_in:Nn #1#2 { p , T , F , TF } { - \exp_args:NNo \prop_map_tokens:Nn #1 - { \exp_after:wN \@@_if_in:nnn \exp_after:wN { \tl_to_str:n {#2} } } - \prg_return_false: + \@@_if_flat:NTF #1 + { + \exp_after:wN \prop_map_tokens:Nn \exp_after:wN #1 + { + \exp_after:wN \@@_if_in_flat:nnn + \exp_after:wN { \tl_to_str:n {#2} } + } + \prg_return_false: + } + { + \exp_after:wN \@@_get_linked:w #1 {#2} + \use_none:n \prg_return_true: \prg_return_false: + } } -\cs_new:Npn \@@_if_in:nnn #1#2#3 +\cs_new:Npn \@@_if_in_flat:nnn #1#2#3 { \str_if_eq:eeT {#1} {#2} { \prop_map_break:n { \use_i:nn \prg_return_true: } } @@ -1415,38 +2162,6 @@ % \end{macro} % \end{macro} % -% \subsection{Recovering values from property lists with branching} -% -% \begin{macro}[TF, tested = m3prop004] -% { -% \prop_get:NnN, \prop_get:NVN, \prop_get:NvN, \prop_get:NeN, -% \prop_get:NoN, \prop_get:NxN, -% \prop_get:cnN, \prop_get:cVN, \prop_get:cvN, \prop_get:ceN, -% \prop_get:coN, \prop_get:cxN, -% \prop_get:cnc -% } -% Getting the value corresponding to a key, keeping track of whether -% the key was present or not, is implemented as a conditional (with -% side effects). If the key was absent, the token list is not altered. -% \begin{macrocode} -\prg_new_protected_conditional:Npnn \prop_get:NnN #1#2#3 { T , F , TF } - { - \@@_split:NnTF #1 {#2} - { - \tl_set:Nn #3 {##2} - \prg_return_true: - } - { \prg_return_false: } - } -\prg_generate_conditional_variant:Nnn \prop_get:NnN - { NV , Nv , Ne , c , cV , cv , ce } { T , F , TF } -\prg_generate_conditional_variant:Nnn \prop_get:NnN - { No , Nx , co , cx } { T , F , TF } -\prg_generate_conditional_variant:Nnn \prop_get:NnN - { cnc } { T , F , TF } -% \end{macrocode} -% \end{macro} -% % \subsection{Mapping over property lists} % % \begin{macro}[tested = m3prop003] @@ -1455,6 +2170,9 @@ % \prop_map_function:cN, \prop_map_function:cc % } % \begin{macro}{\@@_map_function:Nw} +% We first |f|-expand to flatten~|#1| in case it was a linked list. +% The \cs{use_i:nnn} removes the leading \cs{s_@@} \cs{@@_chk:w} of +% the flattened prop. % The even-numbered arguments of \cs{@@_map_function:Nw} are keys, % hence have string catcodes, except at the end where they are % \cs{fi:} \cs{prop_map_break:}. The \cs{fi:} ends the \cs{if_false:} @@ -1463,10 +2181,8 @@ % \begin{macrocode} \cs_new:Npn \prop_map_function:NN #1#2 { - \exp_after:wN \use_i_ii:nnn - \exp_after:wN \@@_map_function:Nw - \exp_after:wN #2 - #1 + \exp_last_unbraced:Nnf + \use_i:nnn { \@@_map_function:Nw #2 } #1 \@@_pair:wn \fi: \prop_map_break: \s_@@ { } \@@_pair:wn \fi: \prop_map_break: \s_@@ { } \@@_pair:wn \fi: \prop_map_break: \s_@@ { } @@ -1506,7 +2222,7 @@ { @@_map_ \int_use:N \g__kernel_prg_map_int :wn } \@@_pair:wn \int_gincr:N \g__kernel_prg_map_int \cs_gset_protected:Npn \@@_pair:wn ##1 \s_@@ ##2 {#2} - #1 + \exp_last_unbraced:Nf \use_none:nn #1 \prg_break_point:Nn \prop_map_break: { \int_gdecr:N \g__kernel_prg_map_int @@ -1520,8 +2236,8 @@ % % \begin{macro}[rEXP]{\prop_map_tokens:Nn, \prop_map_tokens:cn} % \begin{macro}{\@@_map_tokens:nw} -% The mapping is very similar to \cs{prop_map_function:NN}. The -% \cs{use_i:nn} removes the leading \cs{s_@@}. The odd construction +% The mapping is very similar to \cs{prop_map_function:NN}. +% The odd construction % |\use:n {#1}| allows |#1| to contain any token without interfering % with \cs{prop_map_break:}. The loop stops when the \meta{key} % between \cs{@@_pair:wn} and \cs{s_@@} is \cs{fi:} @@ -1529,8 +2245,8 @@ % \begin{macrocode} \cs_new:Npn \prop_map_tokens:Nn #1#2 { - \exp_last_unbraced:Nno - \use_i:nn { \@@_map_tokens:nw {#2} } #1 + \exp_last_unbraced:Nnf + \use_i:nnn { \@@_map_tokens:nw {#2} } #1 \@@_pair:wn \fi: \prop_map_break: \s_@@ { } \@@_pair:wn \fi: \prop_map_break: \s_@@ { } \@@_pair:wn \fi: \prop_map_break: \s_@@ { } @@ -1567,16 +2283,98 @@ % \end{macro} % \end{macro} % +% \subsection{Uses of mapping over property lists} +% +% \begin{macro}[EXP]{\prop_count:N, \prop_count:c} +% \begin{macro}[EXP]{\@@_count:nn} +% Counting the key--value pairs in a property list is done using the +% same approach as for other count functions: turn each entry into a +% \texttt{+1} then use integer evaluation to actually do the +% mathematics. +% \begin{macrocode} +\cs_new:Npn \prop_count:N #1 + { + \int_eval:n + { + 0 + \prop_map_function:NN #1 \@@_count:nn + } + } +\cs_new:Npn \@@_count:nn #1#2 { + 1 } +\cs_generate_variant:Nn \prop_count:N { c } +% \end{macrocode} +% \end{macro} +% \end{macro} +% +% \begin{macro}[EXP]{\prop_to_keyval:N} +% \begin{macro}[EXP] +% {\@@_to_keyval_exp_after:wN, \@@_to_keyval:nn, \@@_to_keyval:nnw} +% Each property name and value pair will be returned in the form +% \verb*| |\marg{name}\verb*|= |\marg{value}. As one of the main use cases for +% this macro is to pass the \meta{property list} on to a key--value parser, we +% have to make sure that the behaviour is as good as possible. Using a space +% before the opening brace we get the correct brace stripping behaviour for +% most of the key--value parsers available in \LaTeX{}. +% Iterate over the +% \meta{property list} and remove the leading comma afterwards. Only the value +% has to be protected in \cs{__kernel_exp_not:w} as the property name is +% always a string. After the loop the leading comma is removed by +% \cs{use_none:n} and afterwards \cs{__kernel_exp_not:w} eventually finds the +% opening brace of its argument. +% \begin{macrocode} +\cs_new:Npn \prop_to_keyval:N #1 + { + \__kernel_exp_not:w + \prop_if_empty:NTF #1 + { {} } + { + \exp_after:wN \exp_after:wN \exp_after:wN + { + \tex_expanded:D + { + \exp_not:N \use_none:n + \prop_map_function:NN #1 \@@_to_keyval:nn + } + } + } + } +\cs_new:Npn \@@_to_keyval:nn #1#2 + { , ~ {#1} =~ { \__kernel_exp_not:w {#2} } } +% \end{macrocode} +% \end{macro} +% \end{macro} +% % \subsection{Viewing property lists} % % \begin{macro}[tested = m3show001] % {\prop_show:N, \prop_show:c, \prop_log:N, \prop_log:c} -% \begin{macro}{\@@_show:NN} -% \begin{macro}[rEXP]{\@@_show_validate:w} -% Apply the general \cs{__kernel_chk_tl_type:NnnT}. -% Contrarily to sequences and comma lists, -% we use \cs{msg_show_item:nn} to format both the key and the value -% for each pair. +% \begin{macro}{\@@_show:NN, \@@_show_finally:NNn, \@@_show_prepare:w, \@@_show_loop:NNw, \@@_show_bad_name:NNN, \@@_show_end:NNN, \@@_show_loop_key:wNNN} +% \begin{macro}[rEXP]{\@@_show_flat:w, \@@_show_linked:w} +% Experience shows one source of problems is very hard to debug: when +% a data structure such as a |seq| or~|prop| gets corrupted. In the +% past, \cs{prop_show:N} would in some cases happily show items of +% such a |prop|, even though other more demanding \pkg{l3prop} +% functions would choke. It is thus best to make \cs{prop_show:N} +% check very thoroughly the structure and flag issues, even though +% that is very painful for linked props. Throughout the code below, +% we strive to remain as safe as possible, but in the explanations we +% only state what the arguments are when the prop is correctly formed, +% rather than saying at every step that various arguments can be +% arbitrary junk, made safe by using \cs{tl_to_str:n} generously. +% +% The general \cs{__kernel_chk_tl_type:NnnT} checks that its first +% argument is a token list, and if it is, then it \texttt{e}-expands +% its second argument and compares with the contents of its first +% argument. Thus, within this \texttt{e}-expansion it is safe to use +% \cs{@@_if_flat:NTF} to check if the prop is flat or linked. In the +% flat case we simply reconstruct the expected structure using +% \cs{@@_show_flat:w}, which loops through the prop and correctly +% turns all keys to strings for instance. In the linked case, we use +% \cs{@@_show_linked:w}, which ensures the form \cs{@@_flatten:w} +% \cs[no-index]{@@~\meta{prefix}} \cs{s_@@} \Arg{prefix} \meta{rest}, +% where \meta{prefix} is made into a string and \meta{rest} cannot be +% a brace group or multiple tokens since \cs{@@_show_linked:w} would +% in such cases give a different result from the original token list. % \begin{macrocode} \cs_new_protected:Npn \prop_show:N { \@@_show:NN \msg_show:nneeee } \cs_generate_variant:Nn \prop_show:N { c } @@ -1586,22 +2384,168 @@ { \__kernel_chk_tl_type:NnnT #2 { prop } { - \s_@@ - \exp_after:wN \use_i:nn \exp_after:wN \@@_show_validate:w #2 - \@@_pair:wn \q_recursion_tail \s_@@ { } \q_recursion_stop + \@@_if_flat:NTF #2 + { + \s_@@ \@@_chk:w + \exp_after:wN \@@_show_flat:w #2 + \s_@@ { } + \@@_pair:wn \q_@@_recursion_tail \s_@@ { } + \q_@@_recursion_stop + } + { \exp_after:wN \@@_show_linked:w #2 \s_@@ ! ? \s_@@_stop } } { - #1 { prop } { show } - { \token_to_str:N #2 } - { \prop_map_function:NN #2 \msg_show_item:nn } - { } { } + \@@_if_flat:NTF #2 + { \@@_show_finally:NNn #1 #2 { flat } } + { + \tl_set:Nn \l_@@_internal_tl { #1 #2 } + \exp_after:wN \@@_show_prepare:w #2 #2 + } } } -\cs_new:Npn \@@_show_validate:w #1 \@@_pair:wn #2 \s_@@ #3 +\cs_new:Npn \@@_show_flat:w #1 \@@_pair:wn #2 \s_@@ #3 { - \quark_if_recursion_tail_stop:n {#2} + \@@_if_recursion_tail_stop:n {#2} \exp_not:N \@@_pair:wn \tl_to_str:n {#2} \s_@@ \exp_not:n { {#3} } - \@@_show_validate:w + \@@_show_flat:w + } +\cs_new:Npn \@@_show_linked:w #1 \s_@@ #2#3#4 \s_@@_stop + { + \exp_not:N \@@_flatten:w + \exp_not:c { @@ ~ \tl_to_str:n {#2} } + \s_@@ { \tl_to_str:n {#2} } + \exp_not:n {#3} + } +% \end{macrocode} +% For flat props we are done by using \cs{msg_show:nneeee} or +% \cs{msg_log:nneeee}. The auxiliary \cs{@@_show_finally:NNn} is +% eventually also used in the linked case after some more tests. To +% avoid having to bring along the message function and the property +% list, we store them into \cs{l_@@_internal_tl}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_show_finally:NNn #1#2#3 + { + #1 { prop } { show } + { \token_to_str:N #2 } + { \prop_map_function:NN #2 \msg_show_item:nn } + {#3} { } + } +% \end{macrocode} +% For linked props, we now know they have a reasonable form so that we +% are calling \cs{@@_show_prepare:w} \cs{@@_flatten:w} +% \cs[no-index]{@@~\meta{prefix}} \cs{s_@@} \Arg{prefix} \meta{token} +% \meta{property list}, and the task is to loop through the linked +% list and check integrity. We first set things up: the auxiliary +% \cs{@@_tmp:w} will be in charge of checking that various tokens +% start with \cs[no-index]{@@~\meta{prefix}} (in the sense of string +% representations), and calling one of \cs{@@_show_loop_key:wNNN}, +% \cs{@@_show_end:NNN}, \cs{@@_show_bad_name:NNN}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_show_prepare:w + \@@_flatten:w #1 \s_@@ #2#3#4 + { + \use:e + { + \cs_set_nopar:Npn \exp_not:N \@@_tmp:w + ##1 \token_to_str:N #1 ##2 \s_@@_mark ##3 \s_@@_stop + { + \exp_not:N \tl_if_empty:nTF {##1} + { + \exp_not:N \tl_if_head_is_space:nTF {##2} + { \exp_not:N \exp_args:Nf \@@_show_loop_key:wNNN } + { \exp_not:N \tl_if_empty:nTF } + {##2} + } + { \exp_not:N \use_ii:nn } + \@@_show_end:NNN + \@@_show_bad_name:NNN + } + } + \exp_last_unbraced:NNNo \@@_show_loop:NNw #1 #4 #4 + } +% \end{macrocode} +% The loop will consist of calls to \cs{@@_show_loop:NNw} +% \cs[no-index]{@@~\meta{prefix}} \meta{token} \meta{expansion}, where +% \meta{token} is one of the items in the list, specifically the key +% container for \meta{key$_{i-1}$} (starting at $i=1$ with the +% property list variable itself), and \meta{expansion} stands for the +% expansion of that token, which has already been checked, and takes +% the form \meta{junk} \cs{s_@@} \Arg{value} +% \cs[no-index]{@@~\meta{prefix}~\meta{key_i}}. Thus, the loop +% auxiliary receives the prefix command as |#1|, and the $(i-1)$-th +% and $i$-th key containers as |#2| and~|#5|. Then \cs{@@_tmp:w} +% checks that the name of the $i$-th key container is valid. +% \begin{macrocode} +\cs_new_protected:Npn \@@_show_loop:NNw #1#2 #3 \s_@@ #4#5 + { + \exp_last_two_unbraced:Noo \@@_tmp:w + { \token_to_str:N #5 \s_@@_mark } + { \token_to_str:N #1 \s_@@_mark \s_@@_stop } + #1 #2 #5 + } +% \end{macrocode} +% If the $i$-th key container has the wrong name we get +% \cs{@@_show_bad_name:NNN} \cs[no-index]{@@~\meta{prefix}} +% \meta{previous container} \meta{current container with bad name}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_show_bad_name:NNN #1#2#3 + { + \msg_error:nneeee { prop } { bad-link } + { \tl_tail:N \l_@@_internal_tl } + { \token_to_str:N #2 } + { \token_to_str:N #3 } + { \token_to_str:N #1 } + } +% \end{macrocode} +% If the $i$-th key container has the name +% \cs[no-index]{@@~\meta{prefix}} (without space), it is the trailing +% one. We check that it is the right kind of macro to be a token +% list, and that it has the right contents \cs{use_none:n} +% \meta{previous container}. If so, we are done checking everything, +% and we display the property list using the message function and +% property list name stored in \cs{l_@@_internal_tl}. Note that we +% also use this \cs{l_@@_internal_tl} in the type argument of +% \cs{__kernel_chk_tl_type:NnnT}, to build up the name +% \enquote{\meta{property list} prop entry} used in error messages. +% \begin{macrocode} +\cs_new_protected:Npn \@@_show_end:NNN #1#2#3 + { + \__kernel_chk_tl_type:NnnT #3 + { \tl_tail:N \l_@@_internal_tl prop~entry } + { \exp_not:n { \use_none:n #2 } } + { + \exp_after:wN \@@_show_finally:NNn + \l_@@_internal_tl { linked } + } + } +% \end{macrocode} +% If the $i$-th container has a name \cs[no-index]{@@~\meta{prefix} +% \meta{key}} (with a space before the key), then we have a call to +% \cs{@@_show_loop_key:wNNN} \Arg{key} \meta{junk_1} \meta{junk_2} +% \cs[no-index]{@@~\meta{prefix}} \meta{previous container} +% \meta{current container}. (with an \texttt{f}-expansion to eliminate +% the space). The first argument is the \meta{key} without a leading +% space, thanks to a judicious \texttt{f}-expansion earlier on. We +% check that the \meta{current container} is a token list with the +% expected structure \cs{use_none:n} \meta{previous container} +% \cs{@@_pair:wn} \meta{string} \cs{s_@@} \Arg{anything} \meta{single +% token}. The auxiliary \cs{@@_show_flat:w} is reused to produce the +% \cs{@@_pair:wn} part, and the last token is produced by +% \cs{tl_item:Nn} (we don't waste a specialized auxiliary to speed +% that up). If the check succeed, move on to the next item. +% \begin{macrocode} +\cs_new_protected:Npn \@@_show_loop_key:wNNN #1#2#3#4#5#6 + { + \__kernel_chk_tl_type:NnnT #6 + { \tl_tail:N \l_@@_internal_tl prop~entry } + { + \exp_not:n { \use_none:n #5 } + \exp_after:wN \@@_show_flat:w #6 \s_@@ { } + \@@_pair:wn \q_@@_recursion_tail \s_@@ { } + \q_@@_recursion_stop + \tl_item:Nn #6 { -1 } + } + { \exp_last_unbraced:NNNo \@@_show_loop:NNw #4 #6 #6 } } % \end{macrocode} % \end{macro} diff --git a/macros/latex/contrib/l3kernel/l3quark.dtx b/macros/latex/contrib/l3kernel/l3quark.dtx index 281b8453ad..3aeff3f8de 100644 --- a/macros/latex/contrib/l3kernel/l3quark.dtx +++ b/macros/latex/contrib/l3kernel/l3quark.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3regex.dtx b/macros/latex/contrib/l3kernel/l3regex.dtx index 952c115d29..d59d61ddb6 100644 --- a/macros/latex/contrib/l3kernel/l3regex.dtx +++ b/macros/latex/contrib/l3kernel/l3regex.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3seq.dtx b/macros/latex/contrib/l3kernel/l3seq.dtx index 45e5028d1b..c68a7a86af 100644 --- a/macros/latex/contrib/l3kernel/l3seq.dtx +++ b/macros/latex/contrib/l3kernel/l3seq.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3skip.dtx b/macros/latex/contrib/l3kernel/l3skip.dtx index 319cf75b6e..e18ea29c9b 100644 --- a/macros/latex/contrib/l3kernel/l3skip.dtx +++ b/macros/latex/contrib/l3kernel/l3skip.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3sort.dtx b/macros/latex/contrib/l3kernel/l3sort.dtx index a85bb213b2..0e9e70bbdb 100644 --- a/macros/latex/contrib/l3kernel/l3sort.dtx +++ b/macros/latex/contrib/l3kernel/l3sort.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3str-convert.dtx b/macros/latex/contrib/l3kernel/l3str-convert.dtx index 39b6a43d28..dc87dbb621 100644 --- a/macros/latex/contrib/l3kernel/l3str-convert.dtx +++ b/macros/latex/contrib/l3kernel/l3str-convert.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3str.dtx b/macros/latex/contrib/l3kernel/l3str.dtx index 80b26ea9bc..8be8e8ff09 100644 --- a/macros/latex/contrib/l3kernel/l3str.dtx +++ b/macros/latex/contrib/l3kernel/l3str.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3styleguide.pdf b/macros/latex/contrib/l3kernel/l3styleguide.pdf Binary files differindex dda0393c17..c40939a7a3 100644 --- a/macros/latex/contrib/l3kernel/l3styleguide.pdf +++ b/macros/latex/contrib/l3kernel/l3styleguide.pdf diff --git a/macros/latex/contrib/l3kernel/l3syntax-changes.pdf b/macros/latex/contrib/l3kernel/l3syntax-changes.pdf Binary files differindex 0bce9ea22a..e8db43bb3e 100644 --- a/macros/latex/contrib/l3kernel/l3syntax-changes.pdf +++ b/macros/latex/contrib/l3kernel/l3syntax-changes.pdf diff --git a/macros/latex/contrib/l3kernel/l3sys.dtx b/macros/latex/contrib/l3kernel/l3sys.dtx index 1d628ed6a8..d62bd9a80b 100644 --- a/macros/latex/contrib/l3kernel/l3sys.dtx +++ b/macros/latex/contrib/l3kernel/l3sys.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3term-glossary.pdf b/macros/latex/contrib/l3kernel/l3term-glossary.pdf Binary files differindex da4ee7e729..736d53b4c0 100644 --- a/macros/latex/contrib/l3kernel/l3term-glossary.pdf +++ b/macros/latex/contrib/l3kernel/l3term-glossary.pdf diff --git a/macros/latex/contrib/l3kernel/l3text-case.dtx b/macros/latex/contrib/l3kernel/l3text-case.dtx index 10e68fe763..ec9368ae18 100644 --- a/macros/latex/contrib/l3kernel/l3text-case.dtx +++ b/macros/latex/contrib/l3kernel/l3text-case.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3text-map.dtx b/macros/latex/contrib/l3kernel/l3text-map.dtx index b1787a2f73..c8e5db95dc 100644 --- a/macros/latex/contrib/l3kernel/l3text-map.dtx +++ b/macros/latex/contrib/l3kernel/l3text-map.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3text-purify.dtx b/macros/latex/contrib/l3kernel/l3text-purify.dtx index 2e57a3c3d3..a797b3a609 100644 --- a/macros/latex/contrib/l3kernel/l3text-purify.dtx +++ b/macros/latex/contrib/l3kernel/l3text-purify.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3text.dtx b/macros/latex/contrib/l3kernel/l3text.dtx index 0631cc4695..7ef37636f3 100644 --- a/macros/latex/contrib/l3kernel/l3text.dtx +++ b/macros/latex/contrib/l3kernel/l3text.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % @@ -196,10 +196,7 @@ % codepoint is uppercased, irrespective of the general code of the character. % % \begin{function}[added = 2022-07-04] -% { -% \text_declare_case_equivalent:Nn , -% \text_declare_case_equivalent:cn -% } +% {\text_declare_case_equivalent:Nn} % \begin{syntax} % \cs{text_declare_case_equivalent:Nn} \meta{cmd} \Arg{replacement} % \end{syntax} diff --git a/macros/latex/contrib/l3kernel/l3tl-analysis.dtx b/macros/latex/contrib/l3kernel/l3tl-analysis.dtx index 4c85ed51d9..9ff2ec04e7 100644 --- a/macros/latex/contrib/l3kernel/l3tl-analysis.dtx +++ b/macros/latex/contrib/l3kernel/l3tl-analysis.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % @@ -196,18 +196,15 @@ % \end{variable} % % \begin{variable} -% {\l_@@_analysis_token, \l_@@_analysis_char_token, \l_@@_analysis_next_token} +% {\l_@@_analysis_token, \l_@@_analysis_char_token} % The tokens in the token list are probed with the \TeX{} primitive % \tn{futurelet}. We use \cs{l_@@_analysis_token} in that % construction. In some cases, we convert the following token to a % string before probing it: then the token variable used is -% \cs{l_@@_analysis_char_token}. When getting tokens from the input -% stream we may need to look two tokens ahead, for which we use -% \cs{l_@@_analysis_next_token}. +% \cs{l_@@_analysis_char_token}. % \begin{macrocode} \cs_new_eq:NN \l_@@_analysis_token ? \cs_new_eq:NN \l_@@_analysis_char_token ? -\cs_new_eq:NN \l_@@_analysis_next_token ? % \end{macrocode} % \end{variable} % @@ -1223,13 +1220,12 @@ % \@@_peek_analysis_nonexp:N, \@@_peek_analysis_cs:N, % \@@_peek_analysis_char:N, \@@_peek_analysis_char:w, % \@@_peek_analysis_special:, \@@_peek_analysis_retest:, -% \@@_peek_analysis_next:, \@@_peek_analysis_nextii:, % \@@_peek_analysis_str:, % \@@_peek_analysis_str:w, \@@_peek_analysis_str:n, % \@@_peek_analysis_active_str:n, \@@_peek_analysis_explicit:n, % \@@_peek_analysis_escape:, \@@_peek_analysis_collect:w, % \@@_peek_analysis_collect:n, \@@_peek_analysis_collect_loop:, -% \@@_peek_analysis_collect_test:, \@@_peek_analysis_collect_end:NNN +% \@@_peek_analysis_collect_test:, \@@_peek_analysis_collect_end:NNNN % } % Save the user's code in a control sequence that is suitable for % nested maps. We may wish to pass to this function an \tn{outer} @@ -1254,7 +1250,10 @@ #1 \@@_peek_analysis_loop:NNn \prg_break_point:Nn \peek_analysis_map_break: - { \group_align_safe_end: } + { + \int_gdecr:N \g__kernel_prg_map_int + \group_align_safe_end: + } } \@@_peek_analysis_loop:NNn ? ? ? } @@ -1460,7 +1459,7 @@ \if_meaning:w \l_@@_analysis_token \scan_stop: \exp_after:wN \@@_peek_analysis_normal:N \else: - \exp_after:wN \@@_peek_analysis_next: + \exp_after:wN \@@_peek_analysis_str: \fi: } % \end{macrocode} @@ -1469,41 +1468,22 @@ % begin-group or end-group token (catcode $1$ or~$2$), and we excluded % a few cases that would be difficult later (empty control sequence, % active character with the same character code as its meaning or as -% the escape character). Now look at the \meta{next token} following -% it using a combination of \tn{afterassignment} and \tn{futurelet}. -% (In fact look twice to reset an internal \TeX{} flag in case the -% \meta{next token} had been hit with \cs{exp_not:N}.) -% The syntax of this primitive is \tn{futurelet} \meta{peek token} -% \meta{first token} \meta{next token}, and it sets \meta{peek token} -% equal to \meta{next token}. Traditionally, one takes \meta{first -% token} to be some macro that regains control of the code and, e.g., -% analyses \meta{peek token}. Here, both \meta{first token} and -% \meta{next token} are mostly unknown tokens in the input stream (but -% we know the \meta{first token} has catcode $1$, $2$ or $10$), where -% \meta{first token} was already stored as \cs{l_peek_token}, and we -% regain control using \tn{afterassignment}, which inserts its -% argument after the assignment, hence after \meta{peek token} but -% before \meta{first token}. -% \begin{macrocode} -\cs_new_protected:Npn \@@_peek_analysis_next: - { - \tl_if_empty:oT { \tex_the:D \tex_everyeof:D } - { \tex_everyeof:D { \scan_stop: } } - \tex_afterassignment:D \@@_peek_analysis_nextii: - \tex_futurelet:D \l_@@_analysis_next_token - } -\cs_new_protected:Npn \@@_peek_analysis_nextii: - { - \tex_afterassignment:D \@@_peek_analysis_str: - \tex_futurelet:D \l_@@_analysis_next_token - } -% \end{macrocode} -% We then hit the \meta{first token} with \cs{token_to_str:N} and grab -% characters until finding \cs{l_@@_analysis_next_token}. More +% the escape character). The idea is to apply \cs{token_to_str:N} to +% the \meta{token} then grab characters (of category code~$12$ except +% for spaces that have category code~$10$) to reconstruct it. In +% earlier versions of the code we would peek at the \meta{next token} +% that lies after \meta{token} in the input stream, which would help +% us be more accurate in reconstructing the \meta{token} case in edge +% cases (mentioned below), but this had the side-effect of tokenizing +% the input stream (turning characters into tokens) farther ahead than +% needed. +% +% We hit the \meta{token} with \cs{token_to_str:N} and start grabbing +% characters. More % precisely, by looking at the first character in the string -% representation of the \meta{first token} we distinguish three cases: +% representation of the \meta{token} we distinguish three cases: % a stringified control sequence starts with the escape character; for -% an explicit character we find that same character; for an explicit +% an explicit character we find that same character; for an active % character we find anything else (we made sure to exclude the case of % an active character whose string representation coincides with the % other two cases). @@ -1591,14 +1571,11 @@ % know that until we had run all the various tests including % stringifying the token. We are thus left with the hard work of % picking up one by one the characters in the csname (being careful -% about spaces), until finding a token that matches the \meta{next -% token} picked up earlier (which was not stringified), such that the -% control sequence that we found so far indeed has the expected -% meaning \cs{l_peek_token}. This comparison with \cs{l_peek_token} -% catches a reasonably common case like \cs{c_group_begin_token} |_| -% in which the trailing |_| has category code other: without -% comparison of the constructed csname with \cs{l_peek_token} -% collection would stop at \cs[no-index]{c}, which is wrong. +% about spaces), until the constructed csname has the expected +% meaning. This fails if someone defines a token like +% \cs[no-index]{bgroup@my} whose string representation starts the same +% as another token with the same meaning being an implicit character +% token of category code $1$, $2$, or $10$. % \begin{macrocode} \cs_new_protected:Npn \@@_peek_analysis_escape: { @@ -1615,25 +1592,27 @@ } \cs_new_protected:Npn \@@_peek_analysis_collect_loop: { - \tex_futurelet:D \l_@@_analysis_token - \@@_peek_analysis_collect_test: - } -\cs_new_protected:Npn \@@_peek_analysis_collect_test: - { - \if_meaning:w \l_@@_analysis_token \l_@@_analysis_next_token - \exp_after:wN \if_meaning:w \cs:w \l_@@_internal_a_tl \cs_end: \l_peek_token - \@@_peek_analysis_collect_end:NNN + \exp_after:wN \if_meaning:w + \cs:w + \if_cs_exist:w \l_@@_internal_a_tl \cs_end: + \l_@@_internal_a_tl + \else: + c_one % anything short \fi: + \cs_end: + \l_peek_token + \@@_peek_analysis_collect_end:NNNN \fi: - \@@_peek_analysis_collect:w + \tex_futurelet:D \l_@@_analysis_token + \@@_peek_analysis_collect:w } % \end{macrocode} % End by calling the user code with suitable arguments (here |#1|, % |#2| are \cs{fi:}), which closes the group begun early on. % \begin{macrocode} -\cs_new_protected:Npn \@@_peek_analysis_collect_end:NNN #1#2#3 +\cs_new_protected:Npn \@@_peek_analysis_collect_end:NNNN #1#2#3#4 { - #1 #2 + #1 \tl_put_right:Ne \l_@@_peek_code_tl { { \exp_not:N \exp_not:n { \exp_not:c { \l_@@_internal_a_tl } } } diff --git a/macros/latex/contrib/l3kernel/l3tl-build.dtx b/macros/latex/contrib/l3kernel/l3tl-build.dtx index 66b5a2d03e..7971301ef3 100644 --- a/macros/latex/contrib/l3kernel/l3tl-build.dtx +++ b/macros/latex/contrib/l3kernel/l3tl-build.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3tl.dtx b/macros/latex/contrib/l3kernel/l3tl.dtx index 5429f19c54..1043feaad9 100644 --- a/macros/latex/contrib/l3kernel/l3tl.dtx +++ b/macros/latex/contrib/l3kernel/l3tl.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3token.dtx b/macros/latex/contrib/l3kernel/l3token.dtx index add3e80eff..fe8d1a780e 100644 --- a/macros/latex/contrib/l3kernel/l3token.dtx +++ b/macros/latex/contrib/l3kernel/l3token.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % @@ -102,7 +102,8 @@ % \end{syntax} % Sets the behaviour of the \meta{char} in situations where it is % active (category code $13$) to be equivalent to that of the -% \meta{function}. The category code of the \meta{char} is +% definition of the \meta{function} at the time \cs{char_set_active_eq:NN} +% is used. The category code of the \meta{char} is % \emph{unchanged} by this process. The \meta{function} may itself % be an active character. % \end{function} @@ -118,7 +119,8 @@ % Sets the behaviour of the \meta{char} which has character % code as given by the \meta{integer expression} in situations % where it is active (category code $13$) to be equivalent to that of the -% \meta{function}. The category code of the \meta{char} is +% \meta{function} at the time \cs{char_set_active_eq:nN} +% is used. The category code of the \meta{char} is % \emph{unchanged} by this process. The \meta{function} may itself % be an active character. % \end{function} @@ -424,6 +426,11 @@ % These are implicit tokens which have the category code described % by their name. They are used internally for test purposes but % are also available to the programmer for other uses. +% \begin{texnote} +% The tokens \cs{c_group_begin_token}, \cs{c_group_end_token}, and +% \cs{c_space_token} are \pkg{expl3} counterparts of \LaTeXe{}'s +% \tn{bgroup}, \tn{egroup}, and \cs{@sptoken}. +% \end{texnote} % \end{variable} % % \begin{variable} @@ -786,11 +793,11 @@ % There is often a need to look ahead at the next token in the input % stream while leaving it in place. This is handled using the % \enquote{peek} functions. The generic \cs{peek_after:Nw} is provided -% along with a family of predefined tests for common cases. As peeking -% ahead does \emph{not} skip spaces the predefined tests include both a -% space-respecting and space-skipping version. In addition, using -% \cs{peek_analysis_map_inline:n}, one can map through the following -% tokens in the input stream and repeatedly perform some tests. +% along with a family of predefined tests for common cases. Peeking +% ahead does \emph{not} skip spaces: rather, \cs{peek_remove_spaces:n}. +% should be used. In addition, using \cs{peek_analysis_map_inline:n}, +% one can map through the following tokens in the input stream and +% repeatedly perform some tests. % % \begin{function}{\peek_after:Nw} % \begin{syntax} @@ -957,7 +964,7 @@ % (as appropriate to the result of the test). % \end{function} % -% \begin{function}[added = 2020-12-03, updated = 2022-10-03] +% \begin{function}[added = 2020-12-03, updated = 2024-02-07] % {\peek_analysis_map_inline:n} % \begin{syntax} % \cs{peek_analysis_map_inline:n} \Arg{inline function} @@ -998,6 +1005,12 @@ % effect after the loop. Within the code, \cs{l_peek_token} is set % equal (as a token, not a token list) to the token under % consideration. +% \begin{texnote} +% In case the input stream has not yet been tokenized (converted +% from characters to tokens), characters are tokenized one by one as +% needed by \cs{peek_analysis_map_inline:n} using the current +% category code regime. +% \end{texnote} % \end{function} % % \begin{function}[added = 2020-12-03] diff --git a/macros/latex/contrib/l3kernel/l3unicode.dtx b/macros/latex/contrib/l3kernel/l3unicode.dtx index ce11f62ae2..de3b6067ba 100644 --- a/macros/latex/contrib/l3kernel/l3unicode.dtx +++ b/macros/latex/contrib/l3kernel/l3unicode.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2024-01-22} +% \date{Released 2024-02-13} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/source3.pdf b/macros/latex/contrib/l3kernel/source3.pdf Binary files differindex d74db5c796..09f28f0667 100644 --- a/macros/latex/contrib/l3kernel/source3.pdf +++ b/macros/latex/contrib/l3kernel/source3.pdf diff --git a/macros/latex/contrib/l3kernel/source3body.tex b/macros/latex/contrib/l3kernel/source3body.tex index 5ccc140197..1e396b15fb 100644 --- a/macros/latex/contrib/l3kernel/source3body.tex +++ b/macros/latex/contrib/l3kernel/source3body.tex @@ -272,7 +272,11 @@ following argument specifiers: dependent and their name can change without warning, thus their use is \emph{strongly discouraged} in package code: programmers should instead use the interfaces documented in - \href{interface3.pdf}{interface3.pdf}. + \ifinterface + this documentation. + \else + \href{interface3.pdf}{interface3.pdf}. + \fi \end{description} Notice that the argument specifier describes how the argument is processed prior to being passed to the underlying function. For example, @@ -607,8 +611,7 @@ used on top of \LaTeXe{} if \tn{outer} tokens are used in the arguments. \DocInput{l3color.dtx} \DocInput{l3pdf.dtx} -\part{Removals} - +% implementation part only \ExplSyntaxOn \clist_gput_right:Nn \g_docinput_clist { l3deprecation.dtx } \clist_gput_right:Nn \g_docinput_clist { l3debug.dtx } diff --git a/macros/latex/contrib/l3packages/CHANGELOG.md b/macros/latex/contrib/l3packages/CHANGELOG.md index 05001a0e8f..abef360b7a 100644 --- a/macros/latex/contrib/l3packages/CHANGELOG.md +++ b/macros/latex/contrib/l3packages/CHANGELOG.md @@ -7,6 +7,15 @@ this project uses date-based 'snapshot' version identifiers. ## [Unreleased] +## [2024-02-13] + +### Changed +- Preparation for move of `xtemplate` concepts to the kernel + +### Removed +- `\IfInstanceExist(TF)` +- `xfrac`: moved to https://github.com/latex3/xfrac + ## [2023-10-10] ### Changed @@ -65,7 +74,7 @@ this project uses date-based 'snapshot' version identifiers. ## [2022-05-30] -## Added +### Added - `\SetTemplateKeys` for _ad hoc_ adjustment of template values ### Changed @@ -99,19 +108,16 @@ this project uses date-based 'snapshot' version identifiers. ## [2021-06-18] ### Fixed - - Correct internal changes to message naming ## [2021-06-01] ### Fixed - - Restore one parameter in `xfrac` ## [2021-05-27] ### Changed - - Internal changes to message naming ## [2021-05-07] @@ -237,7 +243,8 @@ this project uses date-based 'snapshot' version identifiers. - Switch to ISO date format - Improve cross-module use of internal functions -[Unreleased]: https://github.com/latex3/latex3/compare/2023-10-10...HEAD +[Unreleased]: https://github.com/latex3/latex3/compare/2024-02-13...HEAD +[2024-02-13]: https://github.com/latex3/latex3/compare/2023-10-10...2024-02-13 [2023-10-10]: https://github.com/latex3/latex3/compare/2023-08-29...2023-10-10 [2023-08-29]: https://github.com/latex3/latex3/compare/2023-02-02...2023-08-29 [2023-02-02]: https://github.com/latex3/latex3/compare/2023-02-01...2023-02-02 diff --git a/macros/latex/contrib/l3packages/README.md b/macros/latex/contrib/l3packages/README.md index d3ecea7028..2579ee6e96 100644 --- a/macros/latex/contrib/l3packages/README.md +++ b/macros/latex/contrib/l3packages/README.md @@ -1,99 +1,37 @@ -LaTeX3 High-Level Concepts -========================== +# `l3packages`: Higher-level LaTeX3 Concepts (Deprecated) -Release 2023-10-10 +Release 2024-02-13 -Overview --------- +## Overview -The `l3packages` collection 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 LaTeX 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. +The `l3packages` collection dealing with higher-level ideas such as the +Designer Interface, as part of 'LaTeX3' developments. The packages here have +over time migrated into the LaTeX kernel: the material here is retained to +support older files. -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. +The appropriate LaTeX kernel releases incorporating the ideas from the packages +here are -Currently included in the CTAN release of `l3packages` are the following -bundles: -* `l3keys2e` (deprecated in favor of updated LaTeX kernel methods) -* `xfp` (from 2022-06-01 part of the LaTeX format) -* `xfrac` -* `xparse` (deprecated in favor of elements move to the LaTeX kernel) -* `xtemplate` +- `l3keys2e` 2022-06-01 +- `xfp` 2022-06-01 +- `xparse` 2020-10-01 +- `xtemplate` 2024-06-01 -`l3keys2e` (deprecated) ----------- +## Issues -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` (deprecated) ------ - -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. - -From 2022-06-01 release of LaTeX this will be included in the format -so that the package doesn't need loading any longer. - -`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` (deprecated) -------- - -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. - -The development of `xparse` moved to the -[LaTeX2e repository](https://github.com/latex3/latex2e) as `ltcmd`, which is -preloaded in the LaTeX format, and the code for `xparse` in this repository -contains only the deprecated argument types `G`, `l`, and `u`. - -`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. - - -Issues ------- - -The issue tracker for LaTeX3 is currently located +The issue tracker for LaTeX is currently located [on GitHub](https://github.com/latex3/latex3/issues). +## Development team -The LaTeX Project ------------------- +This code is developed by [The LaTeX Project](https://latex-project.org). -Development of LaTeX3 is carried out by -[The LaTeX Project](https://www.latex-project.org/latex3/). +## License -The development team can be contacted -by e-mail: <latex-team@latex-project.org>; for general LaTeX3 discussion -the [LaTeX-L list](#Discussion) should be used. +Released under the LaTeX Project Public License v1.3c or later. See https://www.latex-project.org/lppl.txt ----- -<p>Copyright (C) 1998-2012,2015-2023 The LaTeX Project <br /> -<a href="http://latex-project.org/">http://latex-project.org/</a> <br /> +<p>Copyright (C) 2024 The LaTeX Project <br /> +<a href="https://latex-project.org/">https://latex-project.org/</a> <br /> All rights reserved.</p> diff --git a/macros/latex/contrib/l3packages/l3keys2e.dtx b/macros/latex/contrib/l3packages/l3keys2e.dtx index 376cb136ac..f5c494120a 100644 --- a/macros/latex/contrib/l3packages/l3keys2e.dtx +++ b/macros/latex/contrib/l3packages/l3keys2e.dtx @@ -2,7 +2,7 @@ % %% File: l3keys2e.dtx % -% Copyright (C) 2009,2011-2023 The LaTeX Project +% Copyright (C) 2009-2024 The LaTeX Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this @@ -49,7 +49,7 @@ % \fi % % \title{^^A -% The \textsf{l3keys2e} package\\ +% The \pkg{l3keys2e} package\\ % \LaTeXe{} option processing using \LaTeX3 keys^^A % } % @@ -62,7 +62,7 @@ % }^^A % } % -% \date{Released 2023-10-10} +% \date{Released 2024-02-13} % % \maketitle % @@ -154,7 +154,7 @@ % \end{macrocode} % % \begin{macrocode} -\ProvidesExplPackage{l3keys2e}{2023-10-10}{} +\ProvidesExplPackage{l3keys2e}{2024-02-13}{} {LaTeX2e option processing using LaTeX3 keys} % \end{macrocode} % diff --git a/macros/latex/contrib/l3packages/l3keys2e.ins b/macros/latex/contrib/l3packages/l3keys2e.ins index 667debde59..c4d1545e13 100644 --- a/macros/latex/contrib/l3packages/l3keys2e.ins +++ b/macros/latex/contrib/l3packages/l3keys2e.ins @@ -2,7 +2,7 @@ File: l3keys2e.ins -Copyright (C) 2009,2011,2012,2016,2017,2019-2023 The LaTeX Project +Copyright (C) 2009,2011,2012,2016,2017,2019-2024 The LaTeX Project It may be distributed and/or modified under the conditions of the LaTeX Project Public License (LPPL), either version 1.3c of this @@ -23,7 +23,7 @@ The released version of this bundle is available from CTAN. \preamble -Copyright (C) 2009-2023 The LaTeX Project +Copyright (C) 2009-2024 The LaTeX Project It may be distributed and/or modified under the conditions of the LaTeX Project Public License (LPPL), either version 1.3c of diff --git a/macros/latex/contrib/l3packages/l3keys2e.pdf b/macros/latex/contrib/l3packages/l3keys2e.pdf Binary files differindex cfef37deff..a6e0bd9e41 100644 --- a/macros/latex/contrib/l3packages/l3keys2e.pdf +++ b/macros/latex/contrib/l3packages/l3keys2e.pdf diff --git a/macros/latex/contrib/l3packages/xfp.dtx b/macros/latex/contrib/l3packages/xfp.dtx index 2c3a5bf732..d695d44bb1 100644 --- a/macros/latex/contrib/l3packages/xfp.dtx +++ b/macros/latex/contrib/l3packages/xfp.dtx @@ -2,7 +2,7 @@ % %% File: xfp.dtx % -% Copyright (C) 2017-2023 The LaTeX Project +% Copyright (C) 2017-2024 The LaTeX Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this @@ -51,7 +51,7 @@ % \providecommand\nan{\texttt{NaN}} % % \title{^^A -% The \textsf{xfp} package\\Floating Point Unit^^A +% The \pkg{xfp} package\\ Floating Point Unit^^A % } % % \author{^^A @@ -63,7 +63,7 @@ % }^^A % } % -% \date{Released 2023-10-10} +% \date{Released 2024-02-13} % % \maketitle % @@ -171,7 +171,7 @@ % \end{macrocode} % % \begin{macrocode} -\ProvidesExplPackage{xfp}{2023-10-10}{} +\ProvidesExplPackage{xfp}{2024-02-13}{} {L3 Floating point unit} % \end{macrocode} % diff --git a/macros/latex/contrib/l3packages/xfp.ins b/macros/latex/contrib/l3packages/xfp.ins index 266bfa1661..d7d1ff046a 100644 --- a/macros/latex/contrib/l3packages/xfp.ins +++ b/macros/latex/contrib/l3packages/xfp.ins @@ -2,7 +2,7 @@ File: xfp.ins -Copyright (C) 2017,2019-2023 The LaTeX Project +Copyright (C) 2017,2019-2024 The LaTeX Project It may be distributed and/or modified under the conditions of the LaTeX Project Public License (LPPL), either version 1.3c of this @@ -23,7 +23,7 @@ The released version of this bundle is available from CTAN. \preamble -Copyright (C) 2017-2023 The LaTeX Project +Copyright (C) 2017-2024 The LaTeX Project It may be distributed and/or modified under the conditions of the LaTeX Project Public License (LPPL), either version 1.3c of diff --git a/macros/latex/contrib/l3packages/xfp.pdf b/macros/latex/contrib/l3packages/xfp.pdf Binary files differindex aa5b6c0293..b45cdd5b15 100644 --- a/macros/latex/contrib/l3packages/xfp.pdf +++ b/macros/latex/contrib/l3packages/xfp.pdf diff --git a/macros/latex/contrib/l3packages/xfrac.dtx b/macros/latex/contrib/l3packages/xfrac.dtx deleted file mode 100644 index 6bce5f93ee..0000000000 --- a/macros/latex/contrib/l3packages/xfrac.dtx +++ /dev/null @@ -1,1141 +0,0 @@ -% \iffalse meta-comment -% -%% File: xfrac.dtx -% -% Copyright (C) 2004,2008-2010 Morten Hoegholm -% (C) 2011,2012,2014-2023 The LaTeX Project -% -% It may be distributed and/or modified under the conditions of the -% LaTeX Project Public License (LPPL), either version 1.3c of this -% license or (at your option) any later version. The latest version -% of this license is in the file -% -% https://www.latex-project.org/lppl.txt -% -% This file is part of the "l3packages bundle" (The Work in LPPL) -% and all files in that bundle must be distributed together. -% -% ----------------------------------------------------------------------- -% -% The development version of the bundle can be found at -% -% https://github.com/latex3/latex3 -% -% for those people who are interested. -% -%<*driver|package> -% The version of expl3 required is tested as early as possible, as -% some really old versions do not define \ProvidesExplPackage. -\RequirePackage{expl3}[2018/02/21] -%<package>\@ifpackagelater{expl3}{2018/02/21} -%<package> {} -%<package> {% -%<package> \PackageError{xfrac}{Support package l3kernel too old} -%<package> {% -%<package> Please install an up to date version of l3kernel\MessageBreak -%<package> using your TeX package manager or from CTAN.\MessageBreak -%<package> \MessageBreak -%<package> Loading xfrac will abort!% -%<package> }% -%<package> \endinput -%<package> } -\RequirePackage{amstext,graphicx,l3keys2e,textcomp,xparse,xtemplate} -%</driver|package> -%<*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} -%</driver> -% \fi -% -% \title{^^A -% The \textsf{xfrac} package\\ Split-level fractions^^A -% } -% -% \author{^^A -% The \LaTeX{} Project\thanks -% {^^A -% E-mail: -% \href{mailto:latex-team@latex-project.org} -% {latex-team@latex-project.org}^^A -% }^^A -% } -% -% \date{Released 2023-10-10} -% -% \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}{2023-10-10}{} - {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} - { - \mode_if_math:TF - { - \cs_set_eq:NN \@@_text_or_math:n \text - \cs_set:Npe \@@_denominator_font_change: - { \tex_fam:D \l_@@_denominator_font_tl } - \cs_set:Npe \@@_numerator_font_change: - { \tex_fam:D \l_@@_numerator_font_tl } - \cs_set:Npe \@@_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 , - 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. We annot set the -% |numerator-top-sep| in the restricted template above as it clashes -% with the requirements of the |plainmath| collection which comes next. -% \begin{macrocode} -\DeclareInstance { xfrac } { mathdefault } { math } - { numerator-top-sep = 0pt } -% \end{macrocode} -% -% \begin{macrocode} -\DeclareCollectionInstance { plainmath } { xfrac } { mathdefault } { math } - { - 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} -%</package> -% \end{macrocode} -% -% \end{implementation} -% -% \PrintIndex diff --git a/macros/latex/contrib/l3packages/xfrac.ins b/macros/latex/contrib/l3packages/xfrac.ins deleted file mode 100644 index 511e68c3c3..0000000000 --- a/macros/latex/contrib/l3packages/xfrac.ins +++ /dev/null @@ -1,49 +0,0 @@ -\iffalse meta-comment - -File: xfrac.ins - -Copyright (C) 2004,2008-2010 Morten Hoegholm - (C) 2011,2012,2016,2017,2019-2023 The LaTeX 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 docstrip % -\askforoverwritefalse - -\preamble - -Copyright (C) 2004,2008-2010 Morten Hoegholm - (C) 2011-2023 The LaTeX 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 Binary files differdeleted file mode 100644 index 61a472a901..0000000000 --- a/macros/latex/contrib/l3packages/xfrac.pdf +++ /dev/null diff --git a/macros/latex/contrib/l3packages/xparse.dtx b/macros/latex/contrib/l3packages/xparse.dtx index ba98987d7f..e701edada3 100644 --- a/macros/latex/contrib/l3packages/xparse.dtx +++ b/macros/latex/contrib/l3packages/xparse.dtx @@ -4,7 +4,7 @@ % % Copyright (C) 1999 Frank Mittelbach, Chris Rowley, David Carlisle % (C) 2004-2008 Frank Mittelbach, The LaTeX Project -% (C) 2009-2023 The LaTeX Project +% (C) 2009-2024 The LaTeX Project % % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this @@ -55,7 +55,7 @@ % \begin{itemize}\def\makelabel##1{\hss\llap{\bfseries##1}}}{\end{itemize}} % % \title{^^A -% The \textsf{xparse} package\\ Document command parser^^A +% The \pkg{xparse} package\\ Document command parser^^A % } % % \author{^^A @@ -67,7 +67,7 @@ % }^^A % } % -% \date{Released 2023-10-10} +% \date{Released 2024-02-13} % % \maketitle % @@ -1064,7 +1064,7 @@ % \end{macrocode} % % \begin{macrocode} -\ProvidesExplPackage{xparse}{2023-10-10}{} +\ProvidesExplPackage{xparse}{2024-02-13}{} {L3 Experimental document command parser} % \end{macrocode} % diff --git a/macros/latex/contrib/l3packages/xparse.ins b/macros/latex/contrib/l3packages/xparse.ins index d7973d49f7..9177c53dbc 100644 --- a/macros/latex/contrib/l3packages/xparse.ins +++ b/macros/latex/contrib/l3packages/xparse.ins @@ -2,7 +2,7 @@ File: xparse.ins -Copyright (C) 2000-2012,2016,2017,2019-2023 The LaTeX Project +Copyright (C) 2000-2012,2016,2017,2019-2024 The LaTeX Project It may be distributed and/or modified under the conditions of the LaTeX Project Public License (LPPL), either version 1.3c of this @@ -23,7 +23,7 @@ The released version of this bundle is available from CTAN. \preamble -Copyright (C) 2009-2023 The LaTeX Project +Copyright (C) 2009-2024 The LaTeX Project It may be distributed and/or modified under the conditions of the LaTeX Project Public License (LPPL), either version 1.3c of diff --git a/macros/latex/contrib/l3packages/xparse.pdf b/macros/latex/contrib/l3packages/xparse.pdf Binary files differindex 483370f2fc..183ebaaa78 100644 --- a/macros/latex/contrib/l3packages/xparse.pdf +++ b/macros/latex/contrib/l3packages/xparse.pdf diff --git a/macros/latex/contrib/l3packages/xtemplate-2023-10-10.sty b/macros/latex/contrib/l3packages/xtemplate-2023-10-10.sty new file mode 100644 index 0000000000..a0a91afd81 --- /dev/null +++ b/macros/latex/contrib/l3packages/xtemplate-2023-10-10.sty @@ -0,0 +1,1371 @@ +%% +%% This is file `xtemplate-2023-10-10.sty', generated from +%% xtemplate.dtx (with options: `package' on 2023-11-14) +%% then adapted and frozen for compatibility. Development of +%% xtemplate.dtx will continue in the LaTeX2e kernel as lttemplates.dtx. +%% +%% Copyright (C) 2011-2023 The LaTeX 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. +%% +%% File: xtemplate.dtx +\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 + } +\ProvidesExplPackage{xtemplate}{2023-10-10}{} + {L3 Experimental prototype document functions} +\tl_const:Nn \c__xtemplate_code_root_tl { template~code~>~ } +\tl_const:Nn \c__xtemplate_defaults_root_tl { template~defaults~>~ } +\tl_const:Nn \c__xtemplate_instances_root_tl { template~instance~>~ } +\tl_const:Nn \c__xtemplate_keytypes_root_tl { template~key~types~>~ } +\tl_const:Nn \c__xtemplate_key_order_root_tl { template~key~order~>~ } +\tl_const:Nn \c__xtemplate_restrict_root_tl { template~restrictions~>~ } +\tl_const:Nn \c__xtemplate_values_root_tl { template~values~>~ } +\tl_const:Nn \c__xtemplate_vars_root_tl { template~vars~>~ } +\seq_const_from_clist:Nn \c__xtemplate_keytypes_arg_seq + { choice , function , instance } +\prop_new:N \g__xtemplate_object_type_prop +\tl_new:N \l__xtemplate_assignments_tl +\tl_new:N \l__xtemplate_collection_tl +\prop_new:N \l__xtemplate_collections_prop +\tl_new:N \l__xtemplate_default_tl +\bool_new:N \l__xtemplate_error_bool +\bool_new:N \l__xtemplate_global_bool +\bool_new:N \l__xtemplate_restrict_bool +\clist_new:N \l__xtemplate_restrict_clist +\tl_new:N \l__xtemplate_key_name_tl +\tl_new:N \l__xtemplate_keytype_tl +\tl_new:N \l__xtemplate_keytype_arg_tl +\tl_new:N \l__xtemplate_value_tl +\tl_new:N \l__xtemplate_var_tl +\prop_new:N \l__xtemplate_keytypes_prop +\seq_new:N \l__xtemplate_key_order_seq +\prop_new:N \l__xtemplate_values_prop +\prop_new:N \l__xtemplate_vars_prop +\clist_new:N \l__xtemplate_tmp_clist +\dim_new:N \l__xtemplate_tmp_dim +\int_new:N \l__xtemplate_tmp_int +\muskip_new:N \l__xtemplate_tmp_muskip +\skip_new:N \l__xtemplate_tmp_skip +\tl_new:N \l__xtemplate_tmp_tl +\scan_new:N \s__xtemplate_mark +\scan_new:N \s__xtemplate_stop +\quark_new:N \q__xtemplate_nil +\__kernel_quark_new_conditional:Nn \__xtemplate_quark_if_nil:N { F } +\cs_generate_variant:Nn \prop_get:NnNTF { No } +\cs_generate_variant:Nn \prop_get:NnNT { No } +\cs_generate_variant:Nn \prop_get:NnNF { No } +\cs_new_protected:Npn \__xtemplate_execute_if_arg_agree:nnT #1#2#3 + { + \prop_get:NnN \g__xtemplate_object_type_prop {#1} \l__xtemplate_tmp_tl + \int_compare:nNnTF {#2} = \l__xtemplate_tmp_tl + {#3} + { + \msg_error:nneee { xtemplate } + { argument-number-mismatch } {#1} { \l__xtemplate_tmp_tl } {#2} + } + } +\cs_new_protected:Npn \__xtemplate_execute_if_code_exist:nnT #1#2#3 + { + \cs_if_exist:cTF { \c__xtemplate_code_root_tl #1 / #2 } + {#3} + { + \msg_error:nnee { xtemplate } { no-template-code } + {#1} {#2} + } + } +\cs_new_protected:Npn \__xtemplate_execute_if_keytype_exist:nT #1#2 + { + \cs_if_exist:cTF { __xtemplate_store_value_ #1 :n } + {#2} + { \msg_error:nne { xtemplate } { unknown-keytype } {#1} } + } +\cs_generate_variant:Nn \__xtemplate_execute_if_keytype_exist:nT { o } +\cs_new_protected:Npn \__xtemplate_execute_if_type_exist:nT #1#2 + { + \prop_if_in:NnTF \g__xtemplate_object_type_prop {#1} + {#2} + { \msg_error:nne { xtemplate } { unknown-object-type } {#1} } + } +\cs_new_protected:Npn \__xtemplate_if_keys_exist:nnT #1#2#3 + { + \cs_if_exist:cTF { \c__xtemplate_keytypes_root_tl #1 / #2 } + {#3} + { + \msg_error:nnee { xtemplate } { unknown-template } + {#1} {#2} + } + } +\prg_new_conditional:Npnn \__xtemplate_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 \__xtemplate_if_key_value:nT { o } +\cs_generate_variant:Nn \__xtemplate_if_key_value:nF { o } +\cs_generate_variant:Nn \__xtemplate_if_key_value:nTF { o } +\prg_new_conditional:Npnn \__xtemplate_if_instance_exist:nnn #1#2#3 + { T, F, TF } + { + \cs_if_exist:cTF { \c__xtemplate_instances_root_tl #1 / #2 / #3 } + { \prg_return_true: } + { \prg_return_false: } + } +\prg_new_conditional:Npnn \__xtemplate_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: } +} +\cs_new_protected:Npn \__xtemplate_store_defaults:n #1 + { + \prop_gclear_new:c { \c__xtemplate_defaults_root_tl #1 } + \prop_gset_eq:cN { \c__xtemplate_defaults_root_tl #1 } + \l__xtemplate_values_prop + } +\cs_new_protected:Npn \__xtemplate_store_keytypes:n #1 + { + \prop_gclear_new:c { \c__xtemplate_keytypes_root_tl #1 } + \prop_gset_eq:cN { \c__xtemplate_keytypes_root_tl #1 } + \l__xtemplate_keytypes_prop + \seq_gclear_new:c { \c__xtemplate_key_order_root_tl #1 } + \seq_gset_eq:cN { \c__xtemplate_key_order_root_tl #1 } + \l__xtemplate_key_order_seq + } +\cs_new_protected:Npn \__xtemplate_store_values:n #1 + { + \prop_clear_new:c { \c__xtemplate_values_root_tl #1 } + \prop_set_eq:cN { \c__xtemplate_values_root_tl #1 } + \l__xtemplate_values_prop + } +\cs_new_protected:Npn \__xtemplate_store_restrictions:n #1 + { + \clist_gclear_new:c { \c__xtemplate_restrict_root_tl #1 } + \clist_gset_eq:cN { \c__xtemplate_restrict_root_tl #1 } + \l__xtemplate_restrict_clist + } +\cs_new_protected:Npn \__xtemplate_store_vars:n #1 + { + \prop_gclear_new:c { \c__xtemplate_vars_root_tl #1 } + \prop_gset_eq:cN { \c__xtemplate_vars_root_tl #1 } + \l__xtemplate_vars_prop + } +\cs_new_protected:Npn \__xtemplate_recover_defaults:n #1 + { + \prop_if_exist:cTF + { \c__xtemplate_defaults_root_tl #1 } + { + \prop_set_eq:Nc \l__xtemplate_values_prop + { \c__xtemplate_defaults_root_tl #1 } + } + { \prop_clear:N \l__xtemplate_values_prop } + } +\cs_new_protected:Npn \__xtemplate_recover_keytypes:n #1 + { + \prop_if_exist:cTF + { \c__xtemplate_keytypes_root_tl #1 } + { + \prop_set_eq:Nc \l__xtemplate_keytypes_prop + { \c__xtemplate_keytypes_root_tl #1 } + } + { \prop_clear:N \l__xtemplate_keytypes_prop } + \seq_if_exist:cTF { \c__xtemplate_key_order_root_tl #1 } + { + \seq_set_eq:Nc \l__xtemplate_key_order_seq + { \c__xtemplate_key_order_root_tl #1 } + } + { \seq_clear:N \l__xtemplate_key_order_seq } + } +\cs_new_protected:Npn \__xtemplate_recover_restrictions:n #1 + { + \clist_if_exist:cTF + { \c__xtemplate_restrict_root_tl #1 } + { + \clist_set_eq:Nc \l__xtemplate_restrict_clist + { \c__xtemplate_restrict_root_tl #1 } + } + { \clist_clear:N \l__xtemplate_restrict_clist } + } +\cs_new_protected:Npn \__xtemplate_recover_values:n #1 + { + \prop_if_exist:cTF + { \c__xtemplate_values_root_tl #1 } + { + \prop_set_eq:Nc \l__xtemplate_values_prop + { \c__xtemplate_values_root_tl #1 } + } + { \prop_clear:N \l__xtemplate_values_prop } + } +\cs_new_protected:Npn \__xtemplate_recover_vars:n #1 + { + \prop_if_exist:cTF + { \c__xtemplate_vars_root_tl #1 } + { + \prop_set_eq:Nc \l__xtemplate_vars_prop + { \c__xtemplate_vars_root_tl #1 } + } + { \prop_clear:N \l__xtemplate_vars_prop } + } +\cs_new_protected:Npn \__xtemplate_declare_object_type:nn #1#2 + { + \int_set:Nn \l__xtemplate_tmp_int {#2} + \int_compare:nTF { 0 <= \l__xtemplate_tmp_int <= 9 } + { + \msg_info:nnnV { xtemplate } { declare-object-type } + {#1} \l__xtemplate_tmp_int + \prop_gput:NnV \g__xtemplate_object_type_prop {#1} + \l__xtemplate_tmp_int + } + { + \msg_error:nnee { xtemplate } { bad-number-of-arguments } + {#1} { \exp_not:V \l__xtemplate_tmp_int } + } + } +\cs_new_protected:Npn \__xtemplate_declare_template_keys:nnnn #1#2#3#4 + { + \__xtemplate_execute_if_type_exist:nT {#1} + { + \__xtemplate_execute_if_arg_agree:nnT {#1} {#3} + { + \prop_clear:N \l__xtemplate_values_prop + \prop_clear:N \l__xtemplate_keytypes_prop + \seq_clear:N \l__xtemplate_key_order_seq + \keyval_parse:NNn + \__xtemplate_parse_keys_elt:n \__xtemplate_parse_keys_elt:nn {#4} + \__xtemplate_store_defaults:n { #1 / #2 } + \__xtemplate_store_keytypes:n { #1 / #2 } + } + } + } +\cs_new_protected:Npn \__xtemplate_parse_keys_elt:n #1 + { + \__xtemplate_split_keytype:n {#1} + \bool_if:NF \l__xtemplate_error_bool + { + \__xtemplate_execute_if_keytype_exist:oT \l__xtemplate_keytype_tl + { + \seq_map_function:NN \c__xtemplate_keytypes_arg_seq + \__xtemplate_parse_keys_elt_aux:n + \bool_if:NF \l__xtemplate_error_bool + { + \seq_if_in:NoTF \l__xtemplate_key_order_seq + \l__xtemplate_key_name_tl + { + \msg_error:nne { xtemplate } + { duplicate-key-interface } + { \l__xtemplate_key_name_tl } + } + { \__xtemplate_parse_keys_elt_aux: } + } + } + } + } +\cs_new_protected:Npn \__xtemplate_parse_keys_elt_aux:n #1 + { + \str_if_eq:onT \l__xtemplate_keytype_tl {#1} + { + \tl_if_empty:NT \l__xtemplate_keytype_arg_tl + { + \msg_error:nne { xtemplate } + { keytype-requires-argument } {#1} + \bool_set_true:N \l__xtemplate_error_bool + \seq_map_break: + } + } + } +\cs_new:Npn \__xtemplate_parse_keys_elt_aux: + { + \tl_set:Ne \l__xtemplate_tmp_tl + { + \l__xtemplate_keytype_tl + \tl_if_empty:NF \l__xtemplate_keytype_arg_tl + { { \l__xtemplate_keytype_arg_tl } } + } + \prop_put:Noo \l__xtemplate_keytypes_prop \l__xtemplate_key_name_tl + \l__xtemplate_tmp_tl + \seq_put_right:No \l__xtemplate_key_order_seq \l__xtemplate_key_name_tl + \str_if_eq:onT \l__xtemplate_keytype_tl { choice } + { + \exp_args:No \clist_if_in:nnT \l__xtemplate_keytype_arg_tl { unknown } + { \msg_error:nn { xtemplate } { choice-unknown-reserved } } + } + } +\cs_new_protected:Npn \__xtemplate_parse_keys_elt:nn #1#2 + { + \__xtemplate_parse_keys_elt:n {#1} + \use:c { __xtemplate_store_value_ \l__xtemplate_keytype_tl :n } {#2} + } +\cs_new_protected:Npe \__xtemplate_split_keytype:n #1 + { + \exp_not:N \bool_set_false:N \exp_not:N \l__xtemplate_error_bool + \tl_set:Nn \exp_not:N \l__xtemplate_tmp_tl {#1} + \tl_replace_all:Nnn \exp_not:N \l__xtemplate_tmp_tl { : } { \token_to_str:N : } + \tl_if_in:onTF \exp_not:N \l__xtemplate_tmp_tl { \token_to_str:N : } + { + \exp_not:n + { + \tl_clear:N \l__xtemplate_key_name_tl + \exp_after:wN \__xtemplate_split_keytype_aux:w + \l__xtemplate_tmp_tl \s__xtemplate_stop + } + } + { + \exp_not:N \bool_set_true:N \exp_not:N \l__xtemplate_error_bool + \msg_error:nne { xtemplate } { missing-keytype } {#1} + } + } +\use:e + { + \cs_new_protected:Npn \exp_not:N \__xtemplate_split_keytype_aux:w + #1 \token_to_str:N : #2 \s__xtemplate_stop + { + \tl_put_right:Ne \exp_not:N \l__xtemplate_key_name_tl + { + \exp_not:N \tl_trim_spaces:e + { \exp_not:N \tl_to_str:n {#1} } + } + \tl_if_in:nnTF {#2} { \token_to_str:N : } + { + \tl_put_right:Nn \exp_not:N \l__xtemplate_key_name_tl + { \token_to_str:N : } + \exp_not:N \__xtemplate_split_keytype_aux:w #2 \s__xtemplate_stop + } + { + \exp_not:N \tl_if_empty:NTF \exp_not:N \l__xtemplate_key_name_tl + { + \msg_error:nne { xtemplate } { empty-key-name } + { \token_to_str:N : #2 } + } + { \exp_not:N \__xtemplate_split_keytype_arg:n {#2} } + } + } + } +\cs_new_protected:Npn \__xtemplate_split_keytype_arg:n #1 + { + \tl_set:Ne \l__xtemplate_keytype_tl { \tl_trim_spaces:n {#1} } + \tl_clear:N \l__xtemplate_keytype_arg_tl + \cs_set_protected:Npn \__xtemplate_split_keytype_arg_aux:n ##1 + { + \tl_if_in:nnT {#1} {##1} + { + \cs_set:Npn \__xtemplate_split_keytype_arg_aux:w + ####1 ##1 ####2 \s__xtemplate_stop + { + \tl_if_blank:nT {####1} + { + \tl_set:Ne \l__xtemplate_keytype_tl + { \tl_trim_spaces:n {##1} } + \tl_if_blank:nF {####2} + { + \tl_set:Ne \l__xtemplate_keytype_arg_tl + { \use:n ####2 } + } + \seq_map_break: + } + } + \__xtemplate_split_keytype_arg_aux:w #1 \s__xtemplate_stop + } + } + \seq_map_function:NN \c__xtemplate_keytypes_arg_seq + \__xtemplate_split_keytype_arg_aux:n + } +\cs_generate_variant:Nn \__xtemplate_split_keytype_arg:n { o } +\cs_new:Npn \__xtemplate_split_keytype_arg_aux:n #1 { } +\cs_new:Npn \__xtemplate_split_keytype_arg_aux:w #1 \s__xtemplate_stop { } +\cs_new_protected:Npn \__xtemplate_store_value_boolean:n #1 + { \prop_put:Non \l__xtemplate_values_prop \l__xtemplate_key_name_tl {#1} } +\cs_new_protected:Npn \__xtemplate_store_value:n #1 + { \prop_put:Non \l__xtemplate_values_prop \l__xtemplate_key_name_tl {#1} } +\cs_new_eq:NN \__xtemplate_store_value_choice:n \__xtemplate_store_value:n +\cs_new_eq:NN \__xtemplate_store_value_function:n \__xtemplate_store_value:n +\cs_new_eq:NN \__xtemplate_store_value_instance:n \__xtemplate_store_value:n +\cs_new_protected:Npn \__xtemplate_store_value_aux:Nn #1#2 + { \prop_put:Non \l__xtemplate_values_prop \l__xtemplate_key_name_tl {#2} } +\cs_new_protected:Npn \__xtemplate_store_value_integer:n + { \__xtemplate_store_value_aux:Nn \int_eval:n } +\cs_new_protected:Npn \__xtemplate_store_value_length:n + { \__xtemplate_store_value_aux:Nn \dim_eval:n } +\cs_new_protected:Npn \__xtemplate_store_value_muskip:n + { \__xtemplate_store_value_aux:Nn \muskip_eval:n } +\cs_new_protected:Npn \__xtemplate_store_value_real:n + { \__xtemplate_store_value_aux:Nn \fp_eval:n } +\cs_new_protected:Npn \__xtemplate_store_value_skip:n + { \__xtemplate_store_value_aux:Nn \skip_eval:n } +\cs_new_protected:Npn \__xtemplate_store_value_tokenlist:n + { \__xtemplate_store_value_aux:Nn \use:n } +\cs_new_eq:NN \__xtemplate_store_value_commalist:n \__xtemplate_store_value_tokenlist:n +\cs_new_protected:Npn \__xtemplate_declare_template_code:nnnnn #1#2#3#4#5 + { + \__xtemplate_execute_if_type_exist:nT {#1} + { + \__xtemplate_execute_if_arg_agree:nnT {#1}{#3} + { + \__xtemplate_if_keys_exist:nnT {#1} {#2} + { + \__xtemplate_store_key_implementation:nnn {#1} {#2} {#4} + \regex_match:nnTF { \c { AssignTemplateKeys } } {#5} + { \__xtemplate_declare_template_code:nnn { #1 / #2 } {#3} {#5} } + { + \__xtemplate_declare_template_code:nnn + { #1 / #2 } {#3} { \AssignTemplateKeys #5 } + } + } + } + } + } +\cs_new_protected:Npn \__xtemplate_declare_template_code:nnn #1#2#3 + { + \cs_generate_from_arg_count:cNnn + { \c__xtemplate_code_root_tl #1 } + \cs_gset_protected:Npn {#2} {#3} + } +\cs_new_protected:Npn \__xtemplate_store_key_implementation:nnn #1#2#3 + { + \__xtemplate_recover_defaults:n { #1 / #2 } + \__xtemplate_recover_restrictions:n { #1 / #2 } + \__xtemplate_recover_keytypes:n { #1 / #2 } + \prop_clear:N \l__xtemplate_vars_prop + \keyval_parse:nnn + { \__xtemplate_parse_vars_elt:n } { \__xtemplate_parse_vars_elt:nnn { #1 / #2 } } {#3} + \__xtemplate_store_vars:n { #1 / #2 } + \__xtemplate_store_restrictions:n { #1 / #2 } + \prop_map_inline:Nn \l__xtemplate_keytypes_prop + { + \msg_error:nneee { xtemplate } { key-not-implemented } + {##1} {#2} {#1} + } + } +\cs_new_protected:Npn \__xtemplate_parse_vars_elt:n #1 + { \msg_error:nne { xtemplate } { key-no-variable } {#1} } +\cs_new_protected:Npn \__xtemplate_parse_vars_elt:nnn #1#2#3 + { + \tl_set:Ne \l__xtemplate_key_name_tl + { \tl_trim_spaces:e { \tl_to_str:n {#2} } } + \prop_get:NoNTF + \l__xtemplate_keytypes_prop + \l__xtemplate_key_name_tl + \l__xtemplate_keytype_tl + { + \__xtemplate_split_keytype_arg:o \l__xtemplate_keytype_tl + \__xtemplate_parse_vars_elt_aux:nn {#1} {#3} + \prop_remove:NV \l__xtemplate_keytypes_prop \l__xtemplate_key_name_tl + } + { \msg_error:nne { xtemplate } { unknown-key } {#2} } + } +\cs_new_protected:Npn \__xtemplate_parse_vars_elt_aux:nn #1#2 + { + \__xtemplate_parse_vars_elt_aux:nw {#1} #2 global global \s__xtemplate_stop + } +\cs_new_protected:Npn \__xtemplate_parse_vars_elt_aux:nw + #1#2 global #3 global #4 \s__xtemplate_stop + { + \tl_if_blank:nTF {#4} + { \__xtemplate_parse_vars_elt_aux:nnn {#1} { } {#2} } + { + \tl_if_blank:nTF {#2} + { + \exp_args:Nnne \__xtemplate_parse_vars_elt_aux:nnn + {#1} { global } { \tl_trim_spaces:n {#3} } + } + { \msg_error:nnn { xtemplate } { bad-variable } { #2 global #3 } } + } + } +\cs_new_protected:Npn \__xtemplate_parse_vars_elt_aux:nnn #1#2#3 + { + \str_case:VnF \l__xtemplate_keytype_tl + { + { choice } { \__xtemplate_implement_choices:nn {#1} {#3} } + { function } + { + \cs_if_exist:NF #3 + { \cs_new:Npn #3 { } } + \__xtemplate_parse_vars_elt_key:nn {#1} + { + .code:n = + { + \cs_generate_from_arg_count:NNnn + \exp_not:N #3 + \exp_not:c + { cs_ \str_if_eq:nnT {#1} { global } { g } set:Npn } + { \exp_not:o \l__xtemplate_keytype_arg_tl } + {##1} + } + } + \prop_put:Non \l__xtemplate_vars_prop + \l__xtemplate_key_name_tl {#2#3} + } + { instance } + { + \__xtemplate_parse_vars_elt_key:nn {#1} + { + .code:n = + { + \exp_not:c + { cs_ \str_if_eq:nnT {#1} { global } { g } set:Npn } + \exp_not:N #3 { \UseInstance {##1} } + } + } + \prop_put:Non \l__xtemplate_vars_prop + \l__xtemplate_key_name_tl {#2#3} + } + } + { + \tl_if_single:nTF {#3} + { + \cs_if_exist:NF #3 + { \use:c { \__xtemplate_map_var_type: _new:N } #3 } + \__xtemplate_parse_vars_elt_key:nn {#1} + { + . \__xtemplate_map_var_type: + _ \str_if_eq:nnT {#1} { global } { g } set:N + = \exp_not:N #3 + } + \prop_put:Non \l__xtemplate_vars_prop + \l__xtemplate_key_name_tl {#2#3} + } + { \msg_error:nne { xtemplate } { bad-variable } { #2#3 } } + } + } +\cs_new_protected:Npn \__xtemplate_parse_vars_elt_key:nn #1#2 + { + \keys_define:ne { template / #1 } + { \l__xtemplate_key_name_tl #2 } + } +\cs_new:Npn \__xtemplate_map_var_type: + { + \str_case:on \l__xtemplate_keytype_tl + { + { boolean } { bool } + { commalist } { clist } + { integer } { int } + { length } { dim } + { muskip } { muskip } + { real } { fp } + { skip } { skip } + { tokenlist } { tl } + } + } +\cs_new_protected:Npn \__xtemplate_implement_choices:nn #1#2 + { + \clist_set:No \l__xtemplate_tmp_clist { \l__xtemplate_keytype_arg_tl } + \prop_put:Non \l__xtemplate_vars_prop \l__xtemplate_key_name_tl { } + \keys_define:ne { template / #1 } { \l__xtemplate_key_name_tl .choice: } + \keyval_parse:nnn + { \__xtemplate_implement_choice_elt:n } + { \__xtemplate_implement_choice_elt:nnn {#1} } + {#2} + \prop_get:NoNT \l__xtemplate_values_prop \l__xtemplate_key_name_tl + \l__xtemplate_tmp_tl + { \__xtemplate_implement_choices_default: } + \clist_if_empty:NF \l__xtemplate_tmp_clist + { + \clist_map_inline:Nn \l__xtemplate_tmp_clist + { + \msg_error:nne { xtemplate } { choice-not-implemented } + {##1} + } + } + } +\cs_new_protected:Npn \__xtemplate_implement_choices_default: + { + \tl_set:Ne \l__xtemplate_tmp_tl + { \l__xtemplate_key_name_tl \c_space_tl \l__xtemplate_tmp_tl } + \prop_if_in:NoF \l__xtemplate_vars_prop \l__xtemplate_tmp_tl + { + \tl_set:Ne \l__xtemplate_tmp_tl + { \l__xtemplate_key_name_tl \c_space_tl \l__xtemplate_tmp_tl } + \prop_if_in:NoF \l__xtemplate_vars_prop \l__xtemplate_tmp_tl + { + \prop_get:NoN \l__xtemplate_keytypes_prop \l__xtemplate_key_name_tl + \l__xtemplate_tmp_tl + \__xtemplate_split_keytype_arg:o \l__xtemplate_tmp_tl + \prop_get:NoN \l__xtemplate_values_prop \l__xtemplate_key_name_tl + \l__xtemplate_tmp_tl + \msg_error:nneee { xtemplate } { unknown-default-choice } + { \l__xtemplate_key_name_tl } { \l__xtemplate_key_name_tl } + { \l__xtemplate_keytype_arg_tl } + } + } + } +\cs_new_protected:Npn \__xtemplate_implement_choice_elt:nnn #1#2#3 + { + \clist_if_empty:NTF \l__xtemplate_tmp_clist + { + \str_if_eq:nnTF {#2} { unknown } + { \__xtemplate_implement_choice_elt_aux:nnn {#1} {#2} {#3} } + { + \prop_get:NoN \l__xtemplate_keytypes_prop \l__xtemplate_key_name_tl + \l__xtemplate_tmp_tl + \__xtemplate_split_keytype_arg:o \l__xtemplate_tmp_tl + \msg_error:nneee { xtemplate } { unknown-choice } + { \l__xtemplate_key_name_tl } {#2} + { \l__xtemplate_keytype_arg_tl } + } + } + { + \clist_if_in:NnTF \l__xtemplate_tmp_clist {#2} + { + \clist_remove_all:Nn \l__xtemplate_tmp_clist {#2} + \__xtemplate_implement_choice_elt_aux:nnn {#1} {#2} {#3} + } + { + \prop_get:NoN \l__xtemplate_keytypes_prop \l__xtemplate_key_name_tl + \l__xtemplate_tmp_tl + \__xtemplate_split_keytype_arg:o \l__xtemplate_tmp_tl + \msg_error:nneee { xtemplate } { unknown-choice } + { \l__xtemplate_key_name_tl } {#2} + { \l__xtemplate_keytype_arg_tl } + } + } + } +\cs_new_protected:Npn \__xtemplate_implement_choice_elt_aux:nnn #1#2#3 + { + \keys_define:ne { template / #1 } + { \l__xtemplate_key_name_tl / #2 .code:n = { \exp_not:n {#3} } } + \tl_set:Ne \l__xtemplate_tmp_tl + { \l__xtemplate_key_name_tl \c_space_tl #2 } + \prop_put:Non \l__xtemplate_vars_prop \l__xtemplate_tmp_tl {#3} + } +\cs_new_protected:Npn \__xtemplate_implement_choice_elt:n #1 + { + \msg_error:nneee { xtemplate } { choice-requires-code } + { \l__xtemplate_key_name_tl } {#1} + } +\cs_new_protected:Npn \__xtemplate_declare_restricted:nnnn #1#2#3#4 + { + \__xtemplate_if_keys_exist:nnT {#1} {#2} + { + \__xtemplate_set_template_eq:nn { #1 / #3 } { #1 / #2 } + \bool_set_true:N \l__xtemplate_restrict_bool + \__xtemplate_edit_defaults_aux:nnn {#1} {#3} {#4} + \bool_set_false:N \l__xtemplate_restrict_bool + } + } +\cs_new_protected:Npn \__xtemplate_edit_defaults:nnn + { + \bool_set_false:N \l__xtemplate_restrict_bool + \__xtemplate_edit_defaults_aux:nnn + } +\cs_new_protected:Npn \__xtemplate_edit_defaults_aux:nnn #1#2#3 + { + \__xtemplate_if_keys_exist:nnT {#1} {#2} + { + \__xtemplate_recover_defaults:n { #1 / #2 } + \__xtemplate_recover_restrictions:n { #1 / #2 } + \__xtemplate_parse_values:nn { #1 / #2 } {#3} + \__xtemplate_store_defaults:n { #1 / #2 } + \__xtemplate_store_restrictions:n { #1 / #2 } + } + } +\cs_new_protected:Npn \__xtemplate_parse_values:nn #1#2 + { + \clist_clear:N \l__xtemplate_restrict_clist + \__xtemplate_parse_values_aux:nn {#1} {#2} + } +\cs_new_protected:Npn \__xtemplate_parse_values_aux:nn #1#2 + { + \__xtemplate_recover_keytypes:n {#1} + \keyval_parse:NNn + \__xtemplate_parse_values_elt:n \__xtemplate_parse_values_elt:nn {#2} + } +\cs_new_protected:Npn \__xtemplate_parse_values_elt:n #1 + { + \bool_set_true:N \l__xtemplate_error_bool + \msg_error:nne { xtemplate } { key-no-value } {#1} + } +\cs_new_protected:Npn \__xtemplate_parse_values_elt:nn #1#2 + { + \tl_set:Ne \l__xtemplate_key_name_tl + { \tl_trim_spaces:e { \tl_to_str:n {#1} } } + \prop_get:NoNTF \l__xtemplate_keytypes_prop \l__xtemplate_key_name_tl + \l__xtemplate_tmp_tl + { + \bool_if:NTF \l__xtemplate_restrict_bool + { + \clist_if_in:NoF \l__xtemplate_restrict_clist + \l__xtemplate_key_name_tl + { \__xtemplate_parse_values_elt_aux:n {#2} } + } + { \__xtemplate_parse_values_elt_aux:n {#2} } + } + { + \msg_error:nne { xtemplate } { unknown-key } + { \l__xtemplate_key_name_tl } + } + } +\cs_new_protected:Npn \__xtemplate_parse_values_elt_aux:n #1 + { + \clist_put_right:No \l__xtemplate_restrict_clist \l__xtemplate_key_name_tl + \__xtemplate_split_keytype_arg:o \l__xtemplate_tmp_tl + \use:c { __xtemplate_store_value_ \l__xtemplate_keytype_tl :n } {#1} + } +\cs_new_protected:Npn \__xtemplate_set_template_eq:nn #1#2 + { + \__xtemplate_recover_defaults:n {#2} + \__xtemplate_store_defaults:n {#1} + \__xtemplate_recover_keytypes:n {#2} + \__xtemplate_store_keytypes:n {#1} + \__xtemplate_recover_vars:n {#2} + \__xtemplate_store_vars:n {#1} + \cs_gset_eq:cc { \c__xtemplate_code_root_tl #1 } + { \c__xtemplate_code_root_tl #2 } + } +\cs_new_protected:Npn \__xtemplate_declare_instance:nnnnn #1#2#3#4#5 + { + \__xtemplate_execute_if_code_exist:nnT {#1} {#2} + { + \__xtemplate_recover_defaults:n { #1 / #2 } + \__xtemplate_recover_restrictions:n { #1 / #2 } + \__xtemplate_recover_vars:n { #1 / #2 } + \__xtemplate_declare_instance_aux:nnnnn {#1} {#2} {#3} {#4} {#5} + } + } +\cs_new_protected:Npn \__xtemplate_declare_instance_aux:nnnnn #1#2#3#4#5 + { + \bool_set_false:N \l__xtemplate_error_bool + \__xtemplate_parse_values_aux:nn { #1 / #2 } {#5} + \bool_if:NF \l__xtemplate_error_bool + { + \prop_put:Nnn \l__xtemplate_values_prop { from~template } {#2} + \__xtemplate_store_values:n { #1 / #3 / #4 } + \__xtemplate_convert_to_assignments: + \cs_set_protected:cpe { \c__xtemplate_instances_root_tl #1 / #3 / #4 } + { + \exp_not:N \__xtemplate_assignments_push:n + { \exp_not:o \l__xtemplate_assignments_tl } + \exp_not:c { \c__xtemplate_code_root_tl #1 / #2 } + } + \__xtemplate_if_instance_exist:nnnF {#1} { } {#4} + { + \cs_set_eq:cc + { \c__xtemplate_instances_root_tl #1 / / #4 } + { \c__xtemplate_instances_root_tl #1 / #3 / #4 } + } + } + } +\cs_new_protected:Npn \__xtemplate_copy_instance:nnnn #1#2#3#4 + { + \__xtemplate_if_instance_exist:nnnTF {#1} {#2} {#3} + { + \__xtemplate_recover_values:n { #1 / #2 / #3 } + \__xtemplate_store_values:n { #1 / #2 / #4 } + \cs_set_eq:cc { \c__xtemplate_instances_root_tl #1 / #2 / #4 } + { \c__xtemplate_instances_root_tl #1 / #2 / #3 } + } + { + \msg_error:nnee { xtemplate } { unknown-instance } + {#1} {#3} + } + } +\cs_new_protected:Npn \__xtemplate_edit_instance:nnnn #1#2#3#4 + { + \__xtemplate_if_instance_exist:nnnTF {#1} {#2} {#3} + { + \__xtemplate_recover_values:n { #1 / #2 / #3 } + \prop_get:NnN \l__xtemplate_values_prop { from~template } + \l__xtemplate_tmp_tl + \__xtemplate_edit_instance_aux:nonnn {#1} \l__xtemplate_tmp_tl + {#2} {#3} {#4} + } + { + \msg_error:nnee { xtemplate } { unknown-instance } + {#1} {#3} + } + } +\cs_new_protected:Npn \__xtemplate_edit_instance_aux:nnnnn #1#2 + { + \__xtemplate_recover_vars:n { #1 / #2 } + \__xtemplate_declare_instance_aux:nnnnn {#1} {#2} + } +\cs_generate_variant:Nn \__xtemplate_edit_instance_aux:nnnnn { no } +\cs_new_protected:Npn \__xtemplate_convert_to_assignments: + { + \tl_clear:N \l__xtemplate_assignments_tl + \seq_map_function:NN \l__xtemplate_key_order_seq + \__xtemplate_convert_to_assignments_aux:n + } +\cs_new_protected:Npn \__xtemplate_convert_to_assignments_aux:n #1 + { + \prop_get:NnN \l__xtemplate_keytypes_prop {#1} \l__xtemplate_tmp_tl + \__xtemplate_convert_to_assignments_aux:no {#1} \l__xtemplate_tmp_tl + } +\cs_new_protected:Npn \__xtemplate_convert_to_assignments_aux:nn #1#2 + { + \prop_get:NnNT \l__xtemplate_values_prop {#1} \l__xtemplate_value_tl + { + \prop_get:NnNTF \l__xtemplate_vars_prop {#1} \l__xtemplate_var_tl + { + \__xtemplate_split_keytype_arg:n {#2} + \str_if_eq:onF \l__xtemplate_keytype_tl { choice } + { + \str_if_eq:onF \l__xtemplate_keytype_tl { code } + { \__xtemplate_find_global: } + } + \tl_set:Nn \l__xtemplate_key_name_tl {#1} + \use:c { __xtemplate_assign_ \l__xtemplate_keytype_tl : } + } + { \msg_error:nne { xtemplate } { unknown-attribute } {#1} } + } + } +\cs_generate_variant:Nn \__xtemplate_convert_to_assignments_aux:nn { no } +\cs_new_protected:Npn \__xtemplate_find_global: + { + \bool_set_false:N \l__xtemplate_global_bool + \tl_if_in:onT \l__xtemplate_var_tl { global } + { + \exp_after:wN \__xtemplate_find_global_aux:w \l__xtemplate_var_tl \s__xtemplate_stop + } + } +\cs_new_protected:Npn \__xtemplate_find_global_aux:w #1 global #2 \s__xtemplate_stop + { + \tl_set:Nn \l__xtemplate_var_tl {#2} + \bool_set_true:N \l__xtemplate_global_bool + } +\cs_new_protected:Npn \__xtemplate_use_template:nnn #1#2#3 + { + \__xtemplate_execute_if_code_exist:nnT {#1} {#2} + { + \__xtemplate_recover_defaults:n { #1 / #2 } + \__xtemplate_recover_vars:n { #1 / #2 } + \__xtemplate_parse_values:nn { #1 / #2 } {#3} + \__xtemplate_convert_to_assignments: + \use:c { \c__xtemplate_code_root_tl #1 / #2 } + } + } +\cs_new_protected:Npn \__xtemplate_assign_boolean: + { + \bool_if:NTF \l__xtemplate_global_bool + { \__xtemplate_assign_boolean_aux:n { bool_gset } } + { \__xtemplate_assign_boolean_aux:n { bool_set } } + } +\cs_new_protected:Npn \__xtemplate_assign_boolean_aux:n #1 + { + \__xtemplate_if_key_value:oTF \l__xtemplate_value_tl + { + \__xtemplate_key_to_value: + \tl_put_right:Ne \l__xtemplate_assignments_tl + { + \exp_not:c { #1 _eq:NN } + \exp_not:o \l__xtemplate_var_tl + \exp_not:o \l__xtemplate_value_tl + } + } + { + \tl_put_right:Ne \l__xtemplate_assignments_tl + { + \exp_not:c { #1 _ \l__xtemplate_value_tl :N } + \exp_not:o \l__xtemplate_var_tl + } + } + } +\cs_new_protected:Npn \__xtemplate_assign_choice: + { + \__xtemplate_assign_choice_aux:eF + { \l__xtemplate_key_name_tl \c_space_tl \l__xtemplate_value_tl } + { + \__xtemplate_assign_choice_aux:eF + { \l__xtemplate_key_name_tl \c_space_tl unknown } + { + \prop_get:NoN \l__xtemplate_keytypes_prop \l__xtemplate_key_name_tl + \l__xtemplate_tmp_tl + \__xtemplate_split_keytype_arg:o \l__xtemplate_tmp_tl + \msg_error:nneee { xtemplate } { unknown-choice } + { \l__xtemplate_key_name_tl } { \l__xtemplate_value_tl } + { \l__xtemplate_keytype_arg_tl } + } + } + } +\cs_new_protected:Npn \__xtemplate_assign_choice_aux:nF #1 + { + \prop_get:NnNTF + \l__xtemplate_vars_prop + {#1} + \l__xtemplate_tmp_tl + { \tl_put_right:No \l__xtemplate_assignments_tl \l__xtemplate_tmp_tl } + } +\cs_generate_variant:Nn \__xtemplate_assign_choice_aux:nF { e } +\cs_new_protected:Npn \__xtemplate_assign_function: + { + \bool_if:NTF \l__xtemplate_global_bool + { \__xtemplate_assign_function_aux:N \cs_gset:Npn } + { \__xtemplate_assign_function_aux:N \cs_set:Npn } + } +\cs_new_protected:Npn \__xtemplate_assign_function_aux:N #1 + { + \tl_put_right:Ne \l__xtemplate_assignments_tl + { + \cs_generate_from_arg_count:NNnn + \exp_not:o \l__xtemplate_var_tl + \exp_not:N #1 + { \exp_not:o \l__xtemplate_keytype_arg_tl } + { \exp_not:o \l__xtemplate_value_tl } + } + } +\cs_new_protected:Npn \__xtemplate_assign_instance: + { + \bool_if:NTF \l__xtemplate_global_bool + { \__xtemplate_assign_instance_aux:N \cs_gset_protected:Npn } + { \__xtemplate_assign_instance_aux:N \cs_set_protected:Npn } + } +\cs_new_protected:Npn \__xtemplate_assign_instance_aux:N #1 + { + \tl_put_right:Ne \l__xtemplate_assignments_tl + { + \exp_not:N #1 \exp_not:o \l__xtemplate_var_tl + { + \__xtemplate_use_instance:nn + { \exp_not:o \l__xtemplate_keytype_arg_tl } + { \exp_not:o \l__xtemplate_value_tl } + } + } + } +\cs_new_protected:Npn \__xtemplate_assign_integer: + { + \bool_if:NTF \l__xtemplate_global_bool + { \__xtemplate_assign_variable:N \int_gset:Nn } + { \__xtemplate_assign_variable:N \int_set:Nn } + } +\cs_new_protected:Npn \__xtemplate_assign_length: + { + \bool_if:NTF \l__xtemplate_global_bool + { \__xtemplate_assign_variable:N \dim_gset:Nn } + { \__xtemplate_assign_variable:N \dim_set:Nn } +} +\cs_new_protected:Npn \__xtemplate_assign_muskip: + { + \bool_if:NTF \l__xtemplate_global_bool + { \__xtemplate_assign_variable:N \muskip_gset:Nn } + { \__xtemplate_assign_variable:N \muskip_set:Nn } + } +\cs_new_protected:Npn \__xtemplate_assign_real: + { + \bool_if:NTF \l__xtemplate_global_bool + { \__xtemplate_assign_variable:N \fp_gset:Nn } + { \__xtemplate_assign_variable:N \fp_set:Nn } + } +\cs_new_protected:Npn \__xtemplate_assign_skip: + { + \bool_if:NTF \l__xtemplate_global_bool + { \__xtemplate_assign_variable:N \skip_gset:Nn } + { \__xtemplate_assign_variable:N \skip_set:Nn } + } +\cs_new_protected:Npn \__xtemplate_assign_tokenlist: + { + \bool_if:NTF \l__xtemplate_global_bool + { \__xtemplate_assign_tokenlist_aux:NN \tl_gset:NV \tl_gset:Nn } + { \__xtemplate_assign_tokenlist_aux:NN \tl_set:NV \tl_set:Nn } + } +\cs_new_protected:Npn \__xtemplate_assign_tokenlist_aux:NN #1#2 + { + \__xtemplate_if_key_value:oTF \l__xtemplate_value_tl + { + \__xtemplate_key_to_value: + \tl_put_right:Ne \l__xtemplate_assignments_tl + { + #1 \exp_not:o \l__xtemplate_var_tl + \exp_not:o \l__xtemplate_value_tl + } + } + { + \tl_put_right:Ne \l__xtemplate_assignments_tl + { + #2 \exp_not:o \l__xtemplate_var_tl + { \exp_not:o \l__xtemplate_value_tl } + } + } + } +\cs_new_protected:Npn \__xtemplate_assign_commalist: + { + \bool_if:NTF \l__xtemplate_global_bool + { \__xtemplate_assign_tokenlist_aux:NN \clist_gset:NV \clist_gset:Nn } + { \__xtemplate_assign_tokenlist_aux:NN \clist_set:NV \clist_set:Nn } + } +\cs_new_protected:Npn \__xtemplate_assign_variable:N #1 + { + \__xtemplate_if_key_value:oT \l__xtemplate_value_tl + { \__xtemplate_key_to_value: } + \tl_put_right:Ne \l__xtemplate_assignments_tl + { + #1 \exp_not:o \l__xtemplate_var_tl + { \exp_not:o \l__xtemplate_value_tl } + } + } +\cs_new_protected:Npn \__xtemplate_key_to_value: + { \exp_after:wN \__xtemplate_key_to_value_auxi:w \l__xtemplate_value_tl } +\cs_new_protected:Npn \__xtemplate_key_to_value_auxi:w \KeyValue #1 + { + \tl_set:Ne \l__xtemplate_tmp_tl { \tl_trim_spaces:e { \tl_to_str:n {#1} } } + \prop_get:NoNTF + \l__xtemplate_vars_prop + \l__xtemplate_tmp_tl + \l__xtemplate_value_tl + { + \exp_after:wN \__xtemplate_key_to_value_auxii:w \l__xtemplate_value_tl + \s__xtemplate_mark global \q__xtemplate_nil \s__xtemplate_stop + } + { + \msg_error:nne { xtemplate } { unknown-attribute } + { \l__xtemplate_tmp_tl } + } + } +\cs_new_protected:Npn \__xtemplate_key_to_value_auxii:w #1 global #2#3 \s__xtemplate_stop + { + \__xtemplate_quark_if_nil:NF #2 + { \tl_set:Nn \l__xtemplate_value_tl {#2} } + } +\cs_new_protected:Npn \__xtemplate_use_instance:nn #1#2 + { + \__xtemplate_if_use_template:nTF {#2} + { \__xtemplate_use_instance_aux:nNnnn {#1} #2 } + { \__xtemplate_use_instance_aux:nn {#1} {#2} } + } +\cs_new_protected:Npn \__xtemplate_use_instance_aux:nNnnn #1#2#3#4#5 + { + \str_if_eq:nnTF {#1} {#3} + { \__xtemplate_use_template:nnn {#3} {#4} {#5} } + { \msg_error:nnee { xtemplate } { type-mismatch } {#1} {#3} } +} +\cs_new_protected:Npn \__xtemplate_use_instance_aux:nn #1#2 + { + \__xtemplate_get_collection:n {#1} + \__xtemplate_if_instance_exist:nnnTF + {#1} { \l__xtemplate_collection_tl } {#2} + { + \use:c + { + \c__xtemplate_instances_root_tl #1 / + \l__xtemplate_collection_tl / #2 + } + } + { + \__xtemplate_if_instance_exist:nnnTF {#1} { } {#2} + { \use:c { \c__xtemplate_instances_root_tl #1 / / #2 } } + { + \msg_error:nnee { xtemplate } { unknown-instance } + {#1} {#2} + } + } + } +\cs_new_protected:Npn \__xtemplate_use_collection:nn #1#2 + { \prop_put:Nnn \l__xtemplate_collections_prop {#1} {#2} } +\cs_new_protected:Npn \__xtemplate_get_collection:n #1 + { + \prop_get:NnNF \l__xtemplate_collections_prop {#1} + \l__xtemplate_collection_tl + { \tl_clear:N \l__xtemplate_collection_tl } + } +\cs_new:Npn \__xtemplate_assignments_pop: { \l__xtemplate_assignments_tl } +\cs_new_protected:Npn \__xtemplate_assignments_push:n #1 + { \tl_set:Nn \l__xtemplate_assignments_tl {#1} } +\cs_new_protected:Npn \__xtemplate_show_code:nn #1#2 + { \cs_show:c { \c__xtemplate_code_root_tl #1 / #2 } } +\cs_new_protected:Npn \__xtemplate_show_defaults:nn #1#2 + { + \__xtemplate_if_keys_exist:nnT {#1} {#2} + { + \__xtemplate_recover_defaults:n { #1 / #2 } + \__xtemplate_show:Nnnn \l__xtemplate_values_prop + {#1} {#2} { default~values } + } + } +\cs_new_protected:Npn \__xtemplate_show_keytypes:nn #1#2 + { + \__xtemplate_if_keys_exist:nnT {#1} {#2} + { + \__xtemplate_recover_keytypes:n { #1 / #2 } + \__xtemplate_show:Nnnn \l__xtemplate_keytypes_prop + {#1} {#2} { interface } + } + } +\cs_new_protected:Npn \__xtemplate_show_vars:nn #1#2 + { + \__xtemplate_execute_if_code_exist:nnT {#1} {#2} + { + \__xtemplate_recover_vars:n { #1 / #2 } + \__xtemplate_show:Nnnn \l__xtemplate_vars_prop + {#1} {#2} { variable~mapping } + } + } +\cs_new_protected:Npn \__xtemplate_show:Nnnn #1#2#3#4 + { + \msg_show:nneeee { 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 } + } +\cs_new_protected:Npn \__xtemplate_show_values:nnn #1#2#3 + { + \__xtemplate_if_instance_exist:nnnT {#1} {#2} {#3} + { + \__xtemplate_recover_values:n { #1 / #2 / #3 } + \msg_show:nneeee { xtemplate } { show-values } + { \tl_to_str:n {#1} } + { \tl_to_str:n {#2} } + { \tl_to_str:n {#3} } + { + \prop_map_function:NN \l__xtemplate_values_prop + \msg_show_item_unbraced:nn + } + } + } +\msg_new:nnnn { xtemplate } { argument-number-mismatch } + { Object~type~'#1'~takes~#2~argument(s). } + { + Objects~of~type~'#1'~require~#2~argument(s).\\ + You~have~tried~to~make~a~template~for~'#1'~ + with~#3~argument(s),~which~is~not~possible:~ + the~number~of~arguments~must~agree. + } +\msg_new:nnnn { xtemplate } { bad-number-of-arguments } + { Bad~number~of~arguments~for~object~type~'#1'. } + { + An~object~may~accept~between~0~and~9~arguments.\\ + You~asked~to~use~#2~arguments:~this~is~not~supported. + } +\msg_new:nnnn { xtemplate } { bad-variable } + { Incorrect~variable~description~'#1'. } + { + The~argument~'#1'~is~not~of~the~form \\ + ~~'<variable>'\\ + ~or~\\ + ~~'global~<variable>'.\\ + It~must~be~given~in~one~of~these~formats~to~be~used~in~a~template. + } +\msg_new:nnnn { xtemplate } { choice-not-implemented } + { The~choice~'#1'~has~no~implementation. } + { + Each~choice~listed~in~the~interface~for~a~template~must~ + have~an~implementation. + } +\msg_new:nnnn { xtemplate } { choice-no-code } + { The~choice~'#1'~requires~implementation~details. } + { + When~creating~template~code~using~\DeclareTemplateCode,~ + each~choice~name~must~have~an~associated~implementation.\\ + This~should~be~given~after~a~'='~sign:~LaTeX~did~not~find~one. + } +\msg_new:nnnn { xtemplate } { choice-requires-code } + { The~choice~'#2'~for~key~'#1'~requires~an~implementation. } + { + You~should~have~put:\\ + \ \ #1~:~choice~{~#2 = <code> ~} \\ + but~LaTeX~did~not~find~any~<code>. + } +\msg_new:nnnn { xtemplate } { duplicate-key-interface } + { Key~'#1'~appears~twice~in~interface~definition~\msg_line_context:. } + { + Each~key~can~only~have~one~interface~declared~in~a~template.\\ + LaTeX~found~two~interfaces~for~'#1'. + } +\msg_new:nnnn { xtemplate } { keytype-requires-argument } + { The~key~type~'#1'~requires~an~argument~\msg_line_context:. } + { + You~should~have~put:\\ + \ \ <key-name>~:~#1~{~<argument>~} \\ + but~LaTeX~did~not~find~an~<argument>. + } +\msg_new:nnnn { xtemplate } { invalid-keytype } + { The~key~'#1'~is~missing~a~key-type~\msg_line_context:. } + { + Each~key~in~a~template~requires~a~key-type,~given~in~the~form:\\ + \ \ <key>~:~<key-type>\\ + LaTeX~could~not~find~a~<key-type>~in~your~input. + } +\msg_new:nnnn { xtemplate } { key-no-value } + { The~key~'#1'~has~no~value~\msg_line_context:. } + { + When~creating~an~instance~of~a~template~ + every~key~listed~must~include~a~value:\\ + \ \ <key>~=~<value> + } +\msg_new:nnnn { xtemplate } { key-no-variable } + { The~key~'#1'~requires~implementation~details~\msg_line_context:. } + { + When~creating~template~code~using~\DeclareTemplateCode,~ + each~key~name~must~have~an~associated~implementation.\\ + This~should~be~given~after~a~'='~sign:~LaTeX~did~not~find~one. + } +\msg_new:nnnn { xtemplate } { key-not-implemented } + { Key~'#1'~has~no~implementation~\msg_line_context:. } + { + The~definition~of~key~implementations~for~template~'#2'~ + of~object~type~'#3'~does~not~include~any~details~for~key~'#1'.\\ + The~key~was~declared~in~the~interface~definition,~ + and~so~an~implementation~is~required. + } +\msg_new:nnnn { xtemplate } { missing-keytype } + { The~key~'#1'~is~missing~a~key-type~\msg_line_context:. } + { + Key~interface~definitions~should~be~of~the~form\\ + \ \ #1~:~<key-type>\\ + but~LaTeX~could~not~find~a~<key-type>. + } +\msg_new:nnnn { xtemplate } { no-template-code } + { + The~template~'#2'~of~type~'#1'~is~unknown~ + or~has~no~implementation. + } + { + There~is~no~code~available~for~the~template~name~given.\\ + This~should~be~given~using~\DeclareTemplateCode. + } +\msg_new:nnnn { xtemplate } { object-type-mismatch } + { Object~types~'#1'~and~'#2'~do~not~agree. } + { + You~are~trying~to~use~a~template~directly~with~\UseInstance + (or~a~similar~function),~but~the~object~types~do~not~match. + } +\msg_new:nnnn { xtemplate } { unknown-attribute } + { The~template~attribute~'#1'~is~unknown. } + { + There~is~a~definition~in~the~current~template~reading\\ + \ \ \token_to_str:N \KeyValue {~#1~} \\ + but~there~is~no~key~called~'#1'. + } +\msg_new:nnnn { xtemplate } { unknown-choice } + { The~choice~'#2'~was~not~declared~for~key~'#1'. } + { + The~key~'#1'~takes~a~fixed~list~of~choices~ + and~this~list~does~not~include~'#2'. + } +\msg_new:nnnn { xtemplate } { unknown-default-choice } + { The~default~choice~'#2'~was~not~declared~for~key~'#1'. } + { + The~key~'#1'~takes~a~fixed~list~of~choices~ + and~this~list~does~not~include~'#2'. + } +\msg_new:nnnn { xtemplate } { unknown-instance } + { The~instance~'#2'~of~type~'#1'~is~unknown. } + { + You~have~asked~to~use~an~instance~'#2',~ + but~this~has~not~been~created. + } +\msg_new:nnnn { xtemplate } { unknown-key } + { Unknown~template~key~'#1'. } + { + The~key~'#1'~was~not~declared~in~the~interface~ + for~the~current~template. + } +\msg_new:nnnn { xtemplate } { unknown-keytype } + { The~key-type~'#1'~is~unknown. } + { + Valid~key-types~are:\\ + -~boolean;\\ + -~choice;\\ + -~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'. + } +\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 } + } +\cs_new_protected:Npn \DeclareObjectType #1#2 + { \__xtemplate_declare_object_type:nn {#1} {#2} } +\cs_new_protected:Npn \DeclareTemplateInterface #1#2#3#4 + { \__xtemplate_declare_template_keys:nnnn {#1} {#2} {#3} {#4} } +\cs_new_protected:Npn \DeclareTemplateCode #1#2#3#4#5 + { \__xtemplate_declare_template_code:nnnnn {#1} {#2} {#3} {#4} {#5} } +\cs_new_protected:Npn \DeclareRestrictedTemplate #1#2#3#4 + { \__xtemplate_declare_restricted:nnnn {#1} {#2} {#3} {#4} } +\cs_new_protected:Npn \DeclareInstance #1#2#3#4 + { \__xtemplate_declare_instance:nnnnn {#1} {#3} { } {#2} {#4} } +\cs_new_protected:Npn \DeclareCollectionInstance #1#2#3#4#5 + { \__xtemplate_declare_instance:nnnnn {#2} {#4} {#1} {#3} {#5} } +\cs_new_protected:Npn \DeclareInstanceCopy #1#2#3 + { \__xtemplate_copy_instance:nnnn {#1} { } {#3} {#2} } +\cs_new_protected:Npn \EditTemplateDefaults #1#2#3 + { \__xtemplate_edit_defaults:nnn {#1} {#2} {#3} } +\cs_new_protected:Npn \EditInstance #1#2#3 + { \__xtemplate_edit_instance:nnnn {#1} { } {#2} {#3} } +\cs_new_protected:Npn \EditCollectionInstance #1#2#3#4 + { \__xtemplate_edit_instance:nnnn {#2} {#1} {#3} {#4} } +\cs_new_protected:Npn \UseTemplate #1#2#3 + { \__xtemplate_use_template:nnn {#1} {#2} {#3} } +\cs_new_protected:Npn \UseInstance #1#2 + { \__xtemplate_use_instance:nn {#1} {#2} } +\cs_new_protected:Npn \UseCollection #1#2 + { \__xtemplate_use_collection:nn {#1} {#2} } +\cs_new_protected:Npn \ShowTemplateCode #1#2 + { \__xtemplate_show_code:nn {#1} {#2} } +\cs_new_protected:Npn \ShowTemplateDefaults #1#2 + { \__xtemplate_show_defaults:nn {#1} {#2} } +\cs_new_protected:Npn \ShowTemplateInterface #1#2 + { \__xtemplate_show_keytypes:nn {#1} {#2} } +\cs_new_protected:Npn \ShowTemplateVariables #1#2 + { \__xtemplate_show_vars:nn {#1} {#2} } +\cs_new_protected:Npn \ShowInstanceValues #1#2 + { \__xtemplate_show_values:nnn {#1} { } {#2} } +\cs_new_protected:Npn \ShowCollectionInstanceValues #1#2#3 + { \__xtemplate_show_values:nnn {#1} {#2} {#3} } +\cs_new:Npn \IfInstanceExistTF #1#2 + { \__xtemplate_if_instance_exist:nnnTF {#1} { } {#2} } +\cs_new:Npn \IfInstanceExistT #1#2 + { \__xtemplate_if_instance_exist:nnnT {#1} { } {#2} } +\cs_new:Npn \IfInstanceExistF #1#2 + { \__xtemplate_if_instance_exist:nnnF {#1} { } {#2} } +\cs_new_protected:Npn \KeyValue #1 {#1} +\cs_new_protected:Npn \AssignTemplateKeys + { \__xtemplate_assignments_pop: } +\cs_new_protected:Npn \SetTemplateKeys #1#2#3 + { \keys_set_known:nnN { template / #1 / #2 } {#3} \l__xtemplate_tmp_clist } +\cs_new_eq:NN \ShowTemplateKeytypes \ShowTemplateInterface +%% +%% +%% End of file `xtemplate.sty'. diff --git a/macros/latex/contrib/l3packages/xtemplate.dtx b/macros/latex/contrib/l3packages/xtemplate.dtx index 5080d94b0f..ee393ef3b3 100644 --- a/macros/latex/contrib/l3packages/xtemplate.dtx +++ b/macros/latex/contrib/l3packages/xtemplate.dtx @@ -4,7 +4,7 @@ % % Copyright (C) 1999 Frank Mittelbach, Chris Rowley, David Carlisle % (C) 2004-2010 Frank Mittelbach, The LaTeX Project -% (C) 2011-2023 The LaTeX Project +% (C) 2011-2024 The LaTeX Project %\ % It may be distributed and/or modified under the conditions of the % LaTeX Project Public License (LPPL), either version 1.3c of this @@ -24,23 +24,6 @@ % % for those people who are interested. % -%<*driver|package> -% The version of expl3 required is tested as early as possible, as -% some really old versions do not define \ProvidesExplPackage. -\RequirePackage{expl3}[2018/02/21] -%<package>\@ifpackagelater{expl3}{2018/02/21} -%<package> {} -%<package> {% -%<package> \PackageError{xtemplate}{Support package l3kernel too old} -%<package> {% -%<package> Please install an up to date version of l3kernel\MessageBreak -%<package> using your TeX package manager or from CTAN.\MessageBreak -%<package> \MessageBreak -%<package> Loading xtemplate will abort!% -%<package> }% -%<package> \endinput -%<package> } -%</driver|package> %<*driver> \documentclass[full]{l3doc} \usepackage{amstext} @@ -51,7 +34,7 @@ % \fi % % \title{^^A -% The \textsf{xtemplate} package\\ Prototype document functions^^A +% The \pkg{xtemplate} package\\ Prototype document functions^^A % } % % \author{^^A @@ -63,7 +46,7 @@ % }^^A % } % -% \date{Released 2023-10-10} +% \date{Released 2024-02-13} % % \maketitle % @@ -98,7 +81,7 @@ % \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. +% \cls{KOMA-Script} or \cls{memoir}, that allows easy customisation. % \end{itemize} % All three of these approaches have their drawbacks and learning curves. % @@ -467,14 +450,6 @@ % 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} -% % \begin{function}{\DeclareInstanceCopy} % \begin{syntax} % \cs{DeclareInstanceCopy} @@ -707,2225 +682,35 @@ % \end{macrocode} % % \begin{macrocode} -%<@@=xtemplate> -% \end{macrocode} -% -% \begin{macrocode} -\ProvidesExplPackage{xtemplate}{2023-10-10}{} - {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 +%<@@=template> % \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} -% -% \begin{variable}{\s_@@_mark,\s_@@_stop} -% Internal scan marks. -% \begin{macrocode} -\scan_new:N \s_@@_mark -\scan_new:N \s_@@_stop -% \end{macrocode} -% \end{variable} -% -% \begin{variable}{\q_@@_nil} -% Internal quarks. -% \begin{macrocode} -\quark_new:N \q_@@_nil -% \end{macrocode} -% \end{variable} -% -% \begin{macro}[pTF]{\@@_quark_if_nil:n} -% Branching quark conditional. -% \begin{macrocode} -\__kernel_quark_new_conditional:Nn \@@_quark_if_nil:N { F } -% \end{macrocode} -% \end{macro} -% -% \subsection{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:nneee { 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:nnee { 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:nne { 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:nne { 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:nnee { 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}. -% \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}[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:nnnV { xtemplate } { declare-object-type } - {#1} \l_@@_tmp_int - \prop_gput:NnV \g_@@_object_type_prop {#1} - \l_@@_tmp_int - } - { - \msg_error:nnee { 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:nne { 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:nne { xtemplate } - { keytype-requires-argument } {#1} - \bool_set_true:N \l_@@_error_bool - \seq_map_break: - } - } - } -\cs_new:Npn \@@_parse_keys_elt_aux: - { - \tl_set:Ne \l_@@_tmp_tl - { - \l_@@_keytype_tl - \tl_if_empty:NF \l_@@_keytype_arg_tl - { { \l_@@_keytype_arg_tl } } - } - \prop_put: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 } - { - \exp_args:No \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, -% the category code of colons is standardised. After -% that, the standard delimited argument method is used to separate the -% two parts. -% \begin{macrocode} -\cs_new_protected:Npe \@@_split_keytype:n #1 - { - \exp_not:N \bool_set_false:N \exp_not:N \l_@@_error_bool - \tl_set:Nn \exp_not:N \l_@@_tmp_tl {#1} - \tl_replace_all:Nnn \exp_not:N \l_@@_tmp_tl { : } { \token_to_str:N : } - \tl_if_in: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 \s_@@_stop - } - } - { - \exp_not:N \bool_set_true:N \exp_not:N \l_@@_error_bool - \msg_error:nne { xtemplate } { missing-keytype } {#1} - } - } -\use:e - { - \cs_new_protected:Npn \exp_not:N \@@_split_keytype_aux:w - #1 \token_to_str:N : #2 \s_@@_stop - { - \tl_put_right:Ne \exp_not:N \l_@@_key_name_tl - { - \exp_not:N \tl_trim_spaces:e - { \exp_not:N \tl_to_str:n {#1} } - } - \tl_if_in:nnTF {#2} { \token_to_str:N : } - { - \tl_put_right:Nn \exp_not:N \l_@@_key_name_tl - { \token_to_str:N : } - \exp_not:N \@@_split_keytype_aux:w #2 \s_@@_stop - } - { - \exp_not:N \tl_if_empty:NTF \exp_not:N \l_@@_key_name_tl - { - \msg_error:nne { 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:Ne \l_@@_keytype_tl { \tl_trim_spaces:n {#1} } - \tl_clear:N \l_@@_keytype_arg_tl - \cs_set_protected:Npn \@@_split_keytype_arg_aux:n ##1 - { - \tl_if_in:nnT {#1} {##1} - { - \cs_set:Npn \@@_split_keytype_arg_aux:w - ####1 ##1 ####2 \s_@@_stop - { - \tl_if_blank:nT {####1} - { - \tl_set:Ne \l_@@_keytype_tl - { \tl_trim_spaces:n {##1} } - \tl_if_blank:nF {####2} - { - \tl_set:Ne \l_@@_keytype_arg_tl - { \use:n ####2 } - } - \seq_map_break: - } - } - \@@_split_keytype_arg_aux:w #1 \s_@@_stop - } - } - \seq_map_function:NN \c_@@_keytypes_arg_seq - \@@_split_keytype_arg_aux:n - } -\cs_generate_variant:Nn \@@_split_keytype_arg:n { o } -\cs_new:Npn \@@_split_keytype_arg_aux:n #1 { } -\cs_new:Npn \@@_split_keytype_arg_aux:w #1 \s_@@_stop { } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \subsubsection{Storing values} -% -% As \pkg{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} -% \begin{macrocode} -\cs_new_protected:Npn \@@_store_value_boolean:n #1 - { \prop_put:Non \l_@@_values_prop \l_@@_key_name_tl {#1} } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_store_value:n, \@@_store_value_choice:n, -% \@@_store_value_function:n, \@@_store_value_instance:n} -% With no need to worry about delayed evaluation, these keytypes all -% just store the input directly. -% \begin{macrocode} -\cs_new_protected:Npn \@@_store_value:n #1 - { \prop_put:Non \l_@@_values_prop \l_@@_key_name_tl {#1} } -\cs_new_eq:NN \@@_store_value_choice:n \@@_store_value:n -\cs_new_eq:NN \@@_store_value_function:n \@@_store_value:n -\cs_new_eq:NN \@@_store_value_instance:n \@@_store_value:n -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_store_value_aux:Nn, \@@_store_value_integer:n, -% \@@_store_value_length:n, \@@_store_value_muskip:n, -% \@@_store_value_real:n, \@@_store_value_skip:n, -% \@@_store_value_tokenlist:n, \@@_store_value_commalist:n} -% Storing values in \cs{l_@@_values_prop} is in most cases the same. -% \begin{macrocode} -\cs_new_protected:Npn \@@_store_value_aux:Nn #1#2 - { \prop_put:Non \l_@@_values_prop \l_@@_key_name_tl {#2} } -\cs_new_protected:Npn \@@_store_value_integer:n - { \@@_store_value_aux:Nn \int_eval:n } -\cs_new_protected:Npn \@@_store_value_length:n - { \@@_store_value_aux:Nn \dim_eval:n } -\cs_new_protected:Npn \@@_store_value_muskip:n - { \@@_store_value_aux:Nn \muskip_eval:n } -\cs_new_protected:Npn \@@_store_value_real:n - { \@@_store_value_aux:Nn \fp_eval:n } -\cs_new_protected:Npn \@@_store_value_skip:n - { \@@_store_value_aux:Nn \skip_eval:n } -\cs_new_protected:Npn \@@_store_value_tokenlist:n - { \@@_store_value_aux:Nn \use:n } -\cs_new_eq:NN \@@_store_value_commalist:n \@@_store_value_tokenlist:n -% \end{macrocode} -% \end{macro} -% -% \subsection{Implementation part of template declaration} -% -% \begin{macro}{\@@_declare_template_code:nnnnn} -% \begin{macro}{\@@_declare_template_code:nnn} -% The main function for implementing a template starts with a couple of -% simple checks to make sure that there are no obvious mistakes: the -% number of arguments must agree and the template keys must have been -% declared. -% \begin{macrocode} -\cs_new_protected:Npn \@@_declare_template_code:nnnnn #1#2#3#4#5 - { - \@@_execute_if_type_exist:nT {#1} - { - \@@_execute_if_arg_agree:nnT {#1}{#3} - { - \@@_if_keys_exist:nnT {#1} {#2} - { - \@@_store_key_implementation:nnn {#1} {#2} {#4} - \regex_match:nnTF { \c { AssignTemplateKeys } } {#5} - { \@@_declare_template_code:nnn { #1 / #2 } {#3} {#5} } - { - \@@_declare_template_code:nnn - { #1 / #2 } {#3} { \AssignTemplateKeys #5 } - } - } - } - } - } -\cs_new_protected:Npn \@@_declare_template_code:nnn #1#2#3 - { - \cs_generate_from_arg_count:cNnn - { \c_@@_code_root_tl #1 } - \cs_gset_protected:Npn {#2} {#3} - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\@@_store_key_implementation:nnn} -% Actually storing the implementation part of a template is quite easy -% as it only requires the list of keys given to be turned into a -% property list. There is also some error-checking to do, hence the need -% to have the list of defined keytypes available. In certain cases -% (when choices are involved) parsing the key results in changes to the -% default values. That is why they are loaded and then saved again. -% \begin{macrocode} -\cs_new_protected:Npn \@@_store_key_implementation:nnn #1#2#3 - { - \@@_recover_defaults:n { #1 / #2 } - \@@_recover_restrictions:n { #1 / #2 } - \@@_recover_keytypes:n { #1 / #2 } - \prop_clear:N \l_@@_vars_prop - \keyval_parse:nnn - { \@@_parse_vars_elt:n } { \@@_parse_vars_elt:nnn { #1 / #2 } } {#3} - \@@_store_vars:n { #1 / #2 } - \@@_store_restrictions:n { #1 / #2 } - \prop_map_inline:Nn \l_@@_keytypes_prop - { - \msg_error:nneee { 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:nne { xtemplate } { key-no-variable } {#1} } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_parse_vars_elt:nnn} -% The actual storage part here is very simple: the storage bin name -% is placed into the property list. At the same time, a comparison is -% made with the keytypes defined earlier: if there is a mismatch then -% an error is raised. -% \begin{macrocode} -\cs_new_protected:Npn \@@_parse_vars_elt:nnn #1#2#3 - { - \tl_set:Ne \l_@@_key_name_tl - { \tl_trim_spaces:e { \tl_to_str:n {#2} } } - \prop_get:NoNTF - \l_@@_keytypes_prop - \l_@@_key_name_tl - \l_@@_keytype_tl - { - \@@_split_keytype_arg:o \l_@@_keytype_tl - \@@_parse_vars_elt_aux:nn {#1} {#3} - \prop_remove:NV \l_@@_keytypes_prop \l_@@_key_name_tl - } - { \msg_error:nne { xtemplate } { unknown-key } {#2} } - } -% \end{macrocode} -% \end{macro} -% \begin{macro}{\@@_parse_vars_elt_aux:nn} -% \begin{macro}{\@@_parse_vars_elt_aux:nw} -% \begin{macro}{\@@_parse_vars_elt_aux:nnn} -% \begin{macro}{\@@_parse_vars_elt_key:nn} -% Split off any leading \texttt{global} and they look for the way to -% implement. -% \begin{macrocode} -\cs_new_protected:Npn \@@_parse_vars_elt_aux:nn #1#2 - { - \@@_parse_vars_elt_aux:nw {#1} #2 global global \s_@@_stop - } -\cs_new_protected:Npn \@@_parse_vars_elt_aux:nw - #1#2 global #3 global #4 \s_@@_stop - { - \tl_if_blank:nTF {#4} - { \@@_parse_vars_elt_aux:nnn {#1} { } {#2} } - { - \tl_if_blank:nTF {#2} - { - \exp_args:Nnne \@@_parse_vars_elt_aux:nnn - {#1} { global } { \tl_trim_spaces:n {#3} } - } - { \msg_error:nnn { xtemplate } { bad-variable } { #2 global #3 } } - } - } -\cs_new_protected:Npn \@@_parse_vars_elt_aux:nnn #1#2#3 - { - \str_case:VnF \l_@@_keytype_tl - { - { choice } { \@@_implement_choices:nn {#1} {#3} } - { function } - { - \cs_if_exist:NF #3 - { \cs_new:Npn #3 { } } - \@@_parse_vars_elt_key:nn {#1} - { - .code:n = - { - \cs_generate_from_arg_count:NNnn - \exp_not:N #3 - \exp_not:c - { cs_ \str_if_eq:nnT {#1} { global } { g } set:Npn } - { \exp_not:o \l_@@_keytype_arg_tl } - {##1} - } - } - \prop_put:Non \l_@@_vars_prop - \l_@@_key_name_tl {#2#3} - } - { instance } - { - \@@_parse_vars_elt_key:nn {#1} - { - .code:n = - { - \exp_not:c - { cs_ \str_if_eq:nnT {#1} { global } { g } set:Npn } - \exp_not:N #3 { \UseInstance {##1} } - } - } - \prop_put:Non \l_@@_vars_prop - \l_@@_key_name_tl {#2#3} - } - } - { - \tl_if_single:nTF {#3} - { - \cs_if_exist:NF #3 - { \use:c { \@@_map_var_type: _new:N } #3 } - \@@_parse_vars_elt_key:nn {#1} - { - . \@@_map_var_type: - _ \str_if_eq:nnT {#1} { global } { g } set:N - = \exp_not:N #3 - } - \prop_put:Non \l_@@_vars_prop - \l_@@_key_name_tl {#2#3} - } - { \msg_error:nne { xtemplate } { bad-variable } { #2#3 } } - } - } -\cs_new_protected:Npn \@@_parse_vars_elt_key:nn #1#2 - { - \keys_define:ne { template / #1 } - { \l_@@_key_name_tl #2 } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}[EXP]{\@@_map_var_type:} -% Turn a \enquote{friendly} variable type into an \texttt{expl3} one. -% \begin{macrocode} -\cs_new:Npn \@@_map_var_type: - { - \str_case:on \l_@@_keytype_tl - { - { boolean } { bool } - { commalist } { clist } - { integer } { int } - { length } { dim } - { muskip } { muskip } - { real } { fp } - { skip } { skip } - { tokenlist } { tl } - } - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_implement_choices:nn} -% \begin{macro}{\@@_implement_choices_default:} -% Implementing choices requires a second key--value loop. So after a -% little set-up, the standard parser is called. -% \begin{macrocode} -\cs_new_protected:Npn \@@_implement_choices:nn #1#2 - { - \clist_set:No \l_@@_tmp_clist { \l_@@_keytype_arg_tl } - \prop_put:Non \l_@@_vars_prop \l_@@_key_name_tl { } - \keys_define:ne { template / #1 } { \l_@@_key_name_tl .choice: } - \keyval_parse:nnn - { \@@_implement_choice_elt:n } - { \@@_implement_choice_elt:nnn {#1} } - {#2} - \prop_get: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:nne { 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:Ne \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:Ne \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:nneee { 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:nnn, \@@_implement_choice_elt_aux:nnn} -% \begin{macro}{\@@_implement_choice_elt:n} -% The actual storage of the implementation of a choice is mainly about -% error checking. The code here ensures that all choices have to have -% been declared, apart from the special \texttt{unknown} choice, which -% must come last. The code for each choice is stored along with the -% key name in the variables property list. -% \begin{macrocode} -\cs_new_protected:Npn \@@_implement_choice_elt:nnn #1#2#3 - { - \clist_if_empty:NTF \l_@@_tmp_clist - { - \str_if_eq:nnTF {#2} { unknown } - { \@@_implement_choice_elt_aux:nnn {#1} {#2} {#3} } - { - \prop_get:NoN \l_@@_keytypes_prop \l_@@_key_name_tl - \l_@@_tmp_tl - \@@_split_keytype_arg:o \l_@@_tmp_tl - \msg_error:nneee { xtemplate } { unknown-choice } - { \l_@@_key_name_tl } {#2} - { \l_@@_keytype_arg_tl } - } - } - { - \clist_if_in:NnTF \l_@@_tmp_clist {#2} - { - \clist_remove_all:Nn \l_@@_tmp_clist {#2} - \@@_implement_choice_elt_aux:nnn {#1} {#2} {#3} - } - { - \prop_get:NoN \l_@@_keytypes_prop \l_@@_key_name_tl - \l_@@_tmp_tl - \@@_split_keytype_arg:o \l_@@_tmp_tl - \msg_error:nneee { xtemplate } { unknown-choice } - { \l_@@_key_name_tl } {#2} - { \l_@@_keytype_arg_tl } - } - } - } -\cs_new_protected:Npn \@@_implement_choice_elt_aux:nnn #1#2#3 - { - \keys_define:ne { template / #1 } - { \l_@@_key_name_tl / #2 .code:n = { \exp_not:n {#3} } } - \tl_set:Ne \l_@@_tmp_tl - { \l_@@_key_name_tl \c_space_tl #2 } - \prop_put:Non \l_@@_vars_prop \l_@@_tmp_tl {#3} - } -\cs_new_protected:Npn \@@_implement_choice_elt:n #1 - { - \msg_error:nneee { xtemplate } { choice-requires-code } - { \l_@@_key_name_tl } {#1} - } -% \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} - \bool_set_false:N \l_@@_restrict_bool - } - } -% \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, \@@_parse_values_aux: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 - { - \clist_clear:N \l_@@_restrict_clist - \@@_parse_values_aux:nn {#1} {#2} - } -\cs_new_protected:Npn \@@_parse_values_aux:nn #1#2 - { - \@@_recover_keytypes:n {#1} - \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:nne { 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:Ne \l_@@_key_name_tl - { \tl_trim_spaces:e { \tl_to_str:n {#1} } } - \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:nne { 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_restrictions: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_aux: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:cpe { \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}{\@@_copy_instance:nnnn} -% Copy--paste an instance -% \begin{macrocode} -\cs_new_protected:Npn \@@_copy_instance:nnnn #1#2#3#4 - { - \@@_if_instance_exist:nnnTF {#1} {#2} {#3} - { - \@@_recover_values:n { #1 / #2 / #3 } - \@@_store_values:n { #1 / #2 / #4 } - \cs_set_eq:cc { \c_@@_instances_root_tl #1 / #2 / #4 } - { \c_@@_instances_root_tl #1 / #2 / #3 } - } - { - \msg_error:nnee { xtemplate } { unknown-instance } - {#1} {#3} - } - } -% \end{macrocode} -% \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#4 - { - \@@_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} {#4} - } - { - \msg_error:nnee { 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:nne { 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 \s_@@_stop - } - } -\cs_new_protected:Npn \@@_find_global_aux:w #1 global #2 \s_@@_stop - { - \tl_set:Nn \l_@@_var_tl {#2} - \bool_set_true:N \l_@@_global_bool - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \subsection{Using templates directly} -% -% \begin{macro}{\@@_use_template:nnn} -% Directly use a template with a particular parameter setting. -% This is also picked up if used in a nested fashion inside a parameter -% list. The idea is essentially the same as creating an instance, -% just with no saving of the result. -% \begin{macrocode} -\cs_new_protected:Npn \@@_use_template:nnn #1#2#3 - { - \@@_execute_if_code_exist:nnT {#1} {#2} - { - \@@_recover_defaults: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:Ne \l_@@_assignments_tl - { - \exp_not:c { #1 _eq:NN } - \exp_not:o \l_@@_var_tl - \exp_not:o \l_@@_value_tl - } - } - { - \tl_put_right:Ne \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:eF - { \l_@@_key_name_tl \c_space_tl \l_@@_value_tl } - { - \@@_assign_choice_aux:eF - { \l_@@_key_name_tl \c_space_tl unknown } - { - \prop_get:NoN \l_@@_keytypes_prop \l_@@_key_name_tl - \l_@@_tmp_tl - \@@_split_keytype_arg:o \l_@@_tmp_tl - \msg_error:nneee { 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 { e } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\@@_assign_function:} -% \begin{macro}{\@@_assign_function_aux:N} -% This looks a bit messy but is only actually one function. -% \begin{macrocode} -\cs_new_protected:Npn \@@_assign_function: - { - \bool_if:NTF \l_@@_global_bool - { \@@_assign_function_aux:N \cs_gset:Npn } - { \@@_assign_function_aux:N \cs_set:Npn } - } -\cs_new_protected:Npn \@@_assign_function_aux:N #1 - { - \tl_put_right:Ne \l_@@_assignments_tl - { - \cs_generate_from_arg_count:NNnn - \exp_not: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:Ne \l_@@_assignments_tl - { - \exp_not:N #1 \exp_not:o \l_@@_var_tl - { - \@@_use_instance:nn - { \exp_not:o \l_@@_keytype_arg_tl } - { \exp_not:o \l_@@_value_tl } - } - } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\@@_assign_integer:} -% \begin{macro}{\@@_assign_length:} -% \begin{macro}{\@@_assign_muskip:} -% \begin{macro}{\@@_assign_real:} -% \begin{macro}{\@@_assign_skip:} -% All of the calculated assignments use the same underlying code, with -% only the low-level assignment function changing. -% \begin{macrocode} -\cs_new_protected:Npn \@@_assign_integer: - { - \bool_if:NTF \l_@@_global_bool - { \@@_assign_variable:N \int_gset:Nn } - { \@@_assign_variable:N \int_set:Nn } - } -\cs_new_protected:Npn \@@_assign_length: - { - \bool_if:NTF \l_@@_global_bool - { \@@_assign_variable:N \dim_gset:Nn } - { \@@_assign_variable:N \dim_set:Nn } -} -\cs_new_protected:Npn \@@_assign_muskip: - { - \bool_if:NTF \l_@@_global_bool - { \@@_assign_variable:N \muskip_gset:Nn } - { \@@_assign_variable:N \muskip_set:Nn } - } -\cs_new_protected:Npn \@@_assign_real: - { - \bool_if:NTF \l_@@_global_bool - { \@@_assign_variable:N \fp_gset:Nn } - { \@@_assign_variable:N \fp_set:Nn } - } -\cs_new_protected:Npn \@@_assign_skip: - { - \bool_if:NTF \l_@@_global_bool - { \@@_assign_variable:N \skip_gset:Nn } - { \@@_assign_variable:N \skip_set:Nn } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \begin{macro}{\@@_assign_tokenlist:} -% \begin{macro}{\@@_assign_tokenlist_aux:NN} -% Life would be easy here if it were not for \cs{KeyValue}. To deal -% correctly with that, we need to allow for the recovery a stored value -% at point of use. -% \begin{macrocode} -\cs_new_protected:Npn \@@_assign_tokenlist: - { - \bool_if:NTF \l_@@_global_bool - { \@@_assign_tokenlist_aux:NN \tl_gset:NV \tl_gset:Nn } - { \@@_assign_tokenlist_aux:NN \tl_set:NV \tl_set:Nn } - } -\cs_new_protected:Npn \@@_assign_tokenlist_aux:NN #1#2 - { - \@@_if_key_value:oTF \l_@@_value_tl - { - \@@_key_to_value: - \tl_put_right:Ne \l_@@_assignments_tl - { - #1 \exp_not:o \l_@@_var_tl - \exp_not:o \l_@@_value_tl - } - } - { - \tl_put_right:Ne \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:Ne \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:Ne \l_@@_tmp_tl { \tl_trim_spaces:e { \tl_to_str:n {#1} } } - \prop_get:NoNTF - \l_@@_vars_prop - \l_@@_tmp_tl - \l_@@_value_tl - { - \exp_after:wN \@@_key_to_value_auxii:w \l_@@_value_tl - \s_@@_mark global \q_@@_nil \s_@@_stop - } - { - \msg_error:nne { xtemplate } { unknown-attribute } - { \l_@@_tmp_tl } - } - } -\cs_new_protected:Npn \@@_key_to_value_auxii:w #1 global #2#3 \s_@@_stop - { - \@@_quark_if_nil:NF #2 - { \tl_set:Nn \l_@@_value_tl {#2} } - } -% \end{macrocode} -% \end{macro} -% \end{macro} -% \end{macro} -% -% \subsection{Using instances} -% -% \begin{macro}{\@@_use_instance:nn} -% \begin{macro}{\@@_use_instance_aux:nNnnn} -% \begin{macro}{\@@_use_instance_aux:nn} -% Using an instance is just a question of finding the appropriate -% function. 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:nnee { 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:nnee { 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:nneeee { 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:nneeee { xtemplate } { show-values } - { \tl_to_str:n {#1} } - { \tl_to_str:n {#2} } - { \tl_to_str:n {#3} } - { - \prop_map_function:NN \l_@@_values_prop - \msg_show_item_unbraced:nn - } - } - } -% \end{macrocode} -% \end{macro} -% -% \subsection{Messages} -% -% The text for error messages: short and long text for all of them. -% \begin{macrocode} -\msg_new:nnnn { xtemplate } { argument-number-mismatch } - { Object~type~'#1'~takes~#2~argument(s). } - { - Objects~of~type~'#1'~require~#2~argument(s).\\ - You~have~tried~to~make~a~template~for~'#1'~ - with~#3~argument(s),~which~is~not~possible:~ - the~number~of~arguments~must~agree. - } -\msg_new:nnnn { xtemplate } { bad-number-of-arguments } - { Bad~number~of~arguments~for~object~type~'#1'. } - { - An~object~may~accept~between~0~and~9~arguments.\\ - You~asked~to~use~#2~arguments:~this~is~not~supported. - } -\msg_new:nnnn { xtemplate } { bad-variable } - { Incorrect~variable~description~'#1'. } - { - The~argument~'#1'~is~not~of~the~form \\ - ~~'<variable>'\\ - ~or~\\ - ~~'global~<variable>'.\\ - It~must~be~given~in~one~of~these~formats~to~be~used~in~a~template. - } -\msg_new:nnnn { xtemplate } { choice-not-implemented } - { The~choice~'#1'~has~no~implementation. } - { - Each~choice~listed~in~the~interface~for~a~template~must~ - have~an~implementation. - } -\msg_new:nnnn { xtemplate } { choice-no-code } - { The~choice~'#1'~requires~implementation~details. } - { - When~creating~template~code~using~\DeclareTemplateCode,~ - each~choice~name~must~have~an~associated~implementation.\\ - This~should~be~given~after~a~'='~sign:~LaTeX~did~not~find~one. - } -\msg_new:nnnn { xtemplate } { choice-requires-code } - { The~choice~'#2'~for~key~'#1'~requires~an~implementation. } - { - You~should~have~put:\\ - \ \ #1~:~choice~{~#2 = <code> ~} \\ - but~LaTeX~did~not~find~any~<code>. - } -\msg_new:nnnn { xtemplate } { duplicate-key-interface } - { Key~'#1'~appears~twice~in~interface~definition~\msg_line_context:. } - { - Each~key~can~only~have~one~interface~declared~in~a~template.\\ - LaTeX~found~two~interfaces~for~'#1'. - } -\msg_new:nnnn { xtemplate } { keytype-requires-argument } - { The~key~type~'#1'~requires~an~argument~\msg_line_context:. } - { - You~should~have~put:\\ - \ \ <key-name>~:~#1~{~<argument>~} \\ - but~LaTeX~did~not~find~an~<argument>. - } -\msg_new:nnnn { xtemplate } { invalid-keytype } - { The~key~'#1'~is~missing~a~key-type~\msg_line_context:. } - { - Each~key~in~a~template~requires~a~key-type,~given~in~the~form:\\ - \ \ <key>~:~<key-type>\\ - LaTeX~could~not~find~a~<key-type>~in~your~input. - } -\msg_new:nnnn { xtemplate } { key-no-value } - { The~key~'#1'~has~no~value~\msg_line_context:. } - { - When~creating~an~instance~of~a~template~ - every~key~listed~must~include~a~value:\\ - \ \ <key>~=~<value> - } -\msg_new:nnnn { xtemplate } { key-no-variable } - { The~key~'#1'~requires~implementation~details~\msg_line_context:. } - { - When~creating~template~code~using~\DeclareTemplateCode,~ - each~key~name~must~have~an~associated~implementation.\\ - This~should~be~given~after~a~'='~sign:~LaTeX~did~not~find~one. - } -\msg_new:nnnn { xtemplate } { key-not-implemented } - { Key~'#1'~has~no~implementation~\msg_line_context:. } - { - The~definition~of~key~implementations~for~template~'#2'~ - of~object~type~'#3'~does~not~include~any~details~for~key~'#1'.\\ - The~key~was~declared~in~the~interface~definition,~ - and~so~an~implementation~is~required. - } -\msg_new:nnnn { xtemplate } { missing-keytype } - { The~key~'#1'~is~missing~a~key-type~\msg_line_context:. } - { - Key~interface~definitions~should~be~of~the~form\\ - \ \ #1~:~<key-type>\\ - but~LaTeX~could~not~find~a~<key-type>. - } -\msg_new:nnnn { xtemplate } { no-template-code } - { - The~template~'#2'~of~type~'#1'~is~unknown~ - or~has~no~implementation. - } - { - There~is~no~code~available~for~the~template~name~given.\\ - This~should~be~given~using~\DeclareTemplateCode. - } -\msg_new:nnnn { xtemplate } { object-type-mismatch } - { Object~types~'#1'~and~'#2'~do~not~agree. } - { - You~are~trying~to~use~a~template~directly~with~\UseInstance - (or~a~similar~function),~but~the~object~types~do~not~match. - } -\msg_new:nnnn { xtemplate } { unknown-attribute } - { The~template~attribute~'#1'~is~unknown. } - { - There~is~a~definition~in~the~current~template~reading\\ - \ \ \token_to_str:N \KeyValue {~#1~} \\ - but~there~is~no~key~called~'#1'. - } -\msg_new:nnnn { xtemplate } { unknown-choice } - { The~choice~'#2'~was~not~declared~for~key~'#1'. } - { - The~key~'#1'~takes~a~fixed~list~of~choices~ - and~this~list~does~not~include~'#2'. - } -\msg_new:nnnn { xtemplate } { unknown-default-choice } - { The~default~choice~'#2'~was~not~declared~for~key~'#1'. } - { - The~key~'#1'~takes~a~fixed~list~of~choices~ - and~this~list~does~not~include~'#2'. - } -\msg_new:nnnn { xtemplate } { unknown-instance } - { The~instance~'#2'~of~type~'#1'~is~unknown. } - { - You~have~asked~to~use~an~instance~'#2',~ - but~this~has~not~been~created. - } -\msg_new:nnnn { xtemplate } { unknown-key } - { Unknown~template~key~'#1'. } - { - The~key~'#1'~was~not~declared~in~the~interface~ - for~the~current~template. - } -\msg_new:nnnn { xtemplate } { unknown-keytype } - { The~key-type~'#1'~is~unknown. } - { - Valid~key-types~are:\\ - -~boolean;\\ - -~choice;\\ - -~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}{\DeclareInstanceCopy} -% \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} +% \begin{macro}{\DeclareObjectType} +% If the new kernel code is not loaded, make a couple of compatibility +% additions. Otherwise, hand over to the frozen version. +% \begin{macrocode} +\@ifundefined{NewTemplateType} + {% + \input{xtemplate-2023-10-10.sty}% + \endinput + } + {% + \ProvidesExplPackage{xtemplate}{2023-10-10}{} + {L3 Experimental prototype document functions} + \long\protected\def\DeclareRestrictedTemplate#1#2#3#4{% + \DeclareTemplateCopy{#1}{#3}{#2}% + \EditTemplateDefaults{#1}{#3}{#4}% + }% + } +\ExplSyntaxOn \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 \DeclareInstanceCopy #1#2#3 - { \@@_copy_instance:nnnn {#1} { } {#3} {#2} } -\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} -% \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}{\KeyValue} -% Simply dump the argument when executed: this should not happen. -% \begin{macrocode} -\cs_new_protected:Npn \KeyValue #1 {#1} -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\AssignTemplateKeys} -% A short call to use a token register by proxy. -% \begin{macrocode} -\cs_new_protected:Npn \AssignTemplateKeys - { \@@_assignments_pop: } + { \@@_declare_type:nn {#1} {#2} } +\ExplSyntaxOff % \end{macrocode} % \end{macro} -% -% \begin{macro}{\SetTemplateKeys} -% A friendly wrapper -% \begin{macrocode} -\cs_new_protected:Npn \SetTemplateKeys #1#2#3 - { \keys_set_known:nnN { template / #1 / #2 } {#3} \l_@@_tmp_clist } -% \end{macrocode} % \end{macro} % -% \begin{macrocode} -\cs_new_eq:NN \ShowTemplateKeytypes \ShowTemplateInterface -% \end{macrocode} -% -% \begin{macrocode} %</package> % \end{macrocode} % diff --git a/macros/latex/contrib/l3packages/xtemplate.ins b/macros/latex/contrib/l3packages/xtemplate.ins index 72bf777194..eaf3fe0ead 100644 --- a/macros/latex/contrib/l3packages/xtemplate.ins +++ b/macros/latex/contrib/l3packages/xtemplate.ins @@ -2,7 +2,7 @@ File: xtemplate.ins -Copyright (C) 2000-2012,2016,2017,2019-2023 The LaTeX Project +Copyright (C) 2000-2012,2016,2017,2019-2024 The LaTeX Project It may be distributed and/or modified under the conditions of the LaTeX Project Public License (LPPL), either version 1.3c of this @@ -34,7 +34,7 @@ license information is placed in the derived files. \preamble -Copyright (C) 2011-2023 The LaTeX Project +Copyright (C) 2011-2024 The LaTeX Project It may be distributed and/or modified under the conditions of the LaTeX Project Public License (LPPL), either version 1.3c of diff --git a/macros/latex/contrib/l3packages/xtemplate.pdf b/macros/latex/contrib/l3packages/xtemplate.pdf Binary files differindex 57ab72b148..c5fc465835 100644 --- a/macros/latex/contrib/l3packages/xtemplate.pdf +++ b/macros/latex/contrib/l3packages/xtemplate.pdf |