summaryrefslogtreecommitdiff
path: root/macros/latex
diff options
context:
space:
mode:
authorNorbert Preining <norbert@preining.info>2024-02-15 03:01:30 +0000
committerNorbert Preining <norbert@preining.info>2024-02-15 03:01:30 +0000
commit5b6839a58ea5468b40ebd52b579e680b8e62ff13 (patch)
tree1dfc09f53794115129a07bf566b466cc337606f5 /macros/latex
parenta90336c3069c198b41b7b6eb76e3389610ad5eb7 (diff)
CTAN sync 202402150301
Diffstat (limited to 'macros/latex')
-rw-r--r--macros/latex/contrib/eolang/README.md2
-rw-r--r--macros/latex/contrib/eolang/eolang.12
-rw-r--r--macros/latex/contrib/eolang/eolang.dtx28
-rw-r--r--macros/latex/contrib/eolang/eolang.pdfbin1940589 -> 1940560 bytes
-rwxr-xr-xmacros/latex/contrib/eolang/eolang.pl7
-rw-r--r--macros/latex/contrib/huawei/LICENSE.txt2
-rw-r--r--macros/latex/contrib/huawei/README.md2
-rw-r--r--macros/latex/contrib/huawei/huawei.dtx8
-rw-r--r--macros/latex/contrib/huawei/huawei.ins4
-rw-r--r--macros/latex/contrib/huawei/huawei.pdfbin808894 -> 809033 bytes
-rw-r--r--macros/latex/contrib/jnuexam/jnuexam.cls6
-rw-r--r--macros/latex/contrib/l3kernel/CHANGELOG.md23
-rw-r--r--macros/latex/contrib/l3kernel/README.md2
-rw-r--r--macros/latex/contrib/l3kernel/expl3.dtx4
-rw-r--r--macros/latex/contrib/l3kernel/expl3.pdfbin623299 -> 610363 bytes
-rw-r--r--macros/latex/contrib/l3kernel/interface3.pdfbin1776513 -> 1792513 bytes
-rw-r--r--macros/latex/contrib/l3kernel/interface3.tex6
-rw-r--r--macros/latex/contrib/l3kernel/l3basics.dtx170
-rw-r--r--macros/latex/contrib/l3kernel/l3bitset.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3bootstrap.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3box.dtx8
-rw-r--r--macros/latex/contrib/l3kernel/l3cctab.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3clist.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3coffins.dtx54
-rw-r--r--macros/latex/contrib/l3kernel/l3color.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3debug.dtx98
-rw-r--r--macros/latex/contrib/l3kernel/l3deprecation.dtx17
-rw-r--r--macros/latex/contrib/l3kernel/l3doc.dtx46
-rw-r--r--macros/latex/contrib/l3kernel/l3doc.pdfbin564281 -> 565282 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3docstrip.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3docstrip.pdfbin150985 -> 151264 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3expan.dtx5
-rw-r--r--macros/latex/contrib/l3kernel/l3file.dtx8
-rw-r--r--macros/latex/contrib/l3kernel/l3flag.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-assign.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-aux.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-basics.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-convert.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-expo.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-extended.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-functions.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-logic.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-parse.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-random.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-round.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-symbolic.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-traps.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-trig.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp-types.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fp.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3fparray.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3int.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3intarray.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3kernel-functions.dtx72
-rw-r--r--macros/latex/contrib/l3kernel/l3keys.dtx6
-rw-r--r--macros/latex/contrib/l3kernel/l3legacy.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3luatex.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3msg.dtx53
-rw-r--r--macros/latex/contrib/l3kernel/l3names.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3news.pdfbin396222 -> 396222 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3news01.pdfbin122959 -> 122959 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3news02.pdfbin150919 -> 150919 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3news03.pdfbin148747 -> 148747 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3news04.pdfbin114754 -> 114754 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3news05.pdfbin112524 -> 112524 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3news06.pdfbin151591 -> 151591 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3news07.pdfbin128344 -> 128344 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3news08.pdfbin163484 -> 163484 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3news09.pdfbin250159 -> 250159 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3news10.pdfbin150302 -> 150302 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3news11.pdfbin170102 -> 170102 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3news12.pdfbin205020 -> 205020 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3pdf.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3prefixes.csv9
-rw-r--r--macros/latex/contrib/l3kernel/l3prefixes.pdfbin82696 -> 82731 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3prg.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3prop.dtx2014
-rw-r--r--macros/latex/contrib/l3kernel/l3quark.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3regex.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3seq.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3skip.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3sort.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3str-convert.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3str.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3styleguide.pdfbin296657 -> 296657 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3syntax-changes.pdfbin242421 -> 231496 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3sys.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3term-glossary.pdfbin218315 -> 242296 bytes
-rw-r--r--macros/latex/contrib/l3kernel/l3text-case.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3text-map.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3text-purify.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3text.dtx7
-rw-r--r--macros/latex/contrib/l3kernel/l3tl-analysis.dtx103
-rw-r--r--macros/latex/contrib/l3kernel/l3tl-build.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3tl.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/l3token.dtx31
-rw-r--r--macros/latex/contrib/l3kernel/l3unicode.dtx2
-rw-r--r--macros/latex/contrib/l3kernel/source3.pdfbin7488727 -> 7574815 bytes
-rw-r--r--macros/latex/contrib/l3kernel/source3body.tex9
-rw-r--r--macros/latex/contrib/l3packages/CHANGELOG.md17
-rw-r--r--macros/latex/contrib/l3packages/README.md104
-rw-r--r--macros/latex/contrib/l3packages/l3keys2e.dtx8
-rw-r--r--macros/latex/contrib/l3packages/l3keys2e.ins4
-rw-r--r--macros/latex/contrib/l3packages/l3keys2e.pdfbin412557 -> 412717 bytes
-rw-r--r--macros/latex/contrib/l3packages/xfp.dtx8
-rw-r--r--macros/latex/contrib/l3packages/xfp.ins4
-rw-r--r--macros/latex/contrib/l3packages/xfp.pdfbin332260 -> 332421 bytes
-rw-r--r--macros/latex/contrib/l3packages/xfrac.dtx1141
-rw-r--r--macros/latex/contrib/l3packages/xfrac.ins49
-rw-r--r--macros/latex/contrib/l3packages/xfrac.pdfbin482959 -> 0 bytes
-rw-r--r--macros/latex/contrib/l3packages/xparse.dtx8
-rw-r--r--macros/latex/contrib/l3packages/xparse.ins4
-rw-r--r--macros/latex/contrib/l3packages/xparse.pdfbin534533 -> 534182 bytes
-rw-r--r--macros/latex/contrib/l3packages/xtemplate-2023-10-10.sty1371
-rw-r--r--macros/latex/contrib/l3packages/xtemplate.dtx2265
-rw-r--r--macros/latex/contrib/l3packages/xtemplate.ins4
-rw-r--r--macros/latex/contrib/l3packages/xtemplate.pdfbin450604 -> 448106 bytes
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
index 23028cf96a..2bf8e39de4 100644
--- a/macros/latex/contrib/eolang/eolang.pdf
+++ b/macros/latex/contrib/eolang/eolang.pdf
Binary files differ
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
index 827b703b74..c0c742fa02 100644
--- a/macros/latex/contrib/huawei/huawei.pdf
+++ b/macros/latex/contrib/huawei/huawei.pdf
Binary files differ
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
index 8f722bc6b4..03b7a491c1 100644
--- a/macros/latex/contrib/l3kernel/expl3.pdf
+++ b/macros/latex/contrib/l3kernel/expl3.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/interface3.pdf b/macros/latex/contrib/l3kernel/interface3.pdf
index 7b5ffdc3e9..dcbc798249 100644
--- a/macros/latex/contrib/l3kernel/interface3.pdf
+++ b/macros/latex/contrib/l3kernel/interface3.pdf
Binary files differ
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
index 352082c2ab..9d301da547 100644
--- a/macros/latex/contrib/l3kernel/l3doc.pdf
+++ b/macros/latex/contrib/l3kernel/l3doc.pdf
Binary files differ
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
index 2098a2ce21..923689f3d8 100644
--- a/macros/latex/contrib/l3kernel/l3docstrip.pdf
+++ b/macros/latex/contrib/l3kernel/l3docstrip.pdf
Binary files differ
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
index 4719901866..31473fd53c 100644
--- a/macros/latex/contrib/l3kernel/l3news.pdf
+++ b/macros/latex/contrib/l3kernel/l3news.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3news01.pdf b/macros/latex/contrib/l3kernel/l3news01.pdf
index 155535a4fd..1e97f4aaca 100644
--- a/macros/latex/contrib/l3kernel/l3news01.pdf
+++ b/macros/latex/contrib/l3kernel/l3news01.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3news02.pdf b/macros/latex/contrib/l3kernel/l3news02.pdf
index 678d28fe51..ed804fc6b3 100644
--- a/macros/latex/contrib/l3kernel/l3news02.pdf
+++ b/macros/latex/contrib/l3kernel/l3news02.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3news03.pdf b/macros/latex/contrib/l3kernel/l3news03.pdf
index c4d3285b7e..ffcf4a37f2 100644
--- a/macros/latex/contrib/l3kernel/l3news03.pdf
+++ b/macros/latex/contrib/l3kernel/l3news03.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3news04.pdf b/macros/latex/contrib/l3kernel/l3news04.pdf
index 996d2face0..971bea5082 100644
--- a/macros/latex/contrib/l3kernel/l3news04.pdf
+++ b/macros/latex/contrib/l3kernel/l3news04.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3news05.pdf b/macros/latex/contrib/l3kernel/l3news05.pdf
index daea9c248a..bbdc0373a5 100644
--- a/macros/latex/contrib/l3kernel/l3news05.pdf
+++ b/macros/latex/contrib/l3kernel/l3news05.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3news06.pdf b/macros/latex/contrib/l3kernel/l3news06.pdf
index 8193ffab13..3a46d423fa 100644
--- a/macros/latex/contrib/l3kernel/l3news06.pdf
+++ b/macros/latex/contrib/l3kernel/l3news06.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3news07.pdf b/macros/latex/contrib/l3kernel/l3news07.pdf
index 9492361a4a..6c9632fdf4 100644
--- a/macros/latex/contrib/l3kernel/l3news07.pdf
+++ b/macros/latex/contrib/l3kernel/l3news07.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3news08.pdf b/macros/latex/contrib/l3kernel/l3news08.pdf
index 62d965b72f..5ec72ff809 100644
--- a/macros/latex/contrib/l3kernel/l3news08.pdf
+++ b/macros/latex/contrib/l3kernel/l3news08.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3news09.pdf b/macros/latex/contrib/l3kernel/l3news09.pdf
index 988790dadf..a2ea7832c0 100644
--- a/macros/latex/contrib/l3kernel/l3news09.pdf
+++ b/macros/latex/contrib/l3kernel/l3news09.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3news10.pdf b/macros/latex/contrib/l3kernel/l3news10.pdf
index 6c13a5cbb1..113dfe2f19 100644
--- a/macros/latex/contrib/l3kernel/l3news10.pdf
+++ b/macros/latex/contrib/l3kernel/l3news10.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3news11.pdf b/macros/latex/contrib/l3kernel/l3news11.pdf
index d78d434fa7..74d21a3c62 100644
--- a/macros/latex/contrib/l3kernel/l3news11.pdf
+++ b/macros/latex/contrib/l3kernel/l3news11.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3news12.pdf b/macros/latex/contrib/l3kernel/l3news12.pdf
index 6f9eac3b37..ccbd0305ff 100644
--- a/macros/latex/contrib/l3kernel/l3news12.pdf
+++ b/macros/latex/contrib/l3kernel/l3news12.pdf
Binary files differ
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
index ea2bf15903..2bd39c3787 100644
--- a/macros/latex/contrib/l3kernel/l3prefixes.pdf
+++ b/macros/latex/contrib/l3kernel/l3prefixes.pdf
Binary files differ
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
index dda0393c17..c40939a7a3 100644
--- a/macros/latex/contrib/l3kernel/l3styleguide.pdf
+++ b/macros/latex/contrib/l3kernel/l3styleguide.pdf
Binary files differ
diff --git a/macros/latex/contrib/l3kernel/l3syntax-changes.pdf b/macros/latex/contrib/l3kernel/l3syntax-changes.pdf
index 0bce9ea22a..e8db43bb3e 100644
--- a/macros/latex/contrib/l3kernel/l3syntax-changes.pdf
+++ b/macros/latex/contrib/l3kernel/l3syntax-changes.pdf
Binary files differ
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
index da4ee7e729..736d53b4c0 100644
--- a/macros/latex/contrib/l3kernel/l3term-glossary.pdf
+++ b/macros/latex/contrib/l3kernel/l3term-glossary.pdf
Binary files differ
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
index d74db5c796..09f28f0667 100644
--- a/macros/latex/contrib/l3kernel/source3.pdf
+++ b/macros/latex/contrib/l3kernel/source3.pdf
Binary files differ
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
index cfef37deff..a6e0bd9e41 100644
--- a/macros/latex/contrib/l3packages/l3keys2e.pdf
+++ b/macros/latex/contrib/l3packages/l3keys2e.pdf
Binary files differ
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
index aa5b6c0293..b45cdd5b15 100644
--- a/macros/latex/contrib/l3packages/xfp.pdf
+++ b/macros/latex/contrib/l3packages/xfp.pdf
Binary files differ
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
deleted file mode 100644
index 61a472a901..0000000000
--- a/macros/latex/contrib/l3packages/xfrac.pdf
+++ /dev/null
Binary files differ
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
index 483370f2fc..183ebaaa78 100644
--- a/macros/latex/contrib/l3packages/xparse.pdf
+++ b/macros/latex/contrib/l3packages/xparse.pdf
Binary files differ
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
index 57ab72b148..c5fc465835 100644
--- a/macros/latex/contrib/l3packages/xtemplate.pdf
+++ b/macros/latex/contrib/l3packages/xtemplate.pdf
Binary files differ