diff options
Diffstat (limited to 'macros/latex/contrib')
94 files changed, 2994 insertions, 541 deletions
diff --git a/macros/latex/contrib/l3kernel/CHANGELOG.md b/macros/latex/contrib/l3kernel/CHANGELOG.md index cfca3155d0..5e967d3607 100644 --- a/macros/latex/contrib/l3kernel/CHANGELOG.md +++ b/macros/latex/contrib/l3kernel/CHANGELOG.md @@ -7,10 +7,22 @@ this project uses date-based 'snapshot' version identifiers. ## [Unreleased] -## [2020-10-27] +## [2020-12-03] ### Added +- `\peek_analysis_map_inline:n` +- `\peek_regex:nTF`, `\peek_regex_remove_once:nTF`, and + `\peek_regex_replace_once:nnTF` +- `\token_case_catcode:NnTF`, `\token_case_charcode:NnTF`, and + `\token_case_meaning:NnTF` + +### Changed +- Extend `\text_expand:n` to cover `\@protected@testopt` +- Extend `\text_purify:n` to cover `\@protected@testopt` +## [2020-10-27] + +### Added - `\token_if_font_selection:N(TF)` (see #806) ### Fixed @@ -793,7 +805,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/2020-10-27...HEAD +[Unreleased]: https://github.com/latex3/latex3/compare/2020-12-03...HEAD +[2020-12-03]: https://github.com/latex3/latex3/compare/2020-10-27...2020-12-03 [2020-10-27]: https://github.com/latex3/latex3/compare/2020-10-05...2020-10-27 [2020-10-05]: https://github.com/latex3/latex3/compare/2020-09-24...2020-10-05 [2020-09-24]: https://github.com/latex3/latex3/compare/2020-09-06...2020-09-24 diff --git a/macros/latex/contrib/l3kernel/README.md b/macros/latex/contrib/l3kernel/README.md index 2361e047a8..5a0e2ee3a0 100644 --- a/macros/latex/contrib/l3kernel/README.md +++ b/macros/latex/contrib/l3kernel/README.md @@ -1,7 +1,7 @@ LaTeX3 Programming Conventions ============================== -Release 2020-10-27 +Release 2020-12-03 Overview -------- diff --git a/macros/latex/contrib/l3kernel/expl3.dtx b/macros/latex/contrib/l3kernel/expl3.dtx index 59b8cad3a7..fc49c0aa90 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{2020-10-27}% +\def\ExplFileDate{2020-12-03}% %<*driver> \documentclass[full]{l3doc} \usepackage{graphicx} @@ -51,7 +51,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % @@ -526,9 +526,20 @@ % \end{arg-description} % There are two other specifiers with more general meanings: % \begin{arg-description} -% \item[D] This means: \textbf{Do not use}. This special case is used -% for \TeX{} primitives. Programmers outside the kernel team should -% not use these functions! +% \item[D] Stands for \textbf{Do not use}. This special case is used +% for \TeX{} primitives. These functions have no standardized +% syntax, they are engine 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}%^^A +% \footnote{If a primitive offers a functionality not yet in the +% kernel, programmers and users are encouraged to write to the +% \texttt{LaTeX-L} mailing list +% (\url{mailto:LATEX-L@listserv.uni-heidelberg.de}) describing +% their use-case and intended behaviour, so that a possible +% interface can be discussed. Temporarily, while an interface is +% not provided, programmers may use the procedure described in the +% \href{l3styleguide.pdf}{l3styleguide.pdf}.}. % \item[w] This means that the argument syntax is \enquote{weird} in that it % does not follow any standard rule. It is used for functions with % arguments that take non standard forms: examples are \TeX{}-level @@ -1072,13 +1083,10 @@ % \begin{macrocode} %<*2ekernel|generic> \begingroup - \catcode`\>=12 - \def\aux#1>{} - \def\auxi{c__kernel_expl_date_tl}% - \edef\auxi{\expandafter\aux\meaning\auxi}% + \catcode`\_=11 \expandafter - \ifx\csname\auxi\endcsname\relax - \global\expandafter\let\csname\auxi\endcsname\ExplFileDate + \ifx\csname c__kernel_expl_date_tl\endcsname\relax + \global\let\c__kernel_expl_date_tl\ExplFileDate \fi \endgroup %</2ekernel|generic> diff --git a/macros/latex/contrib/l3kernel/expl3.pdf b/macros/latex/contrib/l3kernel/expl3.pdf Binary files differindex c249956331..6667f5c234 100644 --- a/macros/latex/contrib/l3kernel/expl3.pdf +++ b/macros/latex/contrib/l3kernel/expl3.pdf diff --git a/macros/latex/contrib/l3kernel/interface3.pdf b/macros/latex/contrib/l3kernel/interface3.pdf Binary files differindex b7b0dbf9a9..a8ced65b24 100644 --- a/macros/latex/contrib/l3kernel/interface3.pdf +++ b/macros/latex/contrib/l3kernel/interface3.pdf diff --git a/macros/latex/contrib/l3kernel/interface3.tex b/macros/latex/contrib/l3kernel/interface3.tex index 071ddd4bdf..cb7bb6e7eb 100644 --- a/macros/latex/contrib/l3kernel/interface3.tex +++ b/macros/latex/contrib/l3kernel/interface3.tex @@ -54,7 +54,7 @@ for those people who are interested. {latex-team@latex-project.org}% }% } -\date{Released 2020-10-27} +\date{Released 2020-12-03} \pagenumbering{roman} \maketitle diff --git a/macros/latex/contrib/l3kernel/l3basics.dtx b/macros/latex/contrib/l3kernel/l3basics.dtx index e7f2c4fd63..025f016eb8 100644 --- a/macros/latex/contrib/l3kernel/l3basics.dtx +++ b/macros/latex/contrib/l3kernel/l3basics.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3bootstrap.dtx b/macros/latex/contrib/l3kernel/l3bootstrap.dtx index eba03dca56..2139dfae9f 100644 --- a/macros/latex/contrib/l3kernel/l3bootstrap.dtx +++ b/macros/latex/contrib/l3kernel/l3bootstrap.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3box.dtx b/macros/latex/contrib/l3kernel/l3box.dtx index a866d6324b..3bd2f1de85 100644 --- a/macros/latex/contrib/l3kernel/l3box.dtx +++ b/macros/latex/contrib/l3kernel/l3box.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3candidates.dtx b/macros/latex/contrib/l3kernel/l3candidates.dtx index 17f7f33891..f376f0e0a4 100644 --- a/macros/latex/contrib/l3kernel/l3candidates.dtx +++ b/macros/latex/contrib/l3kernel/l3candidates.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3cctab.dtx b/macros/latex/contrib/l3kernel/l3cctab.dtx index 6e57d4a1bf..2918f8cae3 100644 --- a/macros/latex/contrib/l3kernel/l3cctab.dtx +++ b/macros/latex/contrib/l3kernel/l3cctab.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3clist.dtx b/macros/latex/contrib/l3kernel/l3clist.dtx index 779062a3cf..f45ff58a92 100644 --- a/macros/latex/contrib/l3kernel/l3clist.dtx +++ b/macros/latex/contrib/l3kernel/l3clist.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3coffins.dtx b/macros/latex/contrib/l3kernel/l3coffins.dtx index 0c0c3c776c..f4d6810d9a 100644 --- a/macros/latex/contrib/l3kernel/l3coffins.dtx +++ b/macros/latex/contrib/l3kernel/l3coffins.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3color-base.dtx b/macros/latex/contrib/l3kernel/l3color-base.dtx index 1d88e3d6ff..fb04d8eee0 100644 --- a/macros/latex/contrib/l3kernel/l3color-base.dtx +++ b/macros/latex/contrib/l3kernel/l3color-base.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3debug.dtx b/macros/latex/contrib/l3kernel/l3debug.dtx index 30f0f78b5e..d05991e37d 100644 --- a/macros/latex/contrib/l3kernel/l3debug.dtx +++ b/macros/latex/contrib/l3kernel/l3debug.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3deprecation.dtx b/macros/latex/contrib/l3kernel/l3deprecation.dtx index 6d13ae1b38..e60766e876 100644 --- a/macros/latex/contrib/l3kernel/l3deprecation.dtx +++ b/macros/latex/contrib/l3kernel/l3deprecation.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3doc.dtx b/macros/latex/contrib/l3kernel/l3doc.dtx index 25ad20f7dd..3eff299b53 100644 --- a/macros/latex/contrib/l3kernel/l3doc.dtx +++ b/macros/latex/contrib/l3kernel/l3doc.dtx @@ -79,7 +79,7 @@ and all files in that bundle must be distributed together. % % \title{The \cls{l3doc} class} % \author{\Team} -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % \maketitle % \tableofcontents % diff --git a/macros/latex/contrib/l3kernel/l3docstrip.dtx b/macros/latex/contrib/l3kernel/l3docstrip.dtx index 2e455845db..7dc8dbe355 100644 --- a/macros/latex/contrib/l3kernel/l3docstrip.dtx +++ b/macros/latex/contrib/l3kernel/l3docstrip.dtx @@ -63,7 +63,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3docstrip.pdf b/macros/latex/contrib/l3kernel/l3docstrip.pdf Binary files differindex 9a63be0ebe..91cfd0b37b 100644 --- a/macros/latex/contrib/l3kernel/l3docstrip.pdf +++ b/macros/latex/contrib/l3kernel/l3docstrip.pdf diff --git a/macros/latex/contrib/l3kernel/l3expan.dtx b/macros/latex/contrib/l3kernel/l3expan.dtx index 49fafac296..544887627c 100644 --- a/macros/latex/contrib/l3kernel/l3expan.dtx +++ b/macros/latex/contrib/l3kernel/l3expan.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3file.dtx b/macros/latex/contrib/l3kernel/l3file.dtx index fc6f7260a0..132741ccda 100644 --- a/macros/latex/contrib/l3kernel/l3file.dtx +++ b/macros/latex/contrib/l3kernel/l3file.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3flag.dtx b/macros/latex/contrib/l3kernel/l3flag.dtx index 2475241f91..f2df290cb5 100644 --- a/macros/latex/contrib/l3kernel/l3flag.dtx +++ b/macros/latex/contrib/l3kernel/l3flag.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-assign.dtx b/macros/latex/contrib/l3kernel/l3fp-assign.dtx index 918c7b9941..b629576584 100644 --- a/macros/latex/contrib/l3kernel/l3fp-assign.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-assign.dtx @@ -40,7 +40,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % \maketitle % % \begin{documentation} diff --git a/macros/latex/contrib/l3kernel/l3fp-aux.dtx b/macros/latex/contrib/l3kernel/l3fp-aux.dtx index 60662e3560..ece95374b5 100644 --- a/macros/latex/contrib/l3kernel/l3fp-aux.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-aux.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-basics.dtx b/macros/latex/contrib/l3kernel/l3fp-basics.dtx index 4fa89b6355..634c5f6f11 100644 --- a/macros/latex/contrib/l3kernel/l3fp-basics.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-basics.dtx @@ -40,7 +40,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-convert.dtx b/macros/latex/contrib/l3kernel/l3fp-convert.dtx index 1b28754a82..ccfaba6475 100644 --- a/macros/latex/contrib/l3kernel/l3fp-convert.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-convert.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-expo.dtx b/macros/latex/contrib/l3kernel/l3fp-expo.dtx index b30fd883bd..77bc35d84d 100644 --- a/macros/latex/contrib/l3kernel/l3fp-expo.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-expo.dtx @@ -40,7 +40,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-extended.dtx b/macros/latex/contrib/l3kernel/l3fp-extended.dtx index 2ff5305b4c..337875215c 100644 --- a/macros/latex/contrib/l3kernel/l3fp-extended.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-extended.dtx @@ -40,7 +40,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-logic.dtx b/macros/latex/contrib/l3kernel/l3fp-logic.dtx index 28ed6bba76..c2422c1a5e 100644 --- a/macros/latex/contrib/l3kernel/l3fp-logic.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-logic.dtx @@ -40,7 +40,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-parse.dtx b/macros/latex/contrib/l3kernel/l3fp-parse.dtx index 894cf439d9..cc8e6520d3 100644 --- a/macros/latex/contrib/l3kernel/l3fp-parse.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-parse.dtx @@ -40,7 +40,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-random.dtx b/macros/latex/contrib/l3kernel/l3fp-random.dtx index e8682c57a8..8ba199e5e4 100644 --- a/macros/latex/contrib/l3kernel/l3fp-random.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-random.dtx @@ -40,7 +40,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-round.dtx b/macros/latex/contrib/l3kernel/l3fp-round.dtx index 03de976829..5187a83a8e 100644 --- a/macros/latex/contrib/l3kernel/l3fp-round.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-round.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp-traps.dtx b/macros/latex/contrib/l3kernel/l3fp-traps.dtx index 377596fb59..1bd6a3ad4f 100644 --- a/macros/latex/contrib/l3kernel/l3fp-traps.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-traps.dtx @@ -40,7 +40,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % \maketitle % % \begin{documentation} diff --git a/macros/latex/contrib/l3kernel/l3fp-trig.dtx b/macros/latex/contrib/l3kernel/l3fp-trig.dtx index eefc4c7ccb..d602e51b6e 100644 --- a/macros/latex/contrib/l3kernel/l3fp-trig.dtx +++ b/macros/latex/contrib/l3kernel/l3fp-trig.dtx @@ -40,7 +40,7 @@ % {latex-team@latex-project.org}^^A % }^^A % } -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fp.dtx b/macros/latex/contrib/l3kernel/l3fp.dtx index 51f1a30c18..2f2dddd645 100644 --- a/macros/latex/contrib/l3kernel/l3fp.dtx +++ b/macros/latex/contrib/l3kernel/l3fp.dtx @@ -49,7 +49,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3fparray.dtx b/macros/latex/contrib/l3kernel/l3fparray.dtx index 5d48154ae8..96a02296b8 100644 --- a/macros/latex/contrib/l3kernel/l3fparray.dtx +++ b/macros/latex/contrib/l3kernel/l3fparray.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3int.dtx b/macros/latex/contrib/l3kernel/l3int.dtx index f246d8400b..90ae8a493e 100644 --- a/macros/latex/contrib/l3kernel/l3int.dtx +++ b/macros/latex/contrib/l3kernel/l3int.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3intarray.dtx b/macros/latex/contrib/l3kernel/l3intarray.dtx index 7c9d2372d9..fc9f8bf2c4 100644 --- a/macros/latex/contrib/l3kernel/l3intarray.dtx +++ b/macros/latex/contrib/l3kernel/l3intarray.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % @@ -461,6 +461,51 @@ % \end{macro} % \end{macro} % +% \begin{macro}[rEXP]{\__kernel_intarray_range_to_clist:Nnn, \@@_range_to_clist:ww} +% Loop through part of the array. +% \begin{macrocode} +\cs_new:Npn \__kernel_intarray_range_to_clist:Nnn #1#2#3 + { + \exp_last_unbraced:Nf \use_none:n + { + \exp_after:wN \@@_range_to_clist:ww + \int_value:w \int_eval:w #2 \exp_after:wN ; + \int_value:w \int_eval:w #3 ; + #1 \prg_break_point: + } + } +\cs_new:Npn \@@_range_to_clist:ww #1 ; #2 ; #3 + { + \if_int_compare:w #1 > #2 \exp_stop_f: + \prg_break:n + \fi: + , \__kernel_intarray_item:Nn #3 {#1} + \exp_after:wN \@@_range_to_clist:ww + \int_value:w \int_eval:w #1 + \c_one_int ; #2 ; #3 + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\__kernel_intarray_gset_range_from_clist:Nnn, \@@_gset_range:Nw} +% Loop through part of the array. +% \begin{macrocode} +\cs_new_protected:Npn \__kernel_intarray_gset_range_from_clist:Nnn #1#2#3 + { + \int_set:Nn \l_@@_loop_int {#2} + \@@_gset_range:Nw #1 #3 , , \prg_break_point: + } +\cs_new_protected:Npn \@@_gset_range:Nw #1 #2 , + { + \if_catcode:w \scan_stop: \tl_to_str:n {#2} \scan_stop: + \prg_break:n + \fi: + \__kernel_intarray_gset:Nnn #1 \l_@@_loop_int {#2} + \int_incr:N \l_@@_loop_int + \@@_gset_range:Nw #1 + } +% \end{macrocode} +% \end{macro} +% % \begin{macro}{\intarray_show:N, \intarray_show:c, \intarray_log:N, \intarray_log:c} % Convert the list to a comma list (with spaces after each comma) % \begin{macrocode} diff --git a/macros/latex/contrib/l3kernel/l3kernel-functions.dtx b/macros/latex/contrib/l3kernel/l3kernel-functions.dtx index b844c89385..76778ce5c2 100644 --- a/macros/latex/contrib/l3kernel/l3kernel-functions.dtx +++ b/macros/latex/contrib/l3kernel/l3kernel-functions.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % @@ -207,6 +207,31 @@ % the \cs{intarray_count:N}, lest a low-level \TeX{} error occur. % \end{function} % +% \begin{function}[rEXP, added = 2020-07-12]{\__kernel_intarray_range_to_clist:Nnn} +% \begin{syntax} +% \cs{__kernel_intarray_range_to_clist:Nnn} \meta{intarray~var} \Arg{start index} \Arg{end index} +% \end{syntax} +% Converts to integer denotations separated by commas the entries of +% the \meta{intarray} from positions \meta{start index} to \meta{end +% index} included. The \meta{start index} and \meta{end index} must +% be suitable for a direct assignment to a \TeX{} count register, must +% be between $1$ and the \cs{intarray_count:N}, and be suitably +% ordered. All tokens have category code other. +% \end{function} +% +% \begin{function}[added = 2020-07-12]{\__kernel_intarray_gset_range_from_clist:Nnn} +% \begin{syntax} +% \cs{__kernel_intarray_gset_range_from_clist:Nnn} \meta{intarray~var} \Arg{start index} \Arg{integer clist} +% \end{syntax} +% Stores the entries of the \meta{clist} as entries of the +% \meta{intarray~var} starting from the \meta{start index}, upwards. +% This is done without any bound checking. The \meta{start index} and +% all entries of the \meta{integer comma list} (which do not undergo +% space trimming and brace stripping as in normal clist mappings) must +% be suitable for a direct assignment to a \TeX{} count register. An +% empty entry may stop the loop. +% \end{function} +% % \begin{function}{\__kernel_ior_open:Nn, \__kernel_ior_open:No} % \begin{syntax} % \cs{__kernel_ior_open:Nn} \meta{stream} \Arg{file name} diff --git a/macros/latex/contrib/l3kernel/l3keys.dtx b/macros/latex/contrib/l3kernel/l3keys.dtx index ea9b1f9d04..495346fa20 100644 --- a/macros/latex/contrib/l3kernel/l3keys.dtx +++ b/macros/latex/contrib/l3kernel/l3keys.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3legacy.dtx b/macros/latex/contrib/l3kernel/l3legacy.dtx index 041c39c333..f640d04cd1 100644 --- a/macros/latex/contrib/l3kernel/l3legacy.dtx +++ b/macros/latex/contrib/l3kernel/l3legacy.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3luatex.dtx b/macros/latex/contrib/l3kernel/l3luatex.dtx index 880eb79f9e..891b7e0654 100644 --- a/macros/latex/contrib/l3kernel/l3luatex.dtx +++ b/macros/latex/contrib/l3kernel/l3luatex.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3msg.dtx b/macros/latex/contrib/l3kernel/l3msg.dtx index cde386e371..96bc2ca447 100644 --- a/macros/latex/contrib/l3kernel/l3msg.dtx +++ b/macros/latex/contrib/l3kernel/l3msg.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3names.dtx b/macros/latex/contrib/l3kernel/l3names.dtx index b75c20eb08..5333720124 100644 --- a/macros/latex/contrib/l3kernel/l3names.dtx +++ b/macros/latex/contrib/l3kernel/l3names.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3news.pdf b/macros/latex/contrib/l3kernel/l3news.pdf Binary files differindex cd9eead890..5dc815800c 100644 --- a/macros/latex/contrib/l3kernel/l3news.pdf +++ b/macros/latex/contrib/l3kernel/l3news.pdf diff --git a/macros/latex/contrib/l3kernel/l3news01.pdf b/macros/latex/contrib/l3kernel/l3news01.pdf Binary files differindex 4919fac2b1..a3dc4f6315 100644 --- a/macros/latex/contrib/l3kernel/l3news01.pdf +++ b/macros/latex/contrib/l3kernel/l3news01.pdf diff --git a/macros/latex/contrib/l3kernel/l3news02.pdf b/macros/latex/contrib/l3kernel/l3news02.pdf Binary files differindex f5a33f51b4..f4fb786dd3 100644 --- a/macros/latex/contrib/l3kernel/l3news02.pdf +++ b/macros/latex/contrib/l3kernel/l3news02.pdf diff --git a/macros/latex/contrib/l3kernel/l3news03.pdf b/macros/latex/contrib/l3kernel/l3news03.pdf Binary files differindex 9ee630bddb..a31cd89b97 100644 --- a/macros/latex/contrib/l3kernel/l3news03.pdf +++ b/macros/latex/contrib/l3kernel/l3news03.pdf diff --git a/macros/latex/contrib/l3kernel/l3news04.pdf b/macros/latex/contrib/l3kernel/l3news04.pdf Binary files differindex 843b757d41..14a59aa796 100644 --- a/macros/latex/contrib/l3kernel/l3news04.pdf +++ b/macros/latex/contrib/l3kernel/l3news04.pdf diff --git a/macros/latex/contrib/l3kernel/l3news05.pdf b/macros/latex/contrib/l3kernel/l3news05.pdf Binary files differindex b6e9ce58cf..9ffb4f8ba7 100644 --- a/macros/latex/contrib/l3kernel/l3news05.pdf +++ b/macros/latex/contrib/l3kernel/l3news05.pdf diff --git a/macros/latex/contrib/l3kernel/l3news06.pdf b/macros/latex/contrib/l3kernel/l3news06.pdf Binary files differindex 0a9530e337..7f3a5bfeff 100644 --- a/macros/latex/contrib/l3kernel/l3news06.pdf +++ b/macros/latex/contrib/l3kernel/l3news06.pdf diff --git a/macros/latex/contrib/l3kernel/l3news07.pdf b/macros/latex/contrib/l3kernel/l3news07.pdf Binary files differindex 2a946711ea..d610b0f7ec 100644 --- a/macros/latex/contrib/l3kernel/l3news07.pdf +++ b/macros/latex/contrib/l3kernel/l3news07.pdf diff --git a/macros/latex/contrib/l3kernel/l3news08.pdf b/macros/latex/contrib/l3kernel/l3news08.pdf Binary files differindex b4e91f3196..aff2eb1a9c 100644 --- a/macros/latex/contrib/l3kernel/l3news08.pdf +++ b/macros/latex/contrib/l3kernel/l3news08.pdf diff --git a/macros/latex/contrib/l3kernel/l3news09.pdf b/macros/latex/contrib/l3kernel/l3news09.pdf Binary files differindex 43ee33b148..325bef60cc 100644 --- a/macros/latex/contrib/l3kernel/l3news09.pdf +++ b/macros/latex/contrib/l3kernel/l3news09.pdf diff --git a/macros/latex/contrib/l3kernel/l3news10.pdf b/macros/latex/contrib/l3kernel/l3news10.pdf Binary files differindex d8cbe38b33..07ce82bad6 100644 --- a/macros/latex/contrib/l3kernel/l3news10.pdf +++ b/macros/latex/contrib/l3kernel/l3news10.pdf diff --git a/macros/latex/contrib/l3kernel/l3news11.pdf b/macros/latex/contrib/l3kernel/l3news11.pdf Binary files differindex 65240af456..18f5740e94 100644 --- a/macros/latex/contrib/l3kernel/l3news11.pdf +++ b/macros/latex/contrib/l3kernel/l3news11.pdf diff --git a/macros/latex/contrib/l3kernel/l3news12.pdf b/macros/latex/contrib/l3kernel/l3news12.pdf Binary files differindex 37d8e10aa1..8dc788a1e6 100644 --- a/macros/latex/contrib/l3kernel/l3news12.pdf +++ b/macros/latex/contrib/l3kernel/l3news12.pdf diff --git a/macros/latex/contrib/l3kernel/l3prefixes.csv b/macros/latex/contrib/l3kernel/l3prefixes.csv index b51851ffec..46ce3a046b 100644 --- a/macros/latex/contrib/l3kernel/l3prefixes.csv +++ b/macros/latex/contrib/l3kernel/l3prefixes.csv @@ -11,7 +11,7 @@ alloc,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,http ampersand,l3kernel,The LaTeX3 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, -array,hobby,Andrew Stacey,,,,2013-03-16,2013-03-16, +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, atsign,l3kernel,The LaTeX3 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, backend,l3backend,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2019-06-04,2019-06-04, @@ -70,6 +70,7 @@ fdudoc,fduthesis,Xiangdong Zeng,https://github.com/stone-zeng/fduthesis,https:// fdulogo,fduthesis,Xiangdong Zeng,https://github.com/stone-zeng/fduthesis,https://github.com/stone-zeng/fduthesis.git,https://github.com/stone-zeng/fduthesis/issues,2018-06-14,2020-03-08, fi,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28, file,l3kernel,The LaTeX3 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, +filehook,ltfilehook,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2020-10-01,2020-10-01, flag,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2017-02-13, fltr,newlfm,Paul Thomson,,,,2013-01-29,2013-01-29, fmdug,dashundergaps,Frank Mittelbach,https://www.latex-project.org/,https://github.com/FrankMittelbach/fmitex.git,https://github.com/FrankMittelbach/fmitex/issues,2018-06-24,2018-06-24, @@ -92,6 +93,7 @@ hbox,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https hcoffin,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28, hobete,hobete,Tobias Görlach,http://www.disk0s1.de,,,2012-11-07,2012-11-07, hook,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2019-06-03,2019-06-03, +hyp,hyperref,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/hyperref.git,https://github.com/latex3/hyperref/issues,2020-11-27,2020-11-27, if,l3kernel,The LaTeX3 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, inf,l3kernel,The LaTeX3 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, insert,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-28,2012-09-28, @@ -99,6 +101,7 @@ int,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https: intarray,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2018-04-06,2018-04-06, ior,l3kernel,The LaTeX3 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, iow,l3kernel,The LaTeX3 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, +jiazhu,jiazhu,Qing Lee,https://github.com/CTeX-org/ctex-kit,https://github.com/CTeX-org/ctex-kit.git,https://github.com/CTeX-org/ctex-kit/issues,2020-05-17,2020-05-17, kernel,l3kernel,The LaTeX3 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, keys,"l3kernel,l3keys2e",The LaTeX3 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, keyval,l3kernel,The LaTeX3 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, @@ -108,13 +111,13 @@ lltxmath,lualatex-math,Philipp Stephani,https://github.com/phst/lualatex-math,ht log,l3kernel,The LaTeX3 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, lua,l3kernel,The LaTeX3 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, luatex,l3kernel,The LaTeX3 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, -jiazhu,jiazhu,Qing Lee,https://github.com/CTeX-org/ctex-kit,https://github.com/CTeX-org/ctex-kit.git,https://github.com/CTeX-org/ctex-kit/issues,2020-05-17,2020-05-17, mark,l3kernel,The LaTeX3 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, marks,l3kernel/xmarks,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2020-02-17,2020-02-17, math,l3kernel,The LaTeX3 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, max,l3kernel,The LaTeX3 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, mcrule,multicolrule,Karl Hagen,https://github.com/polysyllabic/multicolrule,https://github.com/polysyllabic/multicolrule.git,https://github.com/polysyllabic/multicolrule/issues,2018-12-24,2018-12-24, mermap,mercatormap,Thomas F. Sturm,https://github.com/T-F-S/mercatormap,https://github.com/T-F-S/mercatormap.git,https://github.com/T-F-S/mercatormap/issues,2020-02-19,2020-02-19, +metrix,metrix,Tobias Weh,https://github.com/tweh/metrix,git@github.com:tweh/metrix.git,https://github.com/tweh/metrix/issues,2020-10-31,2019-10-09, mhchem,mhchem,Martin Hensel,,,,2014-02-05,2014-02-05, minibox,minibox,Will Robertson,,https://github.com/wspr/will2e.git,https://github.com/wspr/will2e/issues,2020-04-24,2020-04-24, minus,l3kernel,The LaTeX3 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, @@ -140,6 +143,7 @@ overleaf,overleaf,Overleaf,https://www.overleaf.com/about,,support+tex-dev@overl parameter,l3kernel,The LaTeX3 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, pbs,media9,Alexander Grahn,,https://gitlab.com/agrahn/media9,https://gitlab.com/agrahn/media9/issues,2016-02-26,2020-04-15, pdf,l3kernel,The LaTeX3 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, +pdfmanagement,l3pdfmanagement,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2020-11-27,2020-11-27, pdfoverlay,pdfoverlay,David Purton,https://github.com/dcpurton/pdfoverlay,https://github.com/dcpurton/pdfoverlay.git,https://github.com/dcpurton/pdfoverlay/issues,2020-06-22,2020-06-22, pdftex,l3kernel,The LaTeX3 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, peek,l3kernel,The LaTeX3 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, @@ -149,14 +153,15 @@ pi,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https:/ pkgploader,pkgploader,Michiel Helvensteijn,,,,2014-02-05,2014-02-05, platex,platex,Japanese TeX Development Community,https://github.com/texjporg/platex,https://github.com/texjporg/platex.git,https://github.com/texjporg/platex/issues,2020-09-30,2020-09-30, polyglossia,polyglossia,Arthur Reutenauer,https://www.polyglossia.org/,https://github.com/reutenauer/polyglossia,https://github.com/reutenauer/polyglossia/issues,2019-09-03,, +prelim,prelim2e,Marei Peischl,https://github.com/TeXhackse/prelim2e,https://github.com/TeXhackse/prelim2e.git,https://github.com/TeXhackse/prelim2e/issues,2020-11-24,2020-11-24, prg,l3kernel,The LaTeX3 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, primargs,morewrites,Bruno Le Floch,https://github.com/blefloch/latex-morewrites,https://github.com/blefloch/latex-morewrites.git,https://github.com/blefloch/latex-morewrites/issues,2013-03-16,2015-09-22, prop,l3kernel,The LaTeX3 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, 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 LaTeX3 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 -quark,l3kernel,The LaTeX3 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, 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 LaTeX3 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, randomwalk,randomwalk,Bruno Le Floch,https://github.com/blefloch/latex-randomwalk,https://github.com/blefloch/latex-randomwalk.git,https://github.com/blefloch/latex-randomwalk/issues,2013-03-16,2015-09-22, recursion,l3kernel,The LaTeX3 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, regex,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2018-04-06, @@ -168,6 +173,7 @@ scan,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https scontents,scontents,Pablo González,https://github.com/pablgonz/scontents,git@github.com:pablgonz/scontents.git,https://github.com/pablgonz/scontents/issues,2019-12-05,2019-12-05, sdaps,sdaps,Benjamin Berg,https://sdaps.org,https://github.com/sdaps/sdaps-class.git,https://github.com/sdaps/sdaps-class/issues,2020-02-17,2020-02-17, seq,l3kernel,The LaTeX3 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, +shipout,ltshipout,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex2e.git,https://github.com/latex3/latex2e/issues,2020-10-01,2020-10-01, siunitx,siunitx,Joseph Wright,https://github.com/josephwright/siunitx,https://github.com/josephwright/siunitx.git,https://github.com/josephwright/siunitx/issues,2012-11-04,2012-11-04, skip,l3kernel,The LaTeX3 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, sort,l3kernel,The LaTeX3 Project,https://www.latex-project.org/latex3.html,https://github.com/latex3/latex3.git,https://github.com/latex3/latex3/issues,2012-09-27,2017-02-13, @@ -190,6 +196,7 @@ tl,"l3kernel,l3tl-analysis,l3tl-build",The LaTeX3 Project,https://www.latex-proj tmpa,l3kernel,The LaTeX3 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, tmpb,l3kernel,The LaTeX3 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, token,l3kernel,The LaTeX3 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, +twmk,menukeys,Tobias Weh,https://github.com/tweh/menukeys,git@github.com:tweh/menukeys.git,https://github.com/tweh/menukeys/issues,2020-10-31,2020-10-31,“classic” L2 package using only some expl3 code ufcombo,combofont,Ulrike Fischer,https://github.com/u-fischer/combofont,https://github.com/u-fischer/combofont,https://github.com/u-fischer/combofont/issues,2020-04-24,2020-04-24, ufgrid,returntogrid,Ulrike Fischer,https://github.com/u-fischer/returntogrid,https://github.com/u-fischer/returntogrid,https://github.com/u-fischer/returntogrid/issues,2020-04-24,2020-04-24, uftag,tagpdf,Ulrike Fischer,,,,2018-07-15,2018-07-15, diff --git a/macros/latex/contrib/l3kernel/l3prefixes.pdf b/macros/latex/contrib/l3kernel/l3prefixes.pdf Binary files differindex 50089cf13c..78022e7601 100644 --- a/macros/latex/contrib/l3kernel/l3prefixes.pdf +++ b/macros/latex/contrib/l3kernel/l3prefixes.pdf diff --git a/macros/latex/contrib/l3kernel/l3prg.dtx b/macros/latex/contrib/l3kernel/l3prg.dtx index 3f1280f23e..b5b1e4246e 100644 --- a/macros/latex/contrib/l3kernel/l3prg.dtx +++ b/macros/latex/contrib/l3kernel/l3prg.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3prop.dtx b/macros/latex/contrib/l3kernel/l3prop.dtx index 9419a5ce6c..84b41be5a0 100644 --- a/macros/latex/contrib/l3kernel/l3prop.dtx +++ b/macros/latex/contrib/l3kernel/l3prop.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3quark.dtx b/macros/latex/contrib/l3kernel/l3quark.dtx index 16a9b0c447..487393eb6a 100644 --- a/macros/latex/contrib/l3kernel/l3quark.dtx +++ b/macros/latex/contrib/l3kernel/l3quark.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3regex.dtx b/macros/latex/contrib/l3kernel/l3regex.dtx index 2086b5c3ec..5e64d73980 100644 --- a/macros/latex/contrib/l3kernel/l3regex.dtx +++ b/macros/latex/contrib/l3kernel/l3regex.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % @@ -713,7 +713,7 @@ % |{n,}| quantifier? (I think not.) % \item Quantifiers for |\u| and assertions. % \item When matching, keep track of an explicit stack of -% \texttt{current_state} and \texttt{current_submatches}. +% \texttt{curr_state} and \texttt{curr_submatches}. % \item If possible, when a state is reused by the same thread, kill % other subthreads. % \item Use an array rather than \cs[no-index]{l__regex_balance_tl} @@ -845,12 +845,14 @@ % names). % \begin{itemize} % \item \emph{Group}: index of the capturing group, $-1$ for -% non-capturing groups. +% non-capturing groups. ^^A start/end index? % \item \emph{Position}: each token in the query is labelled by an % integer \meta{position}, with $\texttt{min_pos} - 1 \leq % \meta{position} \leq \texttt{max_pos}$. The lowest and highest -% positions correspond to imaginary begin and end markers (with -% inaccessible category code and character code). +% positions $\texttt{min_pos} - 1$ and $\texttt{max_pos}$ +% correspond to imaginary begin and end markers (with +% non-existent category code and character code). +% $\texttt{max_pos}$ is only set quite late in the processing. % \item \emph{Query}: the token list to which we apply the regular % expression. % \item \emph{State}: each state of the \textsc{nfa} is labelled by an @@ -865,8 +867,8 @@ % unique id for all the steps of the matching algorithm. % \end{itemize} % -% We use \pkg{l3intarray} to manipulate arrays of integers (stored into -% some dimension registers in scaled points). We also abuse \TeX{}'s +% We use \pkg{l3intarray} to manipulate arrays of integers. +% We also abuse \TeX{}'s % \tn{toks} registers, by accessing them directly by number rather than % tying them to control sequence using the \tn{newtoks} allocation % functions. Specifically, these arrays and \tn{toks} are used as @@ -876,21 +878,14 @@ % \begin{itemize} % \item \cs{g_@@_state_active_intarray} holds the last \meta{step} in % which each \meta{state} was active. -% \item \cs{g_@@_thread_state_intarray} maps each \meta{thread} (with -% $\texttt{min_active} \leq \meta{thread} < \texttt{max_active}$) to -% the \meta{state} in which the \meta{thread} currently is. The +% \item \cs{g_@@_thread_info_intarray} consists of blocks for each +% \meta{thread} (with $\texttt{min_thread} \leq \meta{thread} < +% \texttt{max_thread}$). Each block has +% $1+2\cs{l_@@_capturing_group_int}$ entries: the \meta{state} in +% which the \meta{thread} currently is, followed by the beginnings +% of all submatches, and then the ends of all submatches. The % \meta{threads} are ordered starting from the best to the least % preferred. -% \item \tn{toks}\meta{thread} holds the submatch information for the -% \meta{thread}, as the contents of a property list. -% \item \cs{g_@@_charcode_intarray} and \cs{g_@@_catcode_intarray} hold the -% character codes and category codes of tokens at each -% \meta{position} in the query. -% \item \cs{g_@@_balance_intarray} holds the balance of begin-group and -% end-group character tokens which appear before that point in the -% token list. -% \item \tn{toks}\meta{position} holds \meta{tokens} which \texttt{o}- -% and \texttt{x}-expand to the \meta{position}-th token in the query. % \item \cs{g_@@_submatch_prev_intarray}, \cs{g_@@_submatch_begin_intarray} % and \cs{g_@@_submatch_end_intarray} hold, for each submatch (as would % be extracted by \cs{regex_extract_all:nnN}), the place where the @@ -901,6 +896,14 @@ % block corresponding to one match with all its submatches stored in % consecutive entries. % \end{itemize} +% When actually building the result, +% \begin{itemize} +% \item \tn{toks}\meta{position} holds \meta{tokens} which \texttt{o}- +% and \texttt{x}-expand to the \meta{position}-th token in the query. +% \item \cs{g_@@_balance_intarray} holds the balance of begin-group and +% end-group character tokens which appear before that point in the +% token list. +% \end{itemize} % % The code is structured as follows. Variables are introduced in the % relevant section. First we present some generic helper functions. Then @@ -944,7 +947,7 @@ { \@@_toks_set:Nn #1 { } } \cs_new_eq:NN \@@_toks_set:Nn \tex_toks:D \cs_new_protected:Npn \@@_toks_set:No #1 - { \@@_toks_set:Nn #1 \exp_after:wN } + { \tex_toks:D #1 \exp_after:wN } % \end{macrocode} % \end{macro} % @@ -975,13 +978,13 @@ % \begin{macrocode} \cs_new_protected:Npn \@@_toks_put_left:Nx #1#2 { - \cs_set:Npx \@@_tmp:w { #2 } + \cs_set_nopar:Npx \@@_tmp:w { #2 } \tex_toks:D #1 \exp_after:wN \exp_after:wN \exp_after:wN { \exp_after:wN \@@_tmp:w \tex_the:D \tex_toks:D #1 } } \cs_new_protected:Npn \@@_toks_put_right:Nx #1#2 { - \cs_set:Npx \@@_tmp:w {#2} + \cs_set_nopar:Npx \@@_tmp:w {#2} \tex_toks:D #1 \exp_after:wN { \tex_the:D \tex_toks:D \exp_after:wN #1 \@@_tmp:w } } @@ -1000,11 +1003,38 @@ \cs_new:Npn \@@_curr_cs_to_str: { \exp_after:wN \exp_after:wN \exp_after:wN \cs_to_str:N - \tex_the:D \tex_toks:D \l_@@_curr_pos_int + \l_@@_curr_token_tl } % \end{macrocode} % \end{macro} % +% \begin{macro}{\@@_intarray_item:NnF, \@@_intarray_item_aux:nNF} +% Item of intarray, with a default value. +% \begin{macrocode} +\cs_new:Npn \@@_intarray_item:NnF #1#2 + { \exp_args:Nf \@@_intarray_item_aux:nNF { \int_eval:n {#2} } #1 } +\cs_new:Npn \@@_intarray_item_aux:nNF #1#2 + { + \if_int_compare:w #1 > \c_zero_int + \exp_after:wN \use_i:nn + \else: + \exp_after:wN \use_ii:nn + \fi: + { \__kernel_intarray_item:Nn #2 {#1} } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_maplike_break:} +% Analogous to \cs{tl_map_break:}, this correctly exits +% \cs{tl_map_inline:nn} and similar constructions and jumps to the +% matching \cs{prg_break_point:Nn} \cs{@@_maplike_break:} |{| |}|. +% \begin{macrocode} +\cs_new:Npn \@@_maplike_break: + { \prg_map_break:Nn \@@_maplike_break: { } } +% \end{macrocode} +% \end{macro} +% % \subsubsection{Constants and variables} % % \begin{macro}{\@@_tmp:w} @@ -1056,19 +1086,6 @@ % \end{macrocode} % \end{variable} % -% \begin{variable}{\g_@@_charcode_intarray, \g_@@_catcode_intarray, \g_@@_balance_intarray} -% The first thing we do when matching is to go once through the query -% token list and store the information for each token into -% \cs{g_@@_charcode_intarray}, \cs{g_@@_catcode_intarray} and \tn{toks} -% registers. We also store the balance of begin-group/end-group -% characters into \cs{g_@@_balance_intarray}. -% \begin{macrocode} -\intarray_new:Nn \g_@@_charcode_intarray { 65536 } -\intarray_new:Nn \g_@@_catcode_intarray { 65536 } -\intarray_new:Nn \g_@@_balance_intarray { 65536 } -% \end{macrocode} -% \end{variable} -% % \begin{variable}{\l_@@_balance_int} % During this phase, \cs{l_@@_balance_int} counts the balance of % begin-group and end-group character tokens which appear before a @@ -1079,15 +1096,6 @@ % \end{macrocode} % \end{variable} % -% \begin{variable}{\l_@@_cs_name_tl} -% This variable is used in \cs{@@_item_cs:n} to store the csname of -% the currently-tested token when the regex contains a sub-regex for -% testing csnames. -% \begin{macrocode} -\tl_new:N \l_@@_cs_name_tl -% \end{macrocode} -% \end{variable} -% % \subsubsection{Testing characters} % % \begin{macro}{\c_@@_ascii_min_int, \c_@@_ascii_max_control_int, \c_@@_ascii_max_int} @@ -1201,7 +1209,7 @@ % \begin{macro} % {\@@_item_caseless_equal:n, \@@_item_caseless_range:nn} % For caseless matching, we perform the test both on the -% \texttt{current_char} and on the \texttt{case_changed_char}. Before +% \texttt{curr_char} and on the \texttt{case_changed_char}. Before % doing the second set of tests, we make sure that % \texttt{case_changed_char} has been computed. % \begin{macrocode} @@ -1308,7 +1316,8 @@ % % \begin{macro}{\@@_item_exact:nn, \@@_item_exact_cs:n} % This matches an exact \meta{category}-\meta{character code} pair, or -% an exact control sequence, more precisely one of several possible control sequences. +% an exact control sequence, more precisely one of several possible +% control sequences, separated by \cs{scan_stop:}. % \begin{macrocode} \cs_new_protected:Npn \@@_item_exact:nn #1#2 { @@ -1337,24 +1346,19 @@ % Match a control sequence (the argument is a compiled regex). % First test the catcode of the current token to be zero. % Then perform the matching test, and break if the csname -% indeed matches. The three \cs{exp_after:wN} expand the contents -% of the \tn{toks}\meta{current position} (of the form \cs{exp_not:n} -% \Arg{control sequence}) to \meta{control sequence}. -% We store the cs name before building states for the cs, as those -% states may overlap with toks registers storing the user's input. +% indeed matches. % \begin{macrocode} \cs_new_protected:Npn \@@_item_cs:n #1 { \int_compare:nNnT \l_@@_curr_catcode_int = 0 { \group_begin: - \__kernel_tl_set:Nx \l_@@_cs_name_tl { \@@_curr_cs_to_str: } \@@_single_match: \@@_disable_submatches: \@@_build_for_cs:n {#1} \bool_set_eq:NN \l_@@_saved_success_bool \g_@@_success_bool - \exp_args:NV \@@_match_cs:n \l_@@_cs_name_tl + \exp_args:Nx \@@_match_cs:n { \@@_curr_cs_to_str: } \if_meaning:w \c_true_bool \g_@@_success_bool \group_insert_after:N \@@_break_true:w \fi: @@ -1822,7 +1826,7 @@ % \item \cs{@@_command_K:} % \item \cs{@@_assertion:Nn} \meta{boolean} \Arg{assertion test}, % where the \meta{assertion test} is \cs{@@_b_test:} or -% |{|\cs{@@_anchor:N} \meta{integer}|}| +% \cs{@@_Z_test:} or \cs{@@_A_test:} or \cs{@@_G_test:} % \end{itemize} % Tests can be the following: % \begin{itemize} @@ -2663,73 +2667,56 @@ % % \subsubsection{Anchoring and simple assertions} % -% \begin{macro}{\@@_compile_anchor:NF} +% \begin{macro}{\@@_compile_anchor_letter:NNN} +% \begin{macro}{\@@_compile_/A:, \@@_compile_/G:, \@@_compile_/Z:, \@@_compile_/z:, \@@_compile_/b:, \@@_compile_/B:} % \begin{macro}+\@@_compile_^:+ -% \begin{macro}{\@@_compile_/A:, \@@_compile_/G:} % \begin{macro}+\@@_compile_$:+ -% \begin{macro}{\@@_compile_/Z:, \@@_compile_/z:} -% In modes where assertions are allowed, anchor to the start of the -% query, the start of the match, or the end of the query, depending on -% the integer |#1|. In other modes, |#2| treats the character as raw, -% with an error for escaped letters (|$| is valid in a class, but |\A| -% is definitely a mistake on the user's part). +% In modes where assertions are forbidden, anchors such as |\A| +% produce an error (|\A|~is invalid in classes); otherwise they add an +% \cs{@@_assertion:Nn} test as appropriate (the only negative +% assertion is~|\B|). The test functions are defined later. The +% implementation for +% |$| and |^| is only different from |\A| etc because these are valid +% in a class. % \begin{macrocode} -\cs_new_protected:Npn \@@_compile_anchor:NF #1#2 +\cs_new_protected:Npn \@@_compile_anchor_letter:NNN #1#2#3 { - \@@_if_in_class_or_catcode:TF {#2} + \@@_if_in_class_or_catcode:TF { \@@_compile_raw_error:N #1 } { \tl_build_put_right:Nn \l_@@_build_tl - { \@@_assertion:Nn \c_true_bool { \@@_anchor:N #1 } } + { \@@_assertion:Nn #2 {#3} } } } -\cs_set_protected:Npn \@@_tmp:w #1#2 - { - \cs_new_protected:cpn { @@_compile_/#1: } - { \@@_compile_anchor:NF #2 { \@@_compile_raw_error:N #1 } } - } -\@@_tmp:w A \l_@@_min_pos_int -\@@_tmp:w G \l_@@_start_pos_int -\@@_tmp:w Z \l_@@_max_pos_int -\@@_tmp:w z \l_@@_max_pos_int +\cs_new_protected:cpn { @@_compile_/A: } + { \@@_compile_anchor_letter:NNN A \c_true_bool \@@_A_test: } +\cs_new_protected:cpn { @@_compile_/G: } + { \@@_compile_anchor_letter:NNN G \c_true_bool \@@_G_test: } +\cs_new_protected:cpn { @@_compile_/Z: } + { \@@_compile_anchor_letter:NNN Z \c_true_bool \@@_Z_test: } +\cs_new_protected:cpn { @@_compile_/z: } + { \@@_compile_anchor_letter:NNN z \c_true_bool \@@_Z_test: } +\cs_new_protected:cpn { @@_compile_/b: } + { \@@_compile_anchor_letter:NNN b \c_true_bool \@@_b_test: } +\cs_new_protected:cpn { @@_compile_/B: } + { \@@_compile_anchor_letter:NNN B \c_false_bool \@@_b_test: } \cs_set_protected:Npn \@@_tmp:w #1#2 { \cs_new_protected:cpn { @@_compile_#1: } - { \@@_compile_anchor:NF #2 { \@@_compile_raw:N #1 } } + { + \@@_if_in_class_or_catcode:TF { \@@_compile_raw:N #1 } + { + \tl_build_put_right:Nn \l_@@_build_tl + { \@@_assertion:Nn \c_true_bool {#2} } + } + } } -\exp_args:Nx \@@_tmp:w { \iow_char:N \^ } \l_@@_min_pos_int -\exp_args:Nx \@@_tmp:w { \iow_char:N \$ } \l_@@_max_pos_int +\exp_args:Nx \@@_tmp:w { \iow_char:N \^ } { \@@_A_test: } +\exp_args:Nx \@@_tmp:w { \iow_char:N \$ } { \@@_Z_test: } % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % \end{macro} -% \end{macro} -% -% \begin{macro}{\@@_compile_/b:, \@@_compile_/B:} -% Contrarily to |^| and |$|, which could be implemented without really -% knowing what precedes in the token list, this requires more -% information, namely, the knowledge of the last character code. -% \begin{macrocode} -\cs_new_protected:cpn { @@_compile_/b: } - { - \@@_if_in_class_or_catcode:TF - { \@@_compile_raw_error:N b } - { - \tl_build_put_right:Nn \l_@@_build_tl - { \@@_assertion:Nn \c_true_bool { \@@_b_test: } } - } - } -\cs_new_protected:cpn { @@_compile_/B: } - { - \@@_if_in_class_or_catcode:TF - { \@@_compile_raw_error:N B } - { - \tl_build_put_right:Nn \l_@@_build_tl - { \@@_assertion:Nn \c_false_bool { \@@_b_test: } } - } - } -% \end{macrocode} -% \end{macro} % % \subsubsection{Character classes} % @@ -3512,7 +3499,9 @@ { \bool_if:NF ##1 { negative~ } assertion:~##2 } } \cs_set:Npn \@@_b_test: { word~boundary } - \cs_set_eq:NN \@@_anchor:N \@@_show_anchor_to_str:N + \cs_set:Npn \@@_Z_test: { anchor~at~end~(\iow_char:N\\Z) } + \cs_set:Npn \@@_A_test: { anchor~at~start~(\iow_char:N\\A) } + \cs_set:Npn \@@_G_test: { anchor~at~start~of~match~(\iow_char:N\\G) } \cs_set_protected:Npn \@@_item_caseful_equal:n ##1 { \@@_show_one:n { char~code~\int_eval:n{##1} } } \cs_set_protected:Npn \@@_item_caseful_range:nn ##1##2 @@ -3655,24 +3644,6 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}[rEXP]{\@@_show_anchor_to_str:N} -% The argument is an integer telling us where the anchor is. We -% convert that to the relevant info. -% \begin{macrocode} -\cs_new:Npn \@@_show_anchor_to_str:N #1 - { - anchor~at~ - \str_case:nnF { #1 } - { - { \l_@@_min_pos_int } { start~(\iow_char:N\\A) } - { \l_@@_start_pos_int } { start~of~match~(\iow_char:N\\G) } - { \l_@@_max_pos_int } { end~(\iow_char:N\\Z) } - } - { <error:~'#1'~not~recognized> } - } -% \end{macrocode} -% \end{macro} -% % \begin{macro}{\@@_show_item_catcode:NnT} % Produce a sequence of categories which the catcode bitmap |#2| % contains, and show it, indenting the tests on which this catcode @@ -3757,8 +3728,9 @@ % Each state of the \textsc{nfa} is stored in a \tn{toks}. The % operations which can appear in the \tn{toks} are % \begin{itemize} -% \item \cs{@@_action_start_wildcard:} inserted at the start -% of the regular expression to make it unanchored. +% \item \cs{@@_action_start_wildcard:N} \meta{boolean} inserted at the +% start of the regular expression, where a \texttt{true} +% \meta{boolean} makes it unanchored. % \item \cs{@@_action_success:} marks the exit state of the % \textsc{nfa}. % \item \cs{@@_action_cost:n} \Arg{shift} is a transition from the @@ -3772,10 +3744,11 @@ % how they detect and avoid infinite loops. For now, we just need to % know that the \texttt{group} variant must be used for transitions % back to the start of a group. -% \item \cs{@@_action_submatch:n} \Arg{key} where the \meta{key} is -% a group number followed by |<| or |>| for the beginning or end of -% group. This causes the current position in the query to be stored -% as the \meta{key} submatch boundary. +% \item \cs{@@_action_submatch:nN} \Arg{group} \meta{key} where the +% \meta{key} is |<| or |>| for the beginning or end of group +% numbered \meta{group}. This causes the current position in the +% query to be stored as the \meta{key} submatch boundary. +% \item One of these actions, within a conditional. % \end{itemize} % % We strive to preserve the following properties while building. @@ -3793,20 +3766,26 @@ % corresponding end-points of nested groups. % \end{itemize} % -% \begin{macro}{\@@_build:n, \@@_build:N} +% \begin{macro}{\@@_build:n, \@@_build_aux:Nn, \@@_build:N, \@@_build_aux:NN} % The \texttt{n}-type function first compiles its argument. Reset some % variables. Allocate two states, and put a wildcard in state $0$ % (transitions to state $1$ and $0$ state). Then build the regex % within a (capturing) group numbered $0$ (current % value of \texttt{capturing_group}). Finally, if the match reaches the -% last state, it is successful. +% last state, it is successful. A \texttt{false} boolean for argument +% |#1| for the auxiliaries will suppress the wildcard and make the +% match anchored: used for \cs{peek_regex:nTF} and similar. % \begin{macrocode} -\cs_new_protected:Npn \@@_build:n #1 +\cs_new_protected:Npn \@@_build:n + { \@@_build_aux:Nn \c_true_bool } +\cs_new_protected:Npn \@@_build:N + { \@@_build_aux:NN \c_true_bool } +\cs_new_protected:Npn \@@_build_aux:Nn #1#2 { - \@@_compile:n {#1} - \@@_build:N \l_@@_internal_regex + \@@_compile:n {#2} + \@@_build_aux:NN #1 \l_@@_internal_regex } -\cs_new_protected:Npn \@@_build:N #1 +\cs_new_protected:Npn \@@_build_aux:NN #1#2 { \@@_standard_escapechar: \int_zero:N \l_@@_capturing_group_int @@ -3814,8 +3793,8 @@ \@@_build_new_state: \@@_build_new_state: \@@_toks_put_right:Nn \l_@@_left_state_int - { \@@_action_start_wildcard: } - \@@_group:nnnN {#1} { 1 } { 0 } \c_false_bool + { \@@_action_start_wildcard:N #1 } + \@@_group:nnnN {#2} { 1 } { 0 } \c_false_bool \@@_toks_put_right:Nn \l_@@_right_state_int { \@@_action_success: } } @@ -3826,19 +3805,14 @@ % The matching code relies on some global intarray variables, but only % uses a range of their entries. Specifically, % \begin{itemize} -% \item \cs{g_@@_state_active_intarray} from \cs{l_@@_min_state_int} -% to $\cs{l_@@_max_state_int}-1$; -% \item \cs{g_@@_thread_state_intarray} from \cs{l_@@_min_active_int} -% to $\cs{l_@@_max_active_int}-1$. +% \item \cs{g_@@_state_active_intarray} from \cs{l_@@_min_state_int} +% to $\cs{l_@@_max_state_int}-1$; % \end{itemize} -% In fact, some data is stored in \tn{toks} registers (local) in the -% same ranges so these ranges mustn't overlap. This is done by -% setting \cs{l_@@_min_active_int} to \cs{l_@@_max_state_int} after -% building the \textsc{nfa}. Here, in this nested call to the -% matching code, we need the new versions of these ranges to involve +% Here, in this nested call to the +% matching code, we need the new versions of this range to involve % completely new entries of the intarray variables, so we begin by % setting (the new) \cs{l_@@_min_state_int} to (the old) -% \cs{l_@@_max_active_int} to use higher entries. +% \cs{l_@@_max_state_int} to use higher entries. % % When using a regex to match a cs, we don't insert a wildcard, we % anchor at the end, and since we ignore submatches, there is no need @@ -3848,8 +3822,7 @@ % \begin{macrocode} \cs_new_protected:Npn \@@_build_for_cs:n #1 { - \int_set_eq:NN \l_@@_min_state_int \l_@@_max_active_int - \int_set_eq:NN \l_@@_max_state_int \l_@@_min_state_int + \int_set_eq:NN \l_@@_min_state_int \l_@@_max_state_int \@@_build_new_state: \@@_build_new_state: \@@_push_lr_states: @@ -3857,7 +3830,7 @@ \@@_pop_lr_states: \@@_toks_put_right:Nn \l_@@_right_state_int { - \if_int_compare:w \l_@@_curr_pos_int = \l_@@_max_pos_int + \if_int_compare:w -2 = \l_@@_curr_char_int \exp_after:wN \@@_action_success: \fi: } @@ -4195,8 +4168,8 @@ \cs_new_protected:Npn \@@_group_submatches:nNN #1#2#3 { \if_int_compare:w #1 > - 1 \exp_stop_f: - \@@_toks_put_left:Nx #2 { \@@_action_submatch:n { #1 < } } - \@@_toks_put_left:Nx #3 { \@@_action_submatch:n { #1 > } } + \@@_toks_put_left:Nx #2 { \@@_action_submatch:nN {#1} < } + \@@_toks_put_left:Nx #3 { \@@_action_submatch:nN {#1} > } \fi: } % \end{macrocode} @@ -4345,16 +4318,14 @@ % % \subsubsection{Others} % -% \begin{macro}{\@@_assertion:Nn, \@@_b_test:, \@@_anchor:N} +% \begin{macro}{\@@_assertion:Nn, \@@_b_test:, \@@_A_test:, \@@_G_test:, \@@_Z_test:} % Usage: \cs{@@_assertion:Nn} \meta{boolean} \Arg{test}, where the % \meta{test} is either of the two other functions. Add a free % transition to a new state, conditionally to the assertion test. The % \cs{@@_b_test:} test is used by the |\b| and |\B| escape: check % if the last character was a word character or not, and do the same % to the current character. The boundary-markers of the string are -% non-word characters for this purpose. Anchors at the start or end -% of match use \cs{@@_anchor:N}, with a position controlled by the -% integer |#1|. +% non-word characters for this purpose. % \begin{macrocode} \cs_new_protected:Npn \@@_assertion:Nn #1#2 { @@ -4374,12 +4345,6 @@ \bool_if:NT #1 { { } } } } -\cs_new_protected:Npn \@@_anchor:N #1 - { - \if_int_compare:w #1 = \l_@@_curr_pos_int - \exp_after:wN \@@_break_true:w - \fi: - } \cs_new_protected:Npn \@@_b_test: { \group_begin: @@ -4389,6 +4354,24 @@ { \group_end: \@@_item_reverse:n \@@_prop_w: } { \group_end: \@@_prop_w: } } +\cs_new_protected:Npn \@@_Z_test: + { + \if_int_compare:w -2 = \l_@@_curr_char_int + \exp_after:wN \@@_break_true:w + \fi: + } +\cs_new_protected:Npn \@@_A_test: + { + \if_int_compare:w -2 = \l_@@_last_char_int + \exp_after:wN \@@_break_true:w + \fi: + } +\cs_new_protected:Npn \@@_G_test: + { + \if_int_compare:w \l_@@_curr_pos_int = \l_@@_start_pos_int + \exp_after:wN \@@_break_true:w + \fi: + } % \end{macrocode} % \end{macro} % @@ -4401,7 +4384,7 @@ \@@_build_new_state: \@@_toks_put_right:Nx \l_@@_left_state_int { - \@@_action_submatch:n { 0< } + \@@_action_submatch:nN { 0 } < \bool_set_true:N \l_@@_fresh_thread_bool \@@_action_free:n { @@ -4423,7 +4406,8 @@ % transitions, the instruction at the new state of the \textsc{nfa} is % performed immediately. When a transition consumes a character, the % new state is appended to a list of \enquote{active states}, stored in -% \cs{g_@@_thread_state_intarray}: this thread is made active again when the next +% \cs{g_@@_thread_info_intarray} (together with submatch information): +% this thread is made active again when the next % token is read from the query. At every step (for each token in the % query), we unpack that list of active states and the corresponding % submatch props, and empty those. @@ -4467,11 +4451,10 @@ % } % The tokens in the query are indexed from \texttt{min_pos} for the % first to $\texttt{max_pos}-1$ for the last, and their information is -% stored in several arrays and \tn{toks} registers with those numbers. We -% don't start from $0$ because the \tn{toks} registers with low -% numbers are used to hold the states of the \textsc{nfa}. We match +% stored in several arrays and \tn{toks} registers with those numbers. +% We match % without backtracking, keeping all threads in lockstep at the -% \texttt{current_pos} in the query. The starting point of the current +% \texttt{curr_pos} in the query. The starting point of the current % match attempt is \texttt{start_pos}, and \texttt{success_pos}, % updated whenever a thread succeeds, is used as the next starting % position. @@ -4488,20 +4471,26 @@ % { % \l_@@_curr_char_int, % \l_@@_curr_catcode_int, +% \l_@@_curr_token_tl, % \l_@@_last_char_int, +% \l_@@_last_char_success_int, % \l_@@_case_changed_char_int % } % The character and category codes of the token at the current -% position; the character code of the token at the previous position; +% position and a token list expanding to that token; the character +% code of the token at the previous position; +% the character code of the token just before a successful match; % and the character code of the result of changing the case of the % current token (|A-Z|$\leftrightarrow$|a-z|). This last integer is % only computed when necessary, and is otherwise \cs{c_max_int}. The -% \texttt{current_char} variable is also used in various other phases +% \texttt{curr_char} variable is also used in various other phases % to hold a character code. % \begin{macrocode} \int_new:N \l_@@_curr_char_int \int_new:N \l_@@_curr_catcode_int +\tl_new:N \l_@@_curr_token_tl \int_new:N \l_@@_last_char_int +\int_new:N \l_@@_last_char_success_int \int_new:N \l_@@_case_changed_char_int % \end{macrocode} % \end{variable} @@ -4517,18 +4506,17 @@ % \end{variable} % % \begin{variable} -% {\l_@@_curr_submatches_prop, \l_@@_success_submatches_prop} +% {\l_@@_curr_submatches_tl, \l_@@_success_submatches_tl} % The submatches for the thread which is currently active are stored -% in the \texttt{current_submatches} property list variable. This -% property list is stored by \cs{@@_action_cost:n} into the -% \tn{toks} register for the target state of the transition, to be -% retrieved when matching at the next position. When a thread -% succeeds, this property list is copied to -% \cs{l_@@_success_submatches_prop}: only the last successful thread +% in the \texttt{curr_submatches} list, which is almost a comma list, +% but ends with a comma. This list is stored by \cs{@@_store_state:n} +% into an intarray variable, to be retrieved when matching at the next +% position. When a thread succeeds, this list is copied to +% \cs{l_@@_success_submatches_tl}: only the last successful thread % remains there. % \begin{macrocode} -\prop_new:N \l_@@_curr_submatches_prop -\prop_new:N \l_@@_success_submatches_prop +\tl_new:N \l_@@_curr_submatches_tl +\tl_new:N \l_@@_success_submatches_tl % \end{macrocode} % \end{variable} % @@ -4551,28 +4539,44 @@ % \end{macrocode} % \end{variable} % -% \begin{variable}{\l_@@_min_active_int, \l_@@_max_active_int} +% \begin{variable}{\l_@@_min_thread_int, \l_@@_max_thread_int} % All the currently active threads are kept in order of precedence in -% \cs{g_@@_thread_state_intarray}, and the corresponding submatches in the -% \tn{toks}. For our purposes, those serve as an array, indexed from -% \texttt{min_active} (inclusive) to \texttt{max_active} (excluded). -% At the start of every step, the whole array is unpacked, so that the -% space can immediately be reused, and \texttt{max_active} is reset to -% \texttt{min_active}, effectively clearing the array. +% \cs{g_@@_thread_info_intarray} together with the corresponding +% submatch information. Data in this intarray is organized as blocks +% from \texttt{min_thread} (included) to \texttt{max_thread} +% (excluded). At the start of every step, the whole array is +% unpacked, so that the space can immediately be reused, and +% \texttt{max_thread} is reset to \texttt{min_thread}, effectively +% clearing the array. % \begin{macrocode} -\int_new:N \l_@@_min_active_int -\int_new:N \l_@@_max_active_int +\int_new:N \l_@@_min_thread_int +\int_new:N \l_@@_max_thread_int % \end{macrocode} % \end{variable} % -% \begin{variable}{\g_@@_state_active_intarray, \g_@@_thread_state_intarray} +% \begin{variable}{\g_@@_state_active_intarray, \g_@@_thread_info_intarray} % \cs{g_@@_state_active_intarray} stores the last \meta{step} in which -% each \meta{state} was active. \cs{g_@@_thread_state_intarray} stores +% each \meta{state} was active. \cs{g_@@_thread_info_intarray} stores % threads to be considered in the next step, more precisely the % states in which these threads are. % \begin{macrocode} \intarray_new:Nn \g_@@_state_active_intarray { 65536 } -\intarray_new:Nn \g_@@_thread_state_intarray { 65536 } +\intarray_new:Nn \g_@@_thread_info_intarray { 65536 } +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_matched_analysis_tl, \l_@@_curr_analysis_tl} +% The list \cs{l_@@_curr_analysis_tl} consists of a brace group +% containing three brace groups corresponding to the current token, +% with the same syntax as \cs{tl_analysis_map_inline:nn}. The list +% \cs{l_@@_matched_analysis_tl} (constructed under the +% \texttt{tl\_build} machinery) has one item for each token that has +% already been treated so far in a given match attempt: each item +% consists of three brace groups with the same syntax as +% \cs{tl_analysis_map_inline:nn}. +% \begin{macrocode} +\tl_new:N \l_@@_matched_analysis_tl +\tl_new:N \l_@@_curr_analysis_tl % \end{macrocode} % \end{variable} % @@ -4635,8 +4639,7 @@ % % \begin{macro}{\@@_match:n, \@@_match_cs:n} % \begin{macro}{\@@_match_init:} -% First store the query into \tn{toks} registers and arrays (see -% \cs{@@_query_set:nnn}). Then initialize the variables that should +% Initialize the variables that should % be set once for each user function (even for multiple % matches). Namely, the overall matching is not yet successful; none of % the states should be marked as visited (\cs{g_@@_state_active_intarray}), and @@ -4647,38 +4650,26 @@ % \begin{macrocode} \cs_new_protected:Npn \@@_match:n #1 { - \int_zero:N \l_@@_balance_int - \int_set:Nn \l_@@_curr_pos_int { 2 * \l_@@_max_state_int } - \@@_query_set:nnn { } { -1 } { -2 } - \int_set_eq:NN \l_@@_min_pos_int \l_@@_curr_pos_int - \tl_analysis_map_inline:nn {#1} - { \@@_query_set:nnn {##1} {"##3} {##2} } - \int_set_eq:NN \l_@@_max_pos_int \l_@@_curr_pos_int - \@@_query_set:nnn { } { -1 } { -2 } \@@_match_init: - \@@_match_once: + \@@_match_once_init: + \tl_analysis_map_inline:nn {#1} + { \@@_match_one_token:nnN {##1} {##2} ##3 } + \@@_match_one_token:nnN { } { -2 } F + \prg_break_point:Nn \@@_maplike_break: { } } \cs_new_protected:Npn \@@_match_cs:n #1 { - \int_zero:N \l_@@_balance_int - \int_set:Nn \l_@@_curr_pos_int - { - \int_max:nn { 2 * \l_@@_max_state_int - \l_@@_min_state_int } - { \l_@@_max_pos_int } - + 1 - } - \@@_query_set:nnn { } { -1 } { -2 } - \int_set_eq:NN \l_@@_min_pos_int \l_@@_curr_pos_int + \int_set_eq:NN \l_@@_min_thread_int \l_@@_max_thread_int + \@@_match_init: + \@@_match_once_init: \str_map_inline:nn {#1} { - \@@_query_set:nnn { \exp_not:n {##1} } - { \tl_if_blank:nTF {##1} { 10 } { 12 } } - { `##1 } + \tl_if_blank:nTF {##1} + { \@@_match_one_token:nnN {##1} {`##1} A } + { \@@_match_one_token:nnN {##1} {`##1} C } } - \int_set_eq:NN \l_@@_max_pos_int \l_@@_curr_pos_int - \@@_query_set:nnn { } { -1 } { -2 } - \@@_match_init: - \@@_match_once: + \@@_match_one_token:nnN { } { -2 } F + \prg_break_point:Nn \@@_maplike_break: { } } \cs_new_protected:Npn \@@_match_init: { @@ -4689,11 +4680,13 @@ \__kernel_intarray_gset:Nnn \g_@@_state_active_intarray {##1} { 1 } } - \int_set_eq:NN \l_@@_min_active_int \l_@@_max_state_int \int_zero:N \l_@@_step_int + \int_set:Nn \l_@@_min_pos_int { 2 } \int_set_eq:NN \l_@@_success_pos_int \l_@@_min_pos_int - \int_set:Nn \l_@@_min_submatch_int - { 2 * \l_@@_max_state_int } + \int_set:Nn \l_@@_last_char_success_int { -2 } + \tl_build_begin:N \l_@@_matched_analysis_tl + \tl_clear:N \l_@@_curr_analysis_tl + \int_set:Nn \l_@@_min_submatch_int { 1 } \int_set_eq:NN \l_@@_submatch_int \l_@@_min_submatch_int \bool_set_false:N \l_@@_empty_success_bool } @@ -4701,21 +4694,24 @@ % \end{macro} % \end{macro} % -% \begin{macro}{\@@_match_once:} -% This function finds one match, then does some action defined by the -% \texttt{every_match} token list, which may recursively call -% \cs{@@_match_once:}. First initialize some variables: set the +% \begin{macro}{\@@_match_once_init:} +% This function resets various variables used when finding one match. +% It is called before the loop through characters, and every time we +% find a match, before searching for another match (this is controlled +% by the \texttt{every_match} token list). +% +% First initialize some variables: set the % conditional which detects identical empty matches; this match % attempt starts at the previous \texttt{success_pos}, is not yet % successful, and has no submatches yet; clear the array of active % threads, and put the starting state $0$ in it. We are then almost % ready to read our first token in the query, but we actually start -% one position earlier than the start, and \texttt{get} that token, to -% set \texttt{last_char} properly for word -% boundaries. Then call \cs{@@_match_loop:}, which runs through the -% query until the end or until a successful match breaks early. +% one position earlier than the start because +% \cs{@@_match_one_token:nnN} increments \cs{l_@@_curr_pos_int} and +% saves \cs{l_@@_curr_char_int} as the \texttt{last_char} so that word +% boundaries can be correctly identified. % \begin{macrocode} -\cs_new_protected:Npn \@@_match_once: +\cs_new_protected:Npn \@@_match_once_init: { \if_meaning:w \c_true_bool \l_@@_empty_success_bool \cs_set:Npn \@@_if_two_empty_matches:F @@ -4728,14 +4724,24 @@ \fi: \int_set_eq:NN \l_@@_start_pos_int \l_@@_success_pos_int \bool_set_false:N \l_@@_match_success_bool - \prop_clear:N \l_@@_curr_submatches_prop - \int_set_eq:NN \l_@@_max_active_int \l_@@_min_active_int + \tl_set:Nx \l_@@_curr_submatches_tl + { \prg_replicate:nn { 2 * \l_@@_capturing_group_int } { 0 , } } + \int_set_eq:NN \l_@@_max_thread_int \l_@@_min_thread_int \@@_store_state:n { \l_@@_min_state_int } \int_set:Nn \l_@@_curr_pos_int { \l_@@_start_pos_int - 1 } - \@@_query_get: - \@@_match_loop: - \l_@@_every_match_tl + \int_set_eq:NN \l_@@_curr_char_int \l_@@_last_char_success_int + \tl_build_get:NN \l_@@_matched_analysis_tl \l_@@_internal_a_tl + \exp_args:NNf \@@_match_once_init_aux: + \tl_map_inline:nn + { \exp_after:wN \l_@@_internal_a_tl \l_@@_curr_analysis_tl } + { \@@_match_one_token:nnN ##1 } + \prg_break_point:Nn \@@_maplike_break: { } + } +\cs_new_protected:Npn \@@_match_once_init_aux: + { + \tl_build_clear:N \l_@@_matched_analysis_tl + \tl_clear:N \l_@@_curr_analysis_tl } % \end{macrocode} % \end{macro} @@ -4753,110 +4759,80 @@ \bool_gset_eq:NN \g_@@_success_bool \l_@@_match_success_bool + \@@_maplike_break: } } \cs_new_protected:Npn \@@_multi_match:n #1 { \tl_set:Nn \l_@@_every_match_tl { - \if_meaning:w \c_true_bool \l_@@_match_success_bool - \bool_gset_true:N \g_@@_success_bool - #1 - \exp_after:wN \@@_match_once: + \if_meaning:w \c_false_bool \l_@@_match_success_bool + \exp_after:wN \@@_maplike_break: \fi: + \bool_gset_true:N \g_@@_success_bool + #1 + \@@_match_once_init: } } % \end{macrocode} % \end{macro} % -% \begin{macro}{\@@_match_loop:} +% \begin{macro}{\@@_match_one_token:nnN} % \begin{macro}[rEXP]{\@@_match_one_active:n} % At each new position, set some variables and get the new character % and category from the query. Then unpack the array of active % threads, and clear it by resetting its length -% (\texttt{max_active}). This results in a sequence of -% \cs{@@_use_state_and_submatches:nn} \Arg{state} \Arg{prop}, and +% (\texttt{max_thread}). This results in a sequence of +% \cs{@@_use_state_and_submatches:w} \meta{state}|,|\meta{submatch-clist}|;| and % we consider those states one by one in order. As soon as a thread % succeeds, exit the step, and, if there are threads to consider at the % next position, and we have not reached the end of the string, -% repeat the loop. Otherwise, the last thread that succeeded is what -% \cs{@@_match_once:} matches. We explain the \texttt{fresh_thread} -% business when describing \cs{@@_action_wildcard:}. +% repeat the loop. Otherwise, the last thread that succeeded is the +% match. We explain the \texttt{fresh_thread} business when +% describing \cs{@@_action_wildcard:}. % \begin{macrocode} -\cs_new_protected:Npn \@@_match_loop: +\cs_new_protected:Npn \@@_match_one_token:nnN #1#2#3 { \int_add:Nn \l_@@_step_int { 2 } \int_incr:N \l_@@_curr_pos_int \int_set_eq:NN \l_@@_last_char_int \l_@@_curr_char_int \int_set_eq:NN \l_@@_case_changed_char_int \c_max_int - \@@_query_get: + \tl_set:Nn \l_@@_curr_token_tl {#1} + \int_set:Nn \l_@@_curr_char_int {#2} + \int_set:Nn \l_@@_curr_catcode_int { "#3 } + \tl_build_put_right:Nx \l_@@_matched_analysis_tl + { \exp_not:o \l_@@_curr_analysis_tl } + \tl_set:Nn \l_@@_curr_analysis_tl { { {#1} {#2} #3 } } \use:x { - \int_set_eq:NN \l_@@_max_active_int \l_@@_min_active_int + \int_set_eq:NN \l_@@_max_thread_int \l_@@_min_thread_int \int_step_function:nnN - { \l_@@_min_active_int } - { \l_@@_max_active_int - 1 } + { \l_@@_min_thread_int } + { \l_@@_max_thread_int - 1 } \@@_match_one_active:n } \prg_break_point: \bool_set_false:N \l_@@_fresh_thread_bool - \if_int_compare:w \l_@@_max_active_int > \l_@@_min_active_int - \if_int_compare:w \l_@@_curr_pos_int < \l_@@_max_pos_int - \exp_after:wN \exp_after:wN \exp_after:wN \@@_match_loop: + \if_int_compare:w \l_@@_max_thread_int > \l_@@_min_thread_int + \if_int_compare:w -2 < \l_@@_curr_char_int + \exp_after:wN \exp_after:wN \exp_after:wN \use_none:n \fi: \fi: + \l_@@_every_match_tl } \cs_new:Npn \@@_match_one_active:n #1 { - \@@_use_state_and_submatches:nn - { \__kernel_intarray_item:Nn \g_@@_thread_state_intarray {#1} } - { \@@_toks_use:w #1 } + \@@_use_state_and_submatches:w + \__kernel_intarray_range_to_clist:Nnn + \g_@@_thread_info_intarray + { 1 + #1 * (\l_@@_capturing_group_int * 2 + 1) } + { (1 + #1) * (\l_@@_capturing_group_int * 2 + 1) } + ; } % \end{macrocode} % \end{macro} % \end{macro} % -% \begin{macro}{\@@_query_set:nnn} -% The arguments are: tokens that \texttt{o} and \texttt{x} expand to -% one token of the query, the catcode, and the character code. Store -% those, and the current brace balance (used later to check for -% overall brace balance) in a \tn{toks} register and some arrays, -% then update the \texttt{balance}. -% \begin{macrocode} -\cs_new_protected:Npn \@@_query_set:nnn #1#2#3 - { - \__kernel_intarray_gset:Nnn \g_@@_charcode_intarray - { \l_@@_curr_pos_int } {#3} - \__kernel_intarray_gset:Nnn \g_@@_catcode_intarray - { \l_@@_curr_pos_int } {#2} - \__kernel_intarray_gset:Nnn \g_@@_balance_intarray - { \l_@@_curr_pos_int } { \l_@@_balance_int } - \@@_toks_set:Nn \l_@@_curr_pos_int {#1} - \int_incr:N \l_@@_curr_pos_int - \if_case:w #2 \exp_stop_f: - \or: \int_incr:N \l_@@_balance_int - \or: \int_decr:N \l_@@_balance_int - \fi: - } -% \end{macrocode} -% \end{macro} -% -% \begin{macro}{\@@_query_get:} -% Extract the current character and category codes at the current -% position from the appropriate arrays. -% \begin{macrocode} -\cs_new_protected:Npn \@@_query_get: - { - \l_@@_curr_char_int - = \__kernel_intarray_item:Nn \g_@@_charcode_intarray - { \l_@@_curr_pos_int } \scan_stop: - \l_@@_curr_catcode_int - = \__kernel_intarray_item:Nn \g_@@_catcode_intarray - { \l_@@_curr_pos_int } \scan_stop: - } -% \end{macrocode} -% \end{macro} -% % \subsubsection{Using states of the \textsc{nfa}} % % \begin{macro}{\@@_use_state:} @@ -4879,20 +4855,20 @@ % \end{macrocode} % \end{macro} % -% \begin{macro}{\@@_use_state_and_submatches:nn} +% \begin{macro}{\@@_use_state_and_submatches:w} % This function is called as one item in the array of active threads % after that array has been unpacked for a new step. Update the -% \texttt{current_state} and \texttt{current_submatches} and use the +% \texttt{curr_state} and \texttt{curr_submatches} and use the % state if it has not yet been encountered at this step. % \begin{macrocode} -\cs_new_protected:Npn \@@_use_state_and_submatches:nn #1 #2 +\cs_new_protected:Npn \@@_use_state_and_submatches:w #1 , #2 ; { \int_set:Nn \l_@@_curr_state_int {#1} \if_int_compare:w \__kernel_intarray_item:Nn \g_@@_state_active_intarray { \l_@@_curr_state_int } < \l_@@_step_int - \tl_set:Nn \l_@@_curr_submatches_prop {#2} + \tl_set:Nn \l_@@_curr_submatches_tl { #2 , } \exp_after:wN \@@_use_state: \fi: \scan_stop: @@ -4902,20 +4878,20 @@ % % \subsubsection{Actions when matching} % -% \begin{macro}{\@@_action_start_wildcard:} +% \begin{macro}{\@@_action_start_wildcard:N} % For an unanchored match, state $0$ has a free transition to the next % and a costly one to itself, to repeat at the next position. To catch % repeated identical empty matches, we need to know if a successful % thread corresponds to an empty match. The instruction resetting % \cs{l_@@_fresh_thread_bool} may be skipped by a successful -% thread, hence we had to add it to \cs{@@_match_loop:} too. +% thread, hence we had to add it to \cs{@@_match_one_token:nnN} too. % \begin{macrocode} -\cs_new_protected:Npn \@@_action_start_wildcard: +\cs_new_protected:Npn \@@_action_start_wildcard:N #1 { \bool_set_true:N \l_@@_fresh_thread_bool \@@_action_free:n {1} \bool_set_false:N \l_@@_fresh_thread_bool - \@@_action_cost:n {0} + \bool_if:NT #1 { \@@_action_cost:n {0} } } % \end{macrocode} % \end{macro} @@ -4954,8 +4930,8 @@ } \int_set:Nn \l_@@_curr_state_int { \int_use:N \l_@@_curr_state_int } - \tl_set:Nn \exp_not:N \l_@@_curr_submatches_prop - { \exp_not:o \l_@@_curr_submatches_prop } + \tl_set:Nn \exp_not:N \l_@@_curr_submatches_tl + { \exp_not:o \l_@@_curr_submatches_tl } } } % \end{macrocode} @@ -4978,21 +4954,26 @@ % % \begin{macro}{\@@_store_state:n} % \begin{macro}{\@@_store_submatches:} -% Put the given state in \cs{g_@@_thread_state_intarray}, and increment -% the length of the array. Also store the current submatch in the -% appropriate \tn{toks}. +% Put the given state and current submatch information in +% \cs{g_@@_thread_info_intarray}, and increment the length of the +% array. % \begin{macrocode} \cs_new_protected:Npn \@@_store_state:n #1 { - \@@_store_submatches: - \__kernel_intarray_gset:Nnn \g_@@_thread_state_intarray - { \l_@@_max_active_int } {#1} - \int_incr:N \l_@@_max_active_int + \exp_args:No \@@_store_submatches:nn + \l_@@_curr_submatches_tl {#1} + \int_incr:N \l_@@_max_thread_int } -\cs_new_protected:Npn \@@_store_submatches: +\cs_new_protected:Npn \@@_store_submatches:nn #1#2 { - \@@_toks_set:No \l_@@_max_active_int - { \l_@@_curr_submatches_prop } + \__kernel_intarray_gset_range_from_clist:Nnn + \g_@@_thread_info_intarray + { + \@@_int_eval:w + 1 + \l_@@_max_thread_int * + (\l_@@_capturing_group_int * 2 + 1) + } + { #2 , #1 } } % \end{macrocode} % \end{macro} @@ -5006,21 +4987,37 @@ % \begin{macrocode} \cs_new_protected:Npn \@@_disable_submatches: { - \cs_set_protected:Npn \@@_store_submatches: { } - \cs_set_protected:Npn \@@_action_submatch:n ##1 { } + \cs_set_protected:Npn \@@_store_submatches:n ##1 { } + \cs_set_protected:Npn \@@_action_submatch:nN ##1##2 { } } % \end{macrocode} % \end{macro} % -% \begin{macro}{\@@_action_submatch:n} +% \begin{macro}{\@@_action_submatch:nN, \@@_action_submatch_aux:w, \@@_action_submatch_auxii:w, \@@_action_submatch_auxiii:w, \@@_action_submatch_auxiv:w} % Update the current submatches with the information from the current % position. Maybe a bottleneck. % \begin{macrocode} -\cs_new_protected:Npn \@@_action_submatch:n #1 +\cs_new_protected:Npn \@@_action_submatch:nN #1#2 + { + \exp_after:wN \@@_action_submatch_aux:w + \l_@@_curr_submatches_tl ; {#1} #2 + } +\cs_new_protected:Npn \@@_action_submatch_aux:w #1 ; #2#3 { - \prop_put:Nno \l_@@_curr_submatches_prop {#1} - { \int_use:N \l_@@_curr_pos_int } + \tl_set:Nx \l_@@_curr_submatches_tl + { + \prg_replicate:nn + { #2 \if_meaning:w > #3 + \l_@@_capturing_group_int \fi: } + { \@@_action_submatch_auxii:w } + \@@_action_submatch_auxiii:w + #1 + } } +\cs_new:Npn \@@_action_submatch_auxii:w + #1 \@@_action_submatch_auxiii:w #2 , + { #2 , #1 \@@_action_submatch_auxiii:w } +\cs_new:Npn \@@_action_submatch_auxiii:w #1 , + { \int_use:N \l_@@_curr_pos_int , } % \end{macrocode} % \end{macro} % @@ -5042,8 +5039,10 @@ \bool_set_eq:NN \l_@@_empty_success_bool \l_@@_fresh_thread_bool \int_set_eq:NN \l_@@_success_pos_int \l_@@_curr_pos_int - \prop_set_eq:NN \l_@@_success_submatches_prop - \l_@@_curr_submatches_prop + \int_set_eq:NN \l_@@_last_char_success_int \l_@@_last_char_int + \tl_build_clear:N \l_@@_matched_analysis_tl + \tl_set_eq:NN \l_@@_success_submatches_tl + \l_@@_curr_submatches_tl \prg_break: } } @@ -5134,6 +5133,14 @@ % \end{macrocode} % \end{macro} % +% \begin{macro}{\@@_replacement_exp_not:V} +% This is used for the implementation of~|\u|, and it gets redefined +% for \cs{peek_regex_replace_once:nnTF}. +% \begin{macrocode} +\cs_new_eq:NN \@@_replacement_exp_not:V \exp_not:V +% \end{macrocode} +% \end{macro} +% % \subsubsection{Query and brace balance} % % \begin{macro}[rEXP]{\@@_query_range:nn} @@ -5189,42 +5196,25 @@ % range is the difference between the brace balances at the % \meta{max~pos} and \meta{min~pos}. These two positions are found in % the corresponding \enquote{submatch} arrays. -%^^A todo: understand when these int_compare are needed % \begin{macrocode} \cs_new_protected:Npn \@@_submatch_balance:n #1 { \int_eval:n - { - \int_compare:nNnTF - { - \__kernel_intarray_item:Nn - \g_@@_submatch_end_intarray {#1} - } - = 0 - { 0 } - { - \__kernel_intarray_item:Nn \g_@@_balance_intarray - { - \__kernel_intarray_item:Nn - \g_@@_submatch_end_intarray {#1} - } - } - - - \int_compare:nNnTF - { - \__kernel_intarray_item:Nn - \g_@@_submatch_begin_intarray {#1} - } - = 0 - { 0 } - { - \__kernel_intarray_item:Nn \g_@@_balance_intarray - { - \__kernel_intarray_item:Nn - \g_@@_submatch_begin_intarray {#1} - } - } - } + { + \@@_intarray_item:NnF \g_@@_balance_intarray + { + \__kernel_intarray_item:Nn + \g_@@_submatch_end_intarray {#1} + } + { 0 } + - + \@@_intarray_item:NnF \g_@@_balance_intarray + { + \__kernel_intarray_item:Nn + \g_@@_submatch_begin_intarray {#1} + } + { 0 } + } } % \end{macrocode} % \end{macro} @@ -5305,6 +5295,14 @@ % \end{macro} % \end{macro} % +% \begin{macro}{\@@_replacement_put:n} +% This gets redefined for \cs{peek_regex_replace_once:nnTF}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_replacement_put:n + { \tl_build_put_right:Nn \l_@@_build_tl } +% \end{macrocode} +% \end{macro} +% % \begin{macro}{\@@_replacement_normal:n} % Most characters are simply sent to the output by % \cs{tl_build_put_right:Nn}, unless a particular category code has been @@ -5314,11 +5312,13 @@ % sequence is non-empty there: it contains an empty entry % corresponding to the initial value of % \cs{l_@@_replacement_category_tl}. +% The argument |#1| can be a space, otherwise it is a single +% character. % \begin{macrocode} \cs_new_protected:Npn \@@_replacement_normal:n #1 { \tl_if_empty:NTF \l_@@_replacement_category_tl - { \tl_build_put_right:Nn \l_@@_build_tl {#1} } + { \@@_replacement_put:n {#1} } { % ( \token_if_eq_charcode:NNTF #1 ) { @@ -5361,7 +5361,7 @@ % % \subsubsection{Submatches} % -% \begin{macro}{\@@_replacement_put_submatch:n} +% \begin{macro}{\@@_replacement_put_submatch:n, \@@_replacement_put_submatch_aux:n} % Insert a submatch in the replacement text. This is dropped if the % submatch number is larger than the number of capturing groups. % Unless the submatch appears inside a |\c{...}| or |\u{...}| @@ -5374,15 +5374,19 @@ \cs_new_protected:Npn \@@_replacement_put_submatch:n #1 { \if_int_compare:w #1 < \l_@@_capturing_group_int - \tl_build_put_right:Nn \l_@@_build_tl - { \@@_query_submatch:n { \int_eval:n { #1 + ##1 } } } - \if_int_compare:w \l_@@_replacement_csnames_int = 0 \exp_stop_f: - \tl_put_right:Nn \l_@@_balance_tl - { - + \@@_submatch_balance:n - { \exp_not:N \int_eval:n { #1 + ##1 } } - } - \fi: + \@@_replacement_put_submatch_aux:n {#1} + \fi: + } +\cs_new_protected:Npn \@@_replacement_put_submatch_aux:n #1 + { + \tl_build_put_right:Nn \l_@@_build_tl + { \@@_query_submatch:n { \int_eval:n { #1 + ##1 } } } + \if_int_compare:w \l_@@_replacement_csnames_int = 0 \exp_stop_f: + \tl_put_right:Nn \l_@@_balance_tl + { + + \@@_submatch_balance:n + { \exp_not:N \int_eval:n { #1 + ##1 } } + } \fi: } % \end{macrocode} @@ -5488,7 +5492,7 @@ { \@@_two_if_eq:NNNNTF #1 #2 \@@_replacement_normal:n \c_left_brace_str - { \@@_replacement_cu_aux:Nw \exp_not:V } + { \@@_replacement_cu_aux:Nw \@@_replacement_exp_not:V } { \@@_replacement_error:NNN u #1#2 } } % \end{macrocode} @@ -5579,7 +5583,7 @@ \cs_new_protected:Npn \@@_replacement_char:nNN #1#2#3 { \tex_lccode:D 0 = `#3 \scan_stop: - \tex_lowercase:D { \tl_build_put_right:Nn \l_@@_build_tl {#1} } + \tex_lowercase:D { \@@_replacement_put:n {#1} } } % \end{macrocode} % \end{macro} @@ -5625,7 +5629,7 @@ \cs_new_protected:Npn \@@_replacement_c_C:w #1#2 { \tl_build_put_right:Nn \l_@@_build_tl - { \exp_not:N \exp_not:N \exp_not:c {#2} } + { \exp_not:N \@@_replacement_exp_not:N \exp_not:c {#2} } } % \end{macrocode} % \end{macro} @@ -5712,7 +5716,7 @@ \__kernel_msg_error:nn { kernel } { replacement-null-space } \fi: \tex_lccode:D `\ = `#2 \scan_stop: - \tex_lowercase:D { \tl_build_put_right:Nn \l_@@_build_tl {~} } + \tex_lowercase:D { \@@_replacement_put:n {~} } } % \end{macrocode} % \end{macro} @@ -5940,6 +5944,14 @@ % \end{macrocode} % \end{variable} % +% \begin{variable}{\g_@@_balance_intarray} +% The first thing we do when matching is to store the balance of +% begin-group/end-group characters into \cs{g_@@_balance_intarray}. +% \begin{macrocode} +\intarray_new:Nn \g_@@_balance_intarray { 65536 } +% \end{macrocode} +% \end{variable} +% % \begin{macro}{\@@_return:} % This function triggers either \cs{prg_return_false:} or % \cs{prg_return_true:} as appropriate to whether a match was found or @@ -5956,6 +5968,36 @@ % \end{macrocode} % \end{macro} % +% \begin{macro}{\@@_query_set:n, \@@_query_set_aux:nN} +% To easily extract subsets of the input once we found the positions +% at which to cut, store the input tokens one by one into successive +% \tn{toks} registers. Also store the brace balance (used to check +% for overall brace balance) in an array. +% \begin{macrocode} +\cs_new_protected:Npn \@@_query_set:n #1 + { + \int_zero:N \l_@@_balance_int + \int_zero:N \l_@@_curr_pos_int + \@@_query_set_aux:nN { } F + \tl_analysis_map_inline:nn {#1} + { \@@_query_set_aux:nN {##1} ##3 } + \@@_query_set_aux:nN { } F + \int_set_eq:NN \l_@@_max_pos_int \l_@@_curr_pos_int + } +\cs_new_protected:Npn \@@_query_set_aux:nN #1#2 + { + \int_incr:N \l_@@_curr_pos_int + \@@_toks_set:Nn \l_@@_curr_pos_int {#1} + \__kernel_intarray_gset:Nnn \g_@@_balance_intarray + { \l_@@_curr_pos_int } { \l_@@_balance_int } + \if_case:w "#2 \exp_stop_f: + \or: \int_incr:N \l_@@_balance_int + \or: \int_decr:N \l_@@_balance_int + \fi: + } +% \end{macrocode} +% \end{macro} +% % \subsubsection{Matching} % % \begin{macro}{\@@_if_match:nn} @@ -6011,6 +6053,7 @@ #1 \@@_match:n {#2} \@@_extract: + \@@_query_set:n {#2} \@@_group_end_extract_seq:N #3 } \cs_new_protected:Npn \@@_extract_all:nnN #1#2#3 @@ -6019,6 +6062,7 @@ \@@_multi_match:n { \@@_extract: } #1 \@@_match:n {#2} + \@@_query_set:n {#2} \@@_group_end_extract_seq:N #3 } % \end{macrocode} @@ -6058,7 +6102,7 @@ } #1 \@@_match:n {#2} -%<assert>\assert_int:n { \l_@@_curr_pos_int = \l_@@_max_pos_int } + \@@_query_set:n {#2} \__kernel_intarray_gset:Nnn \g_@@_submatch_prev_intarray { \l_@@_submatch_int } { 0 } \__kernel_intarray_gset:Nnn \g_@@_submatch_end_intarray @@ -6143,18 +6187,15 @@ % \end{macrocode} % \end{macro} % -% \begin{macro} -% {\@@_extract:, \@@_extract_b:wn, \@@_extract_e:wn} -% Our task here is to extract from the property list -% \cs{l_@@_success_submatches_prop} the list of end-points of -% submatches, and store them in appropriate array entries, from -% \cs{l_@@_zeroth_submatch_int} upwards. We begin by emptying those -% entries. Then for each \meta{key}--\meta{value} pair in -% the property list update the appropriate entry. This -% is somewhat a hack: the \meta{key} is a non-negative integer -% followed by |<| or |>|, which we use in a comparison to $-1$. At the -% end, store the information about the position at which the match -% attempt started, in \cs{g_@@_submatch_prev_intarray}. +% \begin{macro}{\@@_extract:} +% Our task here is to store the list of end-points of submatches, and +% store them in appropriate array entries, from +% \cs{l_@@_zeroth_submatch_int} upwards. First, we store in +% \cs{g_@@_submatch_prev_intarray} the position at which the match +% attempt started. We extract the rest from the comma list +% \cs{l_@@_success_submatches_tl}, which starts with entries to be +% stored in \cs{g_@@_submatch_begin_intarray} and continues with +% entries for \cs{g_@@_submatch_end_intarray}. % \begin{macrocode} \cs_new_protected:Npn \@@_extract: { @@ -6162,34 +6203,26 @@ \int_set_eq:NN \l_@@_zeroth_submatch_int \l_@@_submatch_int \prg_replicate:nn \l_@@_capturing_group_int { - \__kernel_intarray_gset:Nnn \g_@@_submatch_begin_intarray - { \l_@@_submatch_int } { 0 } - \__kernel_intarray_gset:Nnn \g_@@_submatch_end_intarray - { \l_@@_submatch_int } { 0 } \__kernel_intarray_gset:Nnn \g_@@_submatch_prev_intarray { \l_@@_submatch_int } { 0 } \int_incr:N \l_@@_submatch_int } - \prop_map_inline:Nn \l_@@_success_submatches_prop + \__kernel_intarray_gset:Nnn \g_@@_submatch_prev_intarray + { \l_@@_zeroth_submatch_int } { \l_@@_start_pos_int } + \int_zero:N \l_@@_internal_a_int + \clist_map_inline:Nn \l_@@_success_submatches_tl { - \if_int_compare:w ##1 - 1 \exp_stop_f: - \exp_after:wN \@@_extract_e:wn \int_value:w + \if_int_compare:w \l_@@_internal_a_int < \l_@@_capturing_group_int + \__kernel_intarray_gset:Nnn \g_@@_submatch_begin_intarray + { \@@_int_eval:w \l_@@_zeroth_submatch_int + \l_@@_internal_a_int } {##1} \else: - \exp_after:wN \@@_extract_b:wn \int_value:w + \__kernel_intarray_gset:Nnn \g_@@_submatch_end_intarray + { \@@_int_eval:w \l_@@_zeroth_submatch_int + \l_@@_internal_a_int - \l_@@_capturing_group_int } {##1} \fi: - \@@_int_eval:w \l_@@_zeroth_submatch_int + ##1 {##2} + \int_incr:N \l_@@_internal_a_int } - \__kernel_intarray_gset:Nnn \g_@@_submatch_prev_intarray - { \l_@@_zeroth_submatch_int } { \l_@@_start_pos_int } \fi: } -\cs_new_protected:Npn \@@_extract_b:wn #1 < #2 - { - \__kernel_intarray_gset:Nnn - \g_@@_submatch_begin_intarray {#1} {#2} - } -\cs_new_protected:Npn \@@_extract_e:wn #1 > #2 - { \__kernel_intarray_gset:Nnn \g_@@_submatch_end_intarray {#1} {#2} } % \end{macrocode} % \end{macro} % @@ -6214,12 +6247,13 @@ \group_begin: \@@_single_match: #1 - \@@_replacement:n {#2} - \exp_args:No \@@_match:n { #3 } + \exp_args:No \@@_match:n {#3} \if_meaning:w \c_false_bool \g_@@_success_bool \group_end: \else: \@@_extract: + \exp_args:No \@@_query_set:n {#3} + \@@_replacement:n {#2} \int_set:Nn \l_@@_balance_int { \@@_replacement_balance_one_match:n @@ -6259,8 +6293,9 @@ \group_begin: \@@_multi_match:n { \@@_extract: } #1 - \@@_replacement:n {#2} \exp_args:No \@@_match:n {#3} + \exp_args:No \@@_query_set:n {#3} + \@@_replacement:n {#2} \int_set:Nn \l_@@_balance_int { 0 @@ -6320,7 +6355,371 @@ % \end{macrocode} % \end{macro} % -% \subsubsection{Storing and showing compiled patterns} +% \subsubsection{Peeking ahead} +% +% \begin{variable}{\l_@@_peek_true_tl, \l_@@_peek_false_tl} +% True/false code arguments of \cs{peek_regex:nTF} or similar. +% \begin{macrocode} +\tl_new:N \l_@@_peek_true_tl +\tl_new:N \l_@@_peek_false_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_replacement_tl} +% When peeking in \cs{peek_regex_replace_once:nnTF} we need to store +% the replacement text. +% \begin{macrocode} +\tl_new:N \l_@@_replacement_tl +% \end{macrocode} +% \end{variable} +% +% \begin{variable}{\l_@@_input_tl} +% \begin{macro}{\@@_input_item:n} +% Stores each token found as \cs{@@_input_item:n} \Arg{tokens}, where +% the \meta{tokens} \texttt{o}-expand to the token found, as for +% \cs{tl_analysis_map_inline:nn}. +% \begin{macrocode} +\tl_new:N \l_@@_input_tl +\cs_new_eq:NN \@@_input_item:n ? +% \end{macrocode} +% \end{macro} +% \end{variable} +% +% \begin{macro}[TF] +% {\peek_regex:n, \peek_regex:N, \peek_regex_remove_once:n, \peek_regex_remove_once:N} +% The |T| and |F| functions just call the corresponding |TF| function. +% The four |TF| functions differ along two axes: whether to remove the +% token or not, distinguished by using \cs{@@_peek_end:} or +% \cs{@@_peek_remove_end:n} (the latter case needs an argument, as we +% will see), and whether the regex has to be compiled or is already in +% an |N|-type variable, distinguished by calling \cs{@@_build_aux:Nn} +% or \cs{@@_build_aux:NN}. The first argument of these functions is +% \cs{c_false_bool} to indicate that there should be no implicit +% insertion of a wildcard at the start of the pattern: otherwise the +% code would keep looking further into the input stream until matching +% the regex. +% \begin{macrocode} +\cs_new_protected:Npn \peek_regex:nTF #1 + { + \@@_peek:nnTF + { \@@_build_aux:Nn \c_false_bool {#1} } + { \@@_peek_end: } + } +\cs_new_protected:Npn \peek_regex:nT #1#2 + { \peek_regex:nTF {#1} {#2} { } } +\cs_new_protected:Npn \peek_regex:nF #1 { \peek_regex:nTF {#1} { } } +\cs_new_protected:Npn \peek_regex:NTF #1 + { + \@@_peek:nnTF + { \@@_build_aux:NN \c_false_bool #1 } + { \@@_peek_end: } + } +\cs_new_protected:Npn \peek_regex:NT #1#2 + { \peek_regex:NTF #1 {#2} { } } +\cs_new_protected:Npn \peek_regex:NF #1 { \peek_regex:NTF {#1} { } } +\cs_new_protected:Npn \peek_regex_remove_once:nTF #1 + { + \@@_peek:nnTF + { \@@_build_aux:Nn \c_false_bool {#1} } + { \@@_peek_remove_end:n {##1} } + } +\cs_new_protected:Npn \peek_regex_remove_once:nT #1#2 + { \peek_regex_remove_once:nTF {#1} {#2} { } } +\cs_new_protected:Npn \peek_regex_remove_once:nF #1 + { \peek_regex_remove_once:nTF {#1} { } } +\cs_new_protected:Npn \peek_regex_remove_once:NTF #1 + { + \@@_peek:nnTF + { \@@_build_aux:NN \c_false_bool #1 } + { \@@_peek_remove_end:n {##1} } + } +\cs_new_protected:Npn \peek_regex_remove_once:NT #1#2 + { \peek_regex_remove_once:NTF #1 {#2} { } } +\cs_new_protected:Npn \peek_regex_remove_once:NF #1 + { \peek_regex_remove_once:NTF #1 { } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_peek:nnTF, \@@_peek_aux:nnTF} +% Store the user's true/false codes (plus \cs{group_end:}) into two +% token lists. Then build the automaton with |#1|, without submatch +% tracking, and aiming for a single match. Then start matching by +% setting up a few variables like for any regex matching like +% \cs{regex_match:nnTF}, with the addition of \cs{l_@@_input_tl} +% that keeps track of the tokens seen, to reinsert them at the +% end. Instead of \cs{tl_analysis_map_inline:nn} on the input, we +% call \cs{peek_analysis_map_inline:n} to go through tokens in the +% input stream. Since \cs{@@_match_one_token:nnN} calls +% \cs{@@_maplike_break:} we need to catch that and break the +% \cs{peek_analysis_map_inline:n} loop instead. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek:nnTF #1 + { + \@@_peek_aux:nnTF + { + \@@_disable_submatches: + #1 + } + } +\cs_new_protected:Npn \@@_peek_aux:nnTF #1#2#3#4 + { + \group_begin: + \tl_set:Nn \l_@@_peek_true_tl { \group_end: #3 } + \tl_set:Nn \l_@@_peek_false_tl { \group_end: #4 } + \@@_single_match: + #1 + \@@_match_init: + \tl_build_clear:N \l_@@_input_tl + \@@_match_once_init: + \peek_analysis_map_inline:n + { + \tl_build_put_right:Nn \l_@@_input_tl + { \@@_input_item:n {##1} } + \@@_match_one_token:nnN {##1} {##2} ##3 + \use_none:nnn + \prg_break_point:Nn \@@_maplike_break: + { \peek_analysis_map_break:n {#2} } + } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_peek_end:, \@@_peek_remove_end:n} +% Once the regex matches (or permanently fails to match) we call +% \cs{@@_peek_end:}, or \cs{@@_peek_remove_end:n} with argument the +% last token seen (or rather tokens that \texttt{o}-expand and +% \texttt{x}-expand to it). For \cs{peek_regex:nTF} we reinsert +% tokens seen by calling \cs{@@_peek_reinsert:N} regardless of the +% result of the match. For \cs{peek_regex_remove_once:nTF} we reinsert the +% tokens seen only if the match failed; otherwise we just reinsert the +% tokens~|#1|, with one expansion. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_end: + { + \bool_if:NTF \g_@@_success_bool + { \@@_peek_reinsert:N \l_@@_peek_true_tl } + { \@@_peek_reinsert:N \l_@@_peek_false_tl } + } +\cs_new_protected:Npn \@@_peek_remove_end:n #1 + { + \bool_if:NTF \g_@@_success_bool + { \exp_after:wN \l_@@_peek_true_tl #1 } + { \@@_peek_reinsert:N \l_@@_peek_false_tl } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_peek_reinsert:N, \@@_reinsert_item:n} +% Insert the true/false code |#1|, followed by the tokens found, which +% were stored in \cs{l_@@_input_tl}. For this, loop through that +% token list using \cs{@@_reinsert_item:n}, which expands |#1| once to +% get a single token, and jumps over it to expand what follows, with +% suitable \cs{exp:w} and \cs{exp_end:}. We cannot just use +% \cs{use:e} on the whole token list because the result may be +% unbalanced, which would stop the primitive prematurely, or let it +% continue beyond where we would like. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_reinsert:N #1 + { + \tl_build_end:N \l_@@_input_tl + \cs_set_eq:NN \@@_input_item:n \@@_reinsert_item:n + \exp_after:wN #1 \exp:w \l_@@_input_tl \exp_end: + } +\cs_new_protected:Npn \@@_reinsert_item:n #1 + { + \exp_after:wN \exp_after:wN + \exp_after:wN \exp_end: + \exp_after:wN \exp_after:wN + #1 + \exp:w + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}[noTF] +% {\peek_regex_replace_once:nn, \peek_regex_replace_once:Nn} +% Similar to \cs{peek_regex:nTF} above. +% \begin{macrocode} +\cs_new_protected:Npn \peek_regex_replace_once:nnTF #1 + { \@@_peek_replace:nnTF { \@@_build_aux:Nn \c_false_bool {#1} } } +\cs_new_protected:Npn \peek_regex_replace_once:nnT #1#2#3 + { \peek_regex_replace_once:nnTF {#1} {#2} {#3} { } } +\cs_new_protected:Npn \peek_regex_replace_once:nnF #1#2 + { \peek_regex_replace_once:nnTF {#1} {#2} { } } +\cs_new_protected:Npn \peek_regex_replace_once:nn #1#2 + { \peek_regex_replace_once:nnTF {#1} {#2} { } { } } +\cs_new_protected:Npn \peek_regex_replace_once:NnTF #1 + { \@@_peek_replace:nnTF { \@@_build_aux:NN \c_false_bool #1 } } +\cs_new_protected:Npn \peek_regex_replace_once:NnT #1#2#3 + { \peek_regex_replace_once:NnTF #1 {#2} {#3} { } } +\cs_new_protected:Npn \peek_regex_replace_once:NnF #1#2 + { \peek_regex_replace_once:NnTF #1 {#2} { } } +\cs_new_protected:Npn \peek_regex_replace_once:Nn #1#2 + { \peek_regex_replace_once:NnTF #1 {#2} { } { } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_peek_replace:nnTF} +% Same as \cs{@@_peek:nnTF} (used for \cs{peek_regex:nTF} above), but +% without disabling submatches, and with a different end. The +% replacement text |#2| is stored, to be analyzed later. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_replace:nnTF #1#2 + { + \tl_set:Nn \l_@@_replacement_tl {#2} + \@@_peek_aux:nnTF {#1} { \@@_peek_replace_end: } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_peek_replace_end:} +% If the match failed \cs{@@_peek_reinsert:N} reinserts the tokens +% found. Otherwise, finish storing the submatch information using +% \cs{@@_extract:}, and store the input into \tn{toks}. Redefine a +% few auxiliaries to change slightly their expansion behaviour as +% explained below. Analyse the replacement text with +% \cs{@@_replacement:n}, which as usual defines +% \cs{@@_replacement_do_one_match:n} to insert the tokens from the +% start of the match attempt to the beginning of the match, followed +% by the replacement text. The \cs{use:x} expands for instance the +% trailing \cs{@@_query_range:nn} down to a sequence of +% \cs{@@_reinsert_item:n} \Arg{tokens} where \meta{tokens} +% \texttt{o}-expand to a single token that we want to insert. After +% \texttt{x}-expansion, \cs{use:x} does \cs{use:n}, so we have +% \cs{exp_after:wN} \cs{l_@@_peek_true_tl} \cs{exp:w} \ldots{} +% \cs{exp_end:}. This is set up such as to obtain +% \cs{l_@@_peek_true_tl} followed by the replaced tokens (possibly +% unbalanced) in the input stream. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_replace_end: + { + \bool_if:NTF \g_@@_success_bool + { + \@@_extract: + \@@_query_set_from_input_tl: + \cs_set_eq:NN \@@_replacement_put:n \@@_peek_replacement_put:n + \cs_set_eq:NN \@@_replacement_put_submatch_aux:n + \@@_peek_replacement_put_submatch_aux:n + \cs_set_eq:NN \@@_input_item:n \@@_reinsert_item:n + \cs_set_eq:NN \@@_replacement_exp_not:N \@@_peek_replacement_token:n + \cs_set_eq:NN \@@_replacement_exp_not:V \@@_peek_replacement_var:N + \exp_args:No \@@_replacement:n { \l_@@_replacement_tl } + \use:x + { + \exp_not:n { \exp_after:wN \l_@@_peek_true_tl \exp:w } + \@@_replacement_do_one_match:n + { \l_@@_zeroth_submatch_int } + \@@_query_range:nn + { + \__kernel_intarray_item:Nn \g_@@_submatch_end_intarray + { \l_@@_zeroth_submatch_int } + } + { \l_@@_max_pos_int } + \exp_end: + } + } + { \@@_peek_reinsert:N \l_@@_peek_false_tl } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_query_set_from_input_tl:, \@@_query_set_item:n} +% The input was stored into \cs{l_@@_input_tl} as successive items +% \cs{@@_input_item:n} \Arg{tokens}. Store that in successive +% \tn{toks}. It's not clear whether the empty entries before and +% after are both useful. +% \begin{macrocode} +\cs_new_protected:Npn \@@_query_set_from_input_tl: + { + \tl_build_end:N \l_@@_input_tl + \int_zero:N \l_@@_curr_pos_int + \cs_set_eq:NN \@@_input_item:n \@@_query_set_item:n + \@@_query_set_item:n { } + \l_@@_input_tl + \@@_query_set_item:n { } + \int_set_eq:NN \l_@@_max_pos_int \l_@@_curr_pos_int + } +\cs_new_protected:Npn \@@_query_set_item:n #1 + { + \int_incr:N \l_@@_curr_pos_int + \@@_toks_set:Nn \l_@@_curr_pos_int { \@@_input_item:n {#1} } + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_peek_replacement_put:n} +% While building the replacement function +% \cs{@@_replacement_do_one_match:n}, we often want to put simple +% material, given as |#1|, whose \texttt{x}-expansion +% \texttt{o}-expands to a single token. Normally we can just add the +% token to \cs{l_@@_build_tl}, but for +% \cs{peek_regex_replace_once:nnTF} we eventually want to do some +% strange expansion that is basically using \cs{exp_after:wN} to jump +% through numerous tokens (we cannot use \texttt{x}-expansion like for +% \cs{regex_replace_once:nnNTF} because it is ok for the result to be +% unbalanced since we insert it in the input stream rather than +% storing it. When within a csname we don't do any such shenanigan +% because \cs{cs:w} \ldots{} \cs{cs_end:} does all the expansion we +% need. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_replacement_put:n #1 + { + \if_case:w \l_@@_replacement_csnames_int + \tl_build_put_right:Nn \l_@@_build_tl + { \exp_not:N \@@_reinsert_item:n {#1} } + \else: + \tl_build_put_right:Nn \l_@@_build_tl {#1} + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_peek_replacement_token:n} +% When hit with \cs{exp:w}, \cs{@@_peek_replacement_token:n} +% \Arg{token} stops \cs{exp_end:} and does \cs{exp_after:wN} +% \meta{token} \cs{exp:w} to continue expansion after it. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_replacement_token:n #1 + { \exp_after:wN \exp_end: \exp_after:wN #1 \exp:w } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_peek_replacement_put_submatch_aux:n} +% While analyzing the replacement we also have to insert submatches +% found in the query. Since query items \cs{@@_input_item:n} +% \Arg{tokens} expand correctly only when surrounded by \cs{exp:w} +% \ldots{} \cs{exp_end:}, and since these expansion controls are not +% there within csnames (because \cs{cs:w} \ldots{} \cs{cs_end:} make +% them unnecessary in most cases), we have to put \cs{exp:w} and +% \cs{exp_end:} by hand here. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_replacement_put_submatch_aux:n #1 + { + \if_case:w \l_@@_replacement_csnames_int + \tl_build_put_right:Nn \l_@@_build_tl + { \@@_query_submatch:n { \int_eval:n { #1 + ##1 } } } + \else: + \tl_build_put_right:Nn \l_@@_build_tl + { \exp:w \@@_query_submatch:n { \int_eval:n { #1 + ##1 } } \exp_end: } + \fi: + } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{\@@_peek_replacement_var:N} +% This is used for |\u| outside csnames. It makes sure to continue +% expansion with \cs{exp:w} before expanding the variable~|#1| and +% stopping the \cs{exp:w} that precedes. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_replacement_var:N #1 + { + \exp_after:wN \exp_last_unbraced:NV + \exp_after:wN \exp_end: + \exp_after:wN #1 + \exp:w + } +% \end{macrocode} +% \end{macro} % % \subsection{Messages} % @@ -6726,8 +7125,6 @@ %^^A NOT IMPLEMENTED %^^A \p{xx} a character with the xx property %^^A \P{xx} a character without the xx property -%^^A [[:xxx:]] positive POSIX named set -%^^A [[:^xxx:]] negative POSIX named set %^^A (?=...) positive look ahead %^^A (?!...) negative look ahead %^^A (?<=...) positive look behind diff --git a/macros/latex/contrib/l3kernel/l3seq.dtx b/macros/latex/contrib/l3kernel/l3seq.dtx index 737a4c776a..28df8592d9 100644 --- a/macros/latex/contrib/l3kernel/l3seq.dtx +++ b/macros/latex/contrib/l3kernel/l3seq.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3skip.dtx b/macros/latex/contrib/l3kernel/l3skip.dtx index aeef5ae45c..6336081c2c 100644 --- a/macros/latex/contrib/l3kernel/l3skip.dtx +++ b/macros/latex/contrib/l3kernel/l3skip.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3sort.dtx b/macros/latex/contrib/l3kernel/l3sort.dtx index 30d2b9737e..b47a9d73d6 100644 --- a/macros/latex/contrib/l3kernel/l3sort.dtx +++ b/macros/latex/contrib/l3kernel/l3sort.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3str-convert.dtx b/macros/latex/contrib/l3kernel/l3str-convert.dtx index 2acfdcf0aa..e3ebadbcdd 100644 --- a/macros/latex/contrib/l3kernel/l3str-convert.dtx +++ b/macros/latex/contrib/l3kernel/l3str-convert.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3str.dtx b/macros/latex/contrib/l3kernel/l3str.dtx index bdde861655..02083ae9b5 100644 --- a/macros/latex/contrib/l3kernel/l3str.dtx +++ b/macros/latex/contrib/l3kernel/l3str.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3styleguide.pdf b/macros/latex/contrib/l3kernel/l3styleguide.pdf Binary files differindex 6969a153b8..05a31a39b5 100644 --- a/macros/latex/contrib/l3kernel/l3styleguide.pdf +++ b/macros/latex/contrib/l3kernel/l3styleguide.pdf diff --git a/macros/latex/contrib/l3kernel/l3styleguide.tex b/macros/latex/contrib/l3kernel/l3styleguide.tex index c94f8239f8..74449f2f6d 100644 --- a/macros/latex/contrib/l3kernel/l3styleguide.tex +++ b/macros/latex/contrib/l3kernel/l3styleguide.tex @@ -32,7 +32,7 @@ The released version of this bundle is available from CTAN. {latex-team@latex-project.org}% }% } -\date{Released 2020-10-27} +\date{Released 2020-12-03} \begin{document} diff --git a/macros/latex/contrib/l3kernel/l3syntax-changes.pdf b/macros/latex/contrib/l3kernel/l3syntax-changes.pdf Binary files differindex f191cd2bdc..3ef526163b 100644 --- a/macros/latex/contrib/l3kernel/l3syntax-changes.pdf +++ b/macros/latex/contrib/l3kernel/l3syntax-changes.pdf diff --git a/macros/latex/contrib/l3kernel/l3syntax-changes.tex b/macros/latex/contrib/l3kernel/l3syntax-changes.tex index d1ac214651..3d627d053c 100644 --- a/macros/latex/contrib/l3kernel/l3syntax-changes.tex +++ b/macros/latex/contrib/l3kernel/l3syntax-changes.tex @@ -32,7 +32,7 @@ The released version of this bundle is available from CTAN. {latex-team@latex-project.org}% }% } -\date{Released 2020-10-27} +\date{Released 2020-12-03} \newcommand{\TF}{\textit{(TF)}} diff --git a/macros/latex/contrib/l3kernel/l3sys.dtx b/macros/latex/contrib/l3kernel/l3sys.dtx index 4e5e3816c7..8ba6b2f2e6 100644 --- a/macros/latex/contrib/l3kernel/l3sys.dtx +++ b/macros/latex/contrib/l3kernel/l3sys.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3term-glossary.pdf b/macros/latex/contrib/l3kernel/l3term-glossary.pdf Binary files differindex ce59abacbe..3e89b677e0 100644 --- a/macros/latex/contrib/l3kernel/l3term-glossary.pdf +++ b/macros/latex/contrib/l3kernel/l3term-glossary.pdf diff --git a/macros/latex/contrib/l3kernel/l3term-glossary.tex b/macros/latex/contrib/l3kernel/l3term-glossary.tex index 051c23c1bb..2a8aff656d 100644 --- a/macros/latex/contrib/l3kernel/l3term-glossary.tex +++ b/macros/latex/contrib/l3kernel/l3term-glossary.tex @@ -32,7 +32,7 @@ The released version of this bundle is available from CTAN. {latex-team@latex-project.org}% }% } -\date{Released 2020-10-27} +\date{Released 2020-12-03} \newcommand{\TF}{\textit{(TF)}} diff --git a/macros/latex/contrib/l3kernel/l3text-case.dtx b/macros/latex/contrib/l3kernel/l3text-case.dtx index fd41c6bdd3..b52d348867 100644 --- a/macros/latex/contrib/l3kernel/l3text-case.dtx +++ b/macros/latex/contrib/l3kernel/l3text-case.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3text-purify.dtx b/macros/latex/contrib/l3kernel/l3text-purify.dtx index 1bc377ed02..4a4abbf256 100644 --- a/macros/latex/contrib/l3kernel/l3text-purify.dtx +++ b/macros/latex/contrib/l3kernel/l3text-purify.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % @@ -396,6 +396,8 @@ \tiny } { \text_declare_purify_equivalent:Nn #1 { } } +\exp_args:Nc \text_declare_purify_equivalent:Nn + { @protected@testopt } { \use_none:nnn } % \end{macrocode} % Environments have to be handled by pure expansion. % diff --git a/macros/latex/contrib/l3kernel/l3text.dtx b/macros/latex/contrib/l3kernel/l3text.dtx index 893c43da59..9ab4ae6ef5 100644 --- a/macros/latex/contrib/l3kernel/l3text.dtx +++ b/macros/latex/contrib/l3kernel/l3text.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % @@ -634,6 +634,8 @@ % \begin{macro}[EXP]{\@@_expand_protect:N} % \begin{macro}[EXP]{\@@_expand_protect:nN} % \begin{macro}[EXP]{\@@_expand_protect:Nw} +% \begin{macro}[EXP]{\@@_expand_testopt:N} +% \begin{macro}[EXP]{\@@_expand_testopt:NNn} % \begin{macro}[EXP]{\@@_expand_replace:N} % \begin{macro}[EXP]{\@@_expand_replace:n} % \begin{macro}[EXP]{\@@_expand_cs_expand:N} @@ -917,7 +919,8 @@ % \LaTeXe{}'s \cs{protect} makes life interesting. Where possible, we % simply remove it and replace with the \enquote{parent} command; of course, % the \cs{protect} might be explicit, in which case we need to leave it alone -% if it's required. +% if it's required. There is also the case of a straight \tn{@protected@testopt} +% to cover. % \begin{macrocode} \cs_new:Npx \@@_expand_cs:N #1 { @@ -927,7 +930,7 @@ \bool_lazy_and:nnTF { \cs_if_exist_p:N \fmtname } { \str_if_eq_p:Vn \fmtname { LaTeX2e } } - { \exp_not:N \@@_expand_encoding:N #1 } + { \exp_not:N \@@_expand_testopt:N #1 } { \exp_not:N \@@_expand_replace:N #1 } } } @@ -949,6 +952,17 @@ { \@@_expand_store:n { \protect #1 } } \@@_expand_loop:w } +\cs_new:Npn \@@_expand_testopt:N #1 + { + \str_if_eq:nnTF {#1} { \@protected@testopt } + { \@@_expand_testopt:NNn } + { \@@_expand_encoding:N #1 } + } +\cs_new:Npn \@@_expand_testopt:NNn #1#2#3 + { + \@@_expand_store:n {#1} + \@@_expand_loop:w + } % \end{macrocode} % Deal with encoding-specific commands % \begin{macrocode} @@ -1036,6 +1050,8 @@ % \end{macro} % \end{macro} % \end{macro} +% \end{macro} +% \end{macro} % % \begin{macro} % { diff --git a/macros/latex/contrib/l3kernel/l3tl-analysis.dtx b/macros/latex/contrib/l3kernel/l3tl-analysis.dtx index be4bd2406e..cf61a8f702 100644 --- a/macros/latex/contrib/l3kernel/l3tl-analysis.dtx +++ b/macros/latex/contrib/l3kernel/l3tl-analysis.dtx @@ -44,7 +44,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % @@ -52,10 +52,30 @@ % % \section{\pkg{l3tl-analysis} documentation} % -% This module mostly provides internal functions for use in the -% \pkg{l3regex} module. However, it provides as a side-effect a user -% debugging function, very similar to the \cs{ShowTokens} macro from the -% \pkg{ted} package. +% This module provides functions that are particularly useful in the +% \pkg{l3regex} module for mapping through a token list one \meta{token} +% at a time (including begin-group/end-group tokens). For +% \cs{tl_analysis_map_inline:Nn} or \cs{tl_analysis_map_inline:nn}, the +% token list is given as an argument; the analogous function +% \cs{peek_analysis_map_inline:n} documented in \pkg{l3token} finds +% tokens in the input stream instead. In both cases the user provides +% \meta{inline code} that receives three arguments for each +% \meta{token}: +% \begin{itemize} +% \item \meta{tokens}, which both \texttt{o}-expand and +% \texttt{x}-expand to the \meta{token}. The detailed form of +% \meta{tokens} may change in later releases. +% \item \meta{char code}, a decimal representation of the character +% code of the \meta{token}, $-1$ if it is a control sequence. +% \item \meta{catcode}, a capital hexadecimal digit which denotes the +% category code of the \meta{token} (0:~control sequence, +% 1:~begin-group, 2:~end-group, 3:~math shift, 4:~alignment tab, +% 6:~parameter, 7:~superscript, 8:~subscript, A:~space, B:~letter, +% C:~other, D:~active). This can be converted to an integer by +% writing |"|\meta{catcode}. +% \end{itemize} +% In addition, there is a debugging function \cs{tl_analysis_show:n}, +% very similar to the \cs{ShowTokens} macro from the \pkg{ted} package. % % \begin{function}[added = 2018-04-09]{\tl_analysis_show:N, \tl_analysis_show:n} % \begin{syntax} @@ -74,23 +94,9 @@ % \end{syntax} % Applies the \meta{inline function} to each individual \meta{token} % in the \meta{token list}. The \meta{inline function} receives three -% arguments: -% \begin{itemize} -% \item \meta{tokens}, which both \texttt{o}-expand and -% \texttt{x}-expand to the \meta{token}. The detailed form of -% \meta{token} may change in later releases. -% \item \meta{char code}, a decimal representation of the character -% code of the token, $-1$ if it is a control sequence (with -% \meta{catcode} $0$). -% \item \meta{catcode}, a capital hexadecimal digit which denotes -% the category code of the \meta{token} (0: control sequence, 1: -% begin-group, 2: end-group, 3: math shift, 4: alignment tab, 6: -% parameter, 7: superscript, 8: subscript, A: space, B: letter, -% C:other, D:active). -% \end{itemize} -% As all other mappings the mapping is done at the current group -% level, \emph{i.e.}~any local assignments made by the \meta{inline -% function} remain in effect after the loop. +% arguments as explained above. As all other mappings the mapping is +% done at the current group level, \emph{i.e.}~any local assignments +% made by the \meta{inline function} remain in effect after the loop. % \end{function} % % \end{documentation} @@ -182,18 +188,54 @@ % \end{macrocode} % \end{variable} % -% \begin{variable}{\l_@@_analysis_token} -% \begin{variable}{\l_@@_analysis_char_token} +% \begin{variable} +% {\l_@@_analysis_token, \l_@@_analysis_char_token, \l_@@_analysis_next_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}. +% \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}. % \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} +% +% \begin{variable}{\l_@@_peek_code_tl} +% Holds some code to be run once the next token has been fully +% analysed in \cs{peek_analysis_map_inline:n}. +% \begin{macrocode} +\tl_new:N \l_@@_peek_code_tl % \end{macrocode} % \end{variable} +% +% \begin{variable}{\c_@@_peek_catcodes_tl} +% A token list containing the character number~$32$ (space) with all +% possible category codes except $1$ and $2$ (begin-group and +% end-group). Why $32$? Because some \LuaTeX{} versions only allow +% creation of catcode~$10$ (space) tokens with this character code, +% and because even in other engines it is much easier to produce since +% \cs{char_generate:nn} refuses to produce spaces. +% \begin{macrocode} +\group_begin: +\char_set_active_eq:NN \ \scan_stop: +\tl_const:Nx \c_@@_peek_catcodes_tl + { + \char_generate:nn { 32 } { 3 } 3 + \char_generate:nn { 32 } { 4 } 4 + # \char_generate:nn { 32 } { 6 } 6 + \char_generate:nn { 32 } { 7 } 7 + \char_generate:nn { 32 } { 8 } 8 + \c_space_tl \token_to_str:N A + \char_generate:nn { 32 } { 11 } \token_to_str:N B + \char_generate:nn { 32 } { 12 } \token_to_str:N C + \char_generate:nn { 32 } { 13 } \token_to_str:N D + } +\group_end: +% \end{macrocode} % \end{variable} % % \begin{variable}{\l_@@_analysis_normal_int} @@ -1075,6 +1117,358 @@ % \end{macro} % \end{macro} % +% \subsection{Peeking ahead} +% +% \begin{macro}[EXP]{\peek_analysis_map_break:, \peek_analysis_map_break:n} +% The break statements use the general \cs{prg_map_break:Nn}. +% \begin{macrocode} +\cs_new:Npn \peek_analysis_map_break: + { \prg_map_break:Nn \peek_analysis_map_break: { } } +\cs_new:Npn \peek_analysis_map_break:n + { \prg_map_break:Nn \peek_analysis_map_break: } +% \end{macrocode} +% \end{macro} +% +% \begin{variable}{\l_@@_peek_charcode_int} +% \begin{macrocode} +\int_new:N \l_@@_peek_charcode_int +% \end{macrocode} +% \end{variable} +% +% \begin{macro}{\@@_analysis_char_arg:Nw, \@@_analysis_char_arg_aux:Nw} +% After a call to \tn{futurelet} \cs{l_@@_analysis_token} followed by +% a stringified character token (either explicit space or catcode +% other character), grab the argument and pass it to |#1|. We only +% need to do anything in the case of a space. +% \begin{macrocode} +\cs_new:Npn \@@_analysis_char_arg:Nw + { + \if_meaning:w \l_@@_analysis_token \c_space_token + \exp_after:wN \@@_analysis_char_arg_aux:Nw + \fi: + } +\cs_new:Npn \@@_analysis_char_arg_aux:Nw #1 ~ { #1 { ~ } } +% \end{macrocode} +% \end{macro} +% +% \begin{macro} +% { +% \peek_analysis_map_inline:n, +% \@@_peek_analysis_loop:NNn, \@@_peek_analysis_test:, +% \@@_peek_analysis_normal:N, \@@_peek_analysis_cs:, +% \@@_peek_analysis_char:N, \@@_peek_analysis_char:nN, +% \@@_peek_analysis_special:, \@@_peek_analysis_retest:, +% \@@_peek_analysis_next:, \@@_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 +% } +% 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} +% control sequence or active character; for this we will undefine +% potentially-\tn{outer} tokens within a group, closed after the +% function receives its arguments. This user's code function also +% calls the loop auxiliary, and includes the trailing +% \cs{prg_break_point:Nn} for when the user wants to stop the loop. +% The loop auxiliary must remove that break point because it must look +% at the input stream. +% \begin{macrocode} +\cs_new_protected:Npn \peek_analysis_map_inline:n #1 + { + \int_gincr:N \g__kernel_prg_map_int + \cs_set_protected:cpn + { @@_analysis_map_ \int_use:N \g__kernel_prg_map_int :nnN } + ##1##2##3 + { + \group_end: + #1 + \@@_peek_analysis_loop:NNn + \prg_break_point:Nn \peek_analysis_map_break: { } + } + \@@_peek_analysis_loop:NNn ? ? ? + } +% \end{macrocode} +% The loop starts a group (closed by the user-code function defined +% above) with a normalized escape character, and checks if the next +% token is special or \texttt{N}-type. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_analysis_loop:NNn #1#2#3 + { + \group_begin: + \tl_set:Nx \l_@@_peek_code_tl + { + \exp_not:c + { @@_analysis_map_ \int_use:N \g__kernel_prg_map_int :nnN } + } + \int_set:Nn \tex_escapechar:D { `\\ } + \peek_after:Nw \@@_peek_analysis_test: + } +\cs_new_protected:Npn \@@_peek_analysis_test: + { + \if_int_odd:w + \if_catcode:w \exp_not:N \l_peek_token { 1 \exp_stop_f: \fi: + \if_catcode:w \exp_not:N \l_peek_token } 1 \exp_stop_f: \fi: + \if_meaning:w \l_peek_token \c_space_token 1 \exp_stop_f: \fi: + 0 \exp_stop_f: + \exp_after:wN \@@_peek_analysis_special: + \else: + \exp_after:wN \exp_after:wN + \exp_after:wN \@@_peek_analysis_normal:N + \exp_after:wN \exp_not:N + \fi: + } +% \end{macrocode} +% Normal tokens are not too hard, but can be \tn{outer}, hence the +% \cs{exp_not:N} in the code above. If the token is expandable then +% it might be an \tn{outer} or a \TeX{} conditional, so to be safe we +% set it to \cs{scan_stop:} (the assignment is local and stopped by +% the \cs{group_end:} upon calling the user's code). Then distinguish +% characters (including active ones and macro parameter characters) +% from control sequences (whose string representation is more than one +% character because the escape character is printable). For a control +% sequence call the user code with suitable arguments. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_analysis_normal:N #1 + { + \exp_after:wN \reverse_if:N \exp_after:wN \if_meaning:w + \exp_not:N #1 #1 + \tex_let:D #1 \scan_stop: + \tl_put_right:Nn \l_@@_peek_code_tl { { \exp_not:N #1 } } + \else: + \tl_put_right:Nn \l_@@_peek_code_tl { { \exp_not:n {#1} } } + \fi: + \if_charcode:w + \scan_stop: + \exp_after:wN \use_none:n \token_to_str:N #1 \prg_do_nothing: + \scan_stop: + \exp_after:wN \@@_peek_analysis_char:N + \exp_after:wN #1 + \else: + \exp_after:wN \@@_peek_analysis_cs: + \fi: + } +\cs_new_protected:Npn \@@_peek_analysis_cs: + { \l_@@_peek_code_tl { -1 } 0 } +\cs_new_protected:Npn \@@_peek_analysis_char:N #1 + { + \char_set_lccode:nn { `#1 } { 32 } + \tex_lowercase:D { \@@_peek_analysis_char:nN {#1} } #1 + } +\cs_new_protected:Npn \@@_peek_analysis_char:nN #1#2 + { + \cs_set_protected:Npn \@@_tmp:w ##1 #1 ##2 ##3 \scan_stop: + { \exp_args:No \l_@@_peek_code_tl { \int_value:w `#2 } ##2 } + \exp_after:wN \@@_tmp:w \c_@@_peek_catcodes_tl \scan_stop: + } +% \end{macrocode} +% For special characters the idea is to eventually act with +% \cs{token_to_str:N}, then pick up one by one the characters of this +% string representation until hitting the token that follows. First +% determine the character code of (the meaning of) the \meta{token} +% (which we know is a special token), make sure the escape character +% is different from it, normalize the meanings of two active +% characters and the empty control sequence, and filter out these +% cases in \cs{@@_peek_analysis_retest:}. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_analysis_special: + { + \tex_let:D \l_@@_analysis_token = ~ \l_peek_token + \int_set:Nn \l_@@_peek_charcode_int + { \@@_analysis_extract_charcode: } + \if_int_compare:w \l_@@_peek_charcode_int = \tex_escapechar:D + \int_set:Nn \tex_escapechar:D { `\/ } + \fi: + \char_set_active_eq:nN { \l_@@_peek_charcode_int } \scan_stop: + \char_set_active_eq:nN { \tex_escapechar:D } \scan_stop: + \cs_set_eq:cN { } \scan_stop: + \tex_futurelet:D \l_@@_analysis_token + \@@_peek_analysis_retest: + } +\cs_new_protected:Npn \@@_peek_analysis_retest: + { + \if_meaning:w \l_@@_analysis_token \scan_stop: + \exp_after:wN \@@_peek_analysis_normal:N + \else: + \exp_after:wN \@@_peek_analysis_next: + \fi: + } +% \end{macrocode} +% At this point we know the meaning of the \meta{token} in the input +% stream is \cs{l_peek_token}, either a space (32, 10) or a +% 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}. +% 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_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 +% precisely, by looking at the first character in the string +% representation of the \meta{first 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 +% 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). +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_analysis_str: + { + \exp_after:wN \tex_futurelet:D + \exp_after:wN \l_@@_analysis_token + \exp_after:wN \@@_peek_analysis_str:w + \token_to_str:N + } +\cs_new_protected:Npn \@@_peek_analysis_str:w + { \@@_analysis_char_arg:Nw \@@_peek_analysis_str:n } +\cs_new_protected:Npn \@@_peek_analysis_str:n #1 + { + \int_case:nnF { `#1 } + { + { \l_@@_peek_charcode_int } + { \@@_peek_analysis_explicit:n {#1} } + { \tex_escapechar:D } { \@@_peek_analysis_escape: } + } + { \@@_peek_analysis_active_str:n {#1} } + } +% \end{macrocode} +% When |#1| is a stringified active character we pass appropriate +% arguments to the user's code; thankfully \cs{char_generate:nn} +% can make active characters. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_analysis_active_str:n #1 + { + \tl_put_right:Nx \l_@@_peek_code_tl + { + { \char_generate:nn { `#1 } { 13 } } + { \int_value:w `#1 } + \token_to_str:N D + } + \l_@@_peek_code_tl + } +% \end{macrocode} +% When |#1| matches the character we had extracted from the meaning of +% \cs{l_peek_token}, the token was an explicit character, which can be +% a standard space, or a begin-group or end-group character with some +% character code. In the latter two cases we call +% \cs{char_generate:nn} with suitable arguments and put suitable +% \cs{if_false:} \cs{fi:} constructions to make the result balanced +% and such that \texttt{o}-expanding or \texttt{x}-expanding gives +% back a single (unbalanced) begin-group or end-group character. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_analysis_explicit:n #1 + { + \tl_put_right:Nx \l_@@_peek_code_tl + { + \if_meaning:w \l_peek_token \c_space_token + { ~ } { 32 } \token_to_str:N A + \else: + \if_catcode:w \l_peek_token \c_group_begin_token + { + \exp_not:N \exp_after:wN + \char_generate:nn { `#1 } { 1 } + \exp_not:N \if_false: + \if_false: { \fi: } + \exp_not:N \fi: + } + { \int_value:w `#1 } + 1 + \else: + { + \exp_not:N \if_false: + { \if_false: } \fi: + \exp_not:N \fi: + \char_generate:nn { `#1 } { 2 } + } + { \int_value:w `#1 } + 2 + \fi: + \fi: + } + \l_@@_peek_code_tl + } +% \end{macrocode} +% Finally there is the case of a special token whose string +% representation starts with an escape character, namely the token was +% a control sequence. In that case we could have grabbed the token +% directly as an \texttt{N}-type argument, but of course we couldn't +% 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. +% \begin{macrocode} +\cs_new_protected:Npn \@@_peek_analysis_escape: + { + \tl_clear:N \l_@@_internal_a_tl + \tex_futurelet:D \l_@@_analysis_token + \@@_peek_analysis_collect:w + } +\cs_new_protected:Npn \@@_peek_analysis_collect:w + { \@@_analysis_char_arg:Nw \@@_peek_analysis_collect:n } +\cs_new_protected:Npn \@@_peek_analysis_collect:n #1 + { + \tl_put_right:Nn \l_@@_internal_a_tl {#1} + \@@_peek_analysis_collect_loop: + } +\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 + \fi: + \fi: + \@@_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 + { + #1 #2 + \tl_put_right:Nx \l_@@_peek_code_tl + { + { \exp_not:N \exp_not:n { \exp_not:c { \l_@@_internal_a_tl } } } + { -1 } + 0 + } + \l_@@_peek_code_tl + } +% \end{macrocode} +% \end{macro} +% % \subsection{Messages} % % \begin{variable}{\c_@@_analysis_show_etc_str} diff --git a/macros/latex/contrib/l3kernel/l3tl.dtx b/macros/latex/contrib/l3kernel/l3tl.dtx index b942e97703..f88abe05c7 100644 --- a/macros/latex/contrib/l3kernel/l3tl.dtx +++ b/macros/latex/contrib/l3kernel/l3tl.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/l3token.dtx b/macros/latex/contrib/l3kernel/l3token.dtx index 676c55747a..6105f3aab0 100644 --- a/macros/latex/contrib/l3kernel/l3token.dtx +++ b/macros/latex/contrib/l3kernel/l3token.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % @@ -754,14 +754,42 @@ % this includes primitive-like commands defined using |{token.set_lua}|. % \end{function} % +% \begin{function}[added = 2020-12-03, EXP, noTF] +% {\token_case_catcode:Nn, \token_case_charcode:Nn, \token_case_meaning:Nn} +% \begin{syntax} +% \cs{token_case_meaning:NnTF} \meta{test token} \\ +% ~~"{" \\ +% ~~~~\meta{token case_1} \Arg{code case_1} \\ +% ~~~~\meta{token case_2} \Arg{code case_2} \\ +% ~~~~\ldots \\ +% ~~~~\meta{token case_n} \Arg{code case_n} \\ +% ~~"}" \\ +% ~~\Arg{true code} +% ~~\Arg{false code} +% \end{syntax} +% This function compares the \meta{test token} in turn with each of +% the \meta{token cases}. If the two are equal (as described for +% \cs{token_if_eq_catcode:NNTF}, \cs{token_if_eq_charcode:NNTF} and +% \cs{token_if_eq_meaning:NNTF}, respectively) then the associated +% \meta{code} is left in the input stream and other cases are +% discarded. If any of the cases are matched, the \meta{true code} is +% also inserted into the input stream (after the code for the +% appropriate case), while if none match then the \meta{false code} is +% inserted. The functions \cs{token_case_catcode:Nn}, +% \cs{token_case_charcode:Nn}, and \cs{token_case_meaning:Nn}, which +% do nothing if there is no match, are also available. +% \end{function} +% % \section{Peeking ahead at the next token} % % 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. +% \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. % % \begin{function}{\peek_after:Nw} % \begin{syntax} @@ -980,6 +1008,128 @@ % (as appropriate to the result of the test). % \end{function} % +% \begin{function}[added = 2020-12-03]{\peek_analysis_map_inline:n} +% \begin{syntax} +% \cs{peek_analysis_map_inline:n} \Arg{inline function} +% \end{syntax} +% Repeatedly removes one \meta{token} from the input stream and +% applies the \meta{inline function} to it, until +% \cs{peek_analysis_map_break:} is called. The \meta{inline function} +% receives three arguments for each \meta{token} in the input stream: +% \begin{itemize} +% \item \meta{tokens}, which both \texttt{o}-expand and +% \texttt{x}-expand to the \meta{token}. The detailed form of +% \meta{tokens} may change in later releases. +% \item \meta{char code}, a decimal representation of the character +% code of the \meta{token}, $-1$ if it is a control sequence. +% \item \meta{catcode}, a capital hexadecimal digit which denotes the +% category code of the \meta{token} (0:~control sequence, +% 1:~begin-group, 2:~end-group, 3:~math shift, 4:~alignment tab, +% 6:~parameter, 7:~superscript, 8:~subscript, A:~space, B:~letter, +% C:~other, D:~active). This can be converted to an integer by +% writing |"|\meta{catcode}. +% \end{itemize} +% These arguments are the same as for \cs{tl_analysis_map_inline:nn} +% defined in \pkg{l3tl-analysis}. The \meta{char code} and +% \meta{catcode} do not take the meaning of a control sequence or +% active character into account: for instance, upon encountering the +% token \cs{c_group_begin_token} in the input stream, +% \cs{peek_analysis_map_inline:n} calls the \meta{inline function} +% with |#1| being \cs{exp_not:n} |{| \cs{c_group_begin_token} |}| +% (with the current implementation), +% |#2|~being~$-1$, and +% |#3|~being~$0$, as for any other control sequence. In contrast, +% upon encountering an explicit begin-group token~|{|, % ^^A |}| +% the \meta{inline function} is called with arguments +% \cs{exp_after:wN} |{| \cs{if_false:} |}| \cs{fi:}, $123$ and~$1$. +% +% The mapping is done at the current group level, \emph{i.e.}~any +% local assignments made by the \meta{inline function} remain in +% 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. +% \end{function} +% +% \begin{function}[added = 2020-12-03] +% {\peek_analysis_map_break:, \peek_analysis_map_break:n} +% \begin{syntax} +% \cs{peek_analysis_map_inline:n} +% |{| \dots{} \cs{peek_analysis_map_break:n} \Arg{code} |}| +% \end{syntax} +% Stops the \cs{peek_analysis_map_inline:n} loop from seeking more +% tokens, and inserts \meta{code} in the input stream (empty for +% \cs{peek_analysis_map_break:}). +% \end{function} +% +% \begin{function}[added = 2020-12-03, TF]{\peek_regex:n, \peek_regex:N} +% \begin{syntax} +% \cs{peek_regex:nTF} \Arg{regex} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if the \meta{tokens} that follow in the input stream match the +% \meta{regular expression}. Any \meta{tokens} that have been read +% are left in the input stream after the \meta{true code} or +% \meta{false code} (as appropriate to the result of the test). See +% \pkg{l3regex} for documentation of the syntax of regular +% expressions. The \meta{regular expression} is implicitly anchored +% at the start, so for instance \cs{peek_regex:nTF}~|{|~|a|~|}| is +% essentially equivalent to \cs{peek_charcode:NTF}~|a|. +% \begin{texnote} +% Implicit character tokens are correctly considered by +% \cs{peek_regex:nTF} as control sequences, while functions that +% inspect individual tokens (for instance \cs{peek_charcode:NTF}) +% only take into account their meaning. +% \end{texnote} +% \end{function} +% +% \begin{function}[added = 2020-12-03, TF] +% {\peek_regex_remove_once:n, \peek_regex_remove_once:N} +% \begin{syntax} +% \cs{peek_regex_remove_once:nTF} \Arg{regex} \Arg{true code} \Arg{false code} +% \end{syntax} +% Tests if the \meta{tokens} that follow in the input stream match the +% \meta{regex}. If the test is true, the \meta{tokens} are removed +% from the input stream and the \meta{true code} is inserted, while if +% the test is false, the \meta{false code} is inserted followed by the +% \meta{tokens} that were originally in the input stream. +% See \pkg{l3regex} for documentation of the syntax of +% regular expressions. The \meta{regular expression} is implicitly +% anchored at the start, so for instance +% \cs{peek_regex_remove_once:nTF}~|{|~|a|~|}| is essentially equivalent to +% \cs{peek_charcode_remove:NTF}~|a|. +% \begin{texnote} +% Implicit character tokens are correctly considered by +% \cs{peek_regex_remove_once:nTF} as control sequences, while functions +% that inspect individual tokens (for instance +% \cs{peek_charcode:NTF}) only take into account their meaning. +% \end{texnote} +% \end{function} +% +% \begin{function}[added = 2020-12-03, noTF] +% {\peek_regex_replace_once:nn, \peek_regex_replace_once:Nn} +% \begin{syntax} +% \cs{peek_regex_replace_once:nnTF} \Arg{regex} \Arg{replacement} \Arg{true code} \Arg{false code} +% \end{syntax} +% If the \meta{tokens} that follow in the input stream match the +% \meta{regex}, replaces them according to the \meta{replacement} as +% for \cs{regex_replace_once:nnN}, and leaves the result in the input +% stream, after the \meta{true code}. Otherwise, leaves \meta{false +% code} followed by the \meta{tokens} that were originally in the +% input stream, with no modifications. See \pkg{l3regex} for +% documentation of the syntax of regular expressions and of the +% \meta{replacement}: for instance |\0| in the \meta{replacement} is +% replaced by the tokens that were matched in the input stream. The +% \meta{regular expression} is implicitly anchored at the start. In +% contrast to \cs{regex_replace_once:nnN}, no error arises if the +% \meta{replacement} leads to an unbalanced token list: the tokens are +% inserted into the input stream without issue. +% \begin{texnote} +% Implicit character tokens are correctly considered by +% \cs{peek_regex_replace_once:nnTF} as control sequences, while +% functions that inspect individual tokens (for instance +% \cs{peek_charcode:NTF}) only take into account their meaning. +% \end{texnote} +% \end{function} +% % \section{Description of all possible tokens} % \label{sec:l3token:all-tokens} % @@ -1897,9 +2047,10 @@ %<@@=token> % \end{macrocode} % -% \begin{variable}{\s_@@_stop} +% \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} @@ -2566,6 +2717,72 @@ end % \end{macro} % \end{macro} % +% \begin{macro}[EXP, noTF] +% {\token_case_catcode:Nn, \token_case_charcode:Nn, \token_case_meaning:Nn} +% \begin{macro}[EXP]{\@@_case:NNnTF, \@@_case:NNw, \@@_case_end:nw} +% The aim here is to allow the case statement to be evaluated +% using a known number of expansion steps (two), and without +% needing to use an explicit \enquote{end of recursion} marker. +% That is achieved by using the test input as the final case, +% as this is always true. The trick is then to tidy up +% the output such that the appropriate case code plus either +% the \texttt{true} or \texttt{false} branch code is inserted. +% \begin{macrocode} +\cs_new:Npn \token_case_catcode:Nn #1#2 + { \exp:w \@@_case:NNnTF \token_if_eq_catcode:NNTF #1 {#2} { } { } } +\cs_new:Npn \token_case_catcode:NnT #1#2#3 + { \exp:w \@@_case:NNnTF \token_if_eq_catcode:NNTF #1 {#2} {#3} { } } +\cs_new:Npn \token_case_catcode:NnF #1#2 + { \exp:w \@@_case:NNnTF \token_if_eq_catcode:NNTF #1 {#2} { } } +\cs_new:Npn \token_case_catcode:NnTF + { \exp:w \@@_case:NNnTF \token_if_eq_catcode:NNTF } +\cs_new:Npn \token_case_charcode:Nn #1#2 + { \exp:w \@@_case:NNnTF \token_if_eq_charcode:NNTF #1 {#2} { } { } } +\cs_new:Npn \token_case_charcode:NnT #1#2#3 + { \exp:w \@@_case:NNnTF \token_if_eq_charcode:NNTF #1 {#2} {#3} { } } +\cs_new:Npn \token_case_charcode:NnF #1#2 + { \exp:w \@@_case:NNnTF \token_if_eq_charcode:NNTF #1 {#2} { } } +\cs_new:Npn \token_case_charcode:NnTF + { \exp:w \@@_case:NNnTF \token_if_eq_charcode:NNTF } +\cs_new:Npn \token_case_meaning:Nn #1#2 + { \exp:w \@@_case:NNnTF \token_if_eq_meaning:NNTF #1 {#2} { } { } } +\cs_new:Npn \token_case_meaning:NnT #1#2#3 + { \exp:w \@@_case:NNnTF \token_if_eq_meaning:NNTF #1 {#2} {#3} { } } +\cs_new:Npn \token_case_meaning:NnF #1#2 + { \exp:w \@@_case:NNnTF \token_if_eq_meaning:NNTF #1 {#2} { } } +\cs_new:Npn \token_case_meaning:NnTF + { \exp:w \@@_case:NNnTF \token_if_eq_meaning:NNTF } +\cs_new:Npn \@@_case:NNnTF #1#2#3#4#5 + { + \@@_case:NNw #1 #2 #3 #2 { } + \s_@@_mark {#4} + \s_@@_mark {#5} + \s_@@_stop + } +\cs_new:Npn \@@_case:NNw #1#2#3#4 + { + #1 #2 #3 + { \@@_case_end:nw {#4} } + { \@@_case:NNw #1 #2 } + } +% \end{macrocode} +% To tidy up the recursion, there are two outcomes. If there was a hit to +% one of the cases searched for, then |#1| is the code to insert, +% |#2| is the \emph{next} case to check on and |#3| is all of +% the rest of the cases code. That means that |#4| is the \texttt{true} +% branch code, and |#5| tidies up the spare \cs{s_@@_mark} and the +% \texttt{false} branch. On the other hand, if none of the cases matched +% then we arrive here using the \enquote{termination} case of comparing +% the search with itself. That means that |#1| is empty, |#2| is +% the first \cs{s_@@_mark} and so |#4| is the \texttt{false} code (the +% \texttt{true} code is mopped up by |#3|). +% \begin{macrocode} +\cs_new:Npn \@@_case_end:nw #1#2#3 \s_@@_mark #4#5 \s_@@_stop + { \exp_end: #1 #4 } +% \end{macrocode} +% \end{macro} +% \end{macro} +% % \subsection{Peeking ahead at the next token} % % \begin{macrocode} diff --git a/macros/latex/contrib/l3kernel/l3unicode.dtx b/macros/latex/contrib/l3kernel/l3unicode.dtx index 14467d9617..c69fa555ab 100644 --- a/macros/latex/contrib/l3kernel/l3unicode.dtx +++ b/macros/latex/contrib/l3kernel/l3unicode.dtx @@ -43,7 +43,7 @@ % }^^A % } % -% \date{Released 2020-10-27} +% \date{Released 2020-12-03} % % \maketitle % diff --git a/macros/latex/contrib/l3kernel/source3.pdf b/macros/latex/contrib/l3kernel/source3.pdf Binary files differindex 43048251a4..400ff72f7b 100644 --- a/macros/latex/contrib/l3kernel/source3.pdf +++ b/macros/latex/contrib/l3kernel/source3.pdf diff --git a/macros/latex/contrib/l3kernel/source3.tex b/macros/latex/contrib/l3kernel/source3.tex index f47fbf0233..07617c24cb 100644 --- a/macros/latex/contrib/l3kernel/source3.tex +++ b/macros/latex/contrib/l3kernel/source3.tex @@ -53,7 +53,7 @@ for those people who are interested. {latex-team@latex-project.org}% }% } -\date{Released 2020-10-27} +\date{Released 2020-12-03} \pagenumbering{roman} \maketitle diff --git a/macros/latex/contrib/l3kernel/source3body.tex b/macros/latex/contrib/l3kernel/source3body.tex index 5586d73799..483054416e 100644 --- a/macros/latex/contrib/l3kernel/source3body.tex +++ b/macros/latex/contrib/l3kernel/source3body.tex @@ -192,10 +192,22 @@ following argument specifiers: \emph{weird} arguments. This covers everything else, but mainly applies to delimited values (where the argument must be terminated by some specified string). - \item[\texttt{D}] The \texttt{D} specifier means \emph{do not use}. + \item[\texttt{D}] The \texttt{D} stands for \textbf{Do not use}. All of the \TeX{} primitives are initially \cs{let} to a \texttt{D} - name, and some are then given a second name. Only the kernel - team should use anything with a \texttt{D} specifier! + name, and some are then given a second name. + These functions have no standardized syntax, they are engine + 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}%^^A + \footnote{If a primitive offers a functionality not yet in the + kernel, programmers and users are encouraged to write to the + \texttt{LaTeX-L} mailing list + (\url{mailto:LATEX-L@listserv.uni-heidelberg.de}) describing + their use-case and intended behaviour, so that a possible + interface can be discussed. Temporarily, while an interface is + not provided, programmers may use the procedure described in the + \href{l3styleguide.pdf}{l3styleguide.pdf}.}. \end{description} Notice that the argument specifier describes how the argument is processed prior to being passed to the underlying function. For example, diff --git a/macros/latex/contrib/pbalance/LICENSE b/macros/latex/contrib/pbalance/LICENSE new file mode 100644 index 0000000000..dea471c70c --- /dev/null +++ b/macros/latex/contrib/pbalance/LICENSE @@ -0,0 +1,11 @@ +Copyright 2020 Nelson Lago <lago@ime.usp.br> + +This work may be distributed and/or modified under the conditions of the +LaTeX Project Public License, either version 1.3 of this license or (at +your option) any later version. The latest version of this license can be +found at http://www.latex-project.org/lppl.txt and version 1.3 or later +is part of all distributions of LaTeX version 2005/12/01 or later. + +This work has the LPPL maintenance status `maintained'. + +The Current Maintainer of this work is Nelson Lago <lago@ime.usp.br>. diff --git a/macros/latex/contrib/pbalance/README.md b/macros/latex/contrib/pbalance/README.md new file mode 100644 index 0000000000..6ac43ea0c8 --- /dev/null +++ b/macros/latex/contrib/pbalance/README.md @@ -0,0 +1,14 @@ +# pbalance - poor man's balance package for LaTeX + +This is a LaTeX package that tries to *safely* make the columns on the last +page of a two-column document have approximately the same height. It should +"just work" without user intervention, which is particularly useful for +class authors, but also offers the user a command to control the result. +There are, however, three caveats: + +1. Results are adequate, but often not optimal; +2. In some cases, the package may give up (the document is + generated correctly but the last page is not balanced); +3. The package demands additional LaTeX passes. + +Code etc: <https://gitlab.com/lago/pbalance> diff --git a/macros/latex/contrib/pbalance/pbalance.dtx b/macros/latex/contrib/pbalance/pbalance.dtx new file mode 100644 index 0000000000..e63bee62a9 --- /dev/null +++ b/macros/latex/contrib/pbalance/pbalance.dtx @@ -0,0 +1,1069 @@ +% \iffalse meta-comment +% Copyright 2020 Nelson Lago <lago@ime.usp.br> +% +% This work may be distributed and/or modified under the conditions of the +% LaTeX Project Public License, either version 1.3 of this license or (at +% your option) any later version. The latest version of this license can be +% found at http://www.latex-project.org/lppl.txt and version 1.3 or later +% is part of all distributions of LaTeX version 2005/12/01 or later. +% +% This work has the LPPL maintenance status `maintained'. +% +% The Current Maintainer of this work is Nelson Lago <lago@ime.usp.br>. +% +% \fi +% \iffalse +% +%<package>\NeedsTeXFormat{LaTeX2e}[2015/01/01] +%<package>\ProvidesPackage{pbalance}[2020/12/03 v1.0 Poor man's balance] +% +%<*driver> + +\documentclass{ltxdoc} + +\usepackage[hyperref,svgnames,x11names,table]{xcolor} +\usepackage{url} +\urlstyle{sf} +\usepackage{hyperref} +\hypersetup{ + pdfborder={0 0 .6}, + pdfborderstyle={/S/U/W .6}, + urlbordercolor=DodgerBlue, + citebordercolor=White, + linkbordercolor=White, + filebordercolor=White, +} + +\usepackage[draft]{pbalance} +\makeatletter +\dimgdef\@PBtmpHeight{0pt} +\dimgdef\@PBtmpLeftHeight{0pt} +\dimgdef\@PBtmpLeftFloatsHeight{0pt} +\dimgdef\@PBtmpRightHeight{0pt} +\dimgdef\@PBtmpRightFloatsHeight{0pt} +\dimgdef\@PBtmpUsedLeft{0pt} +\dimgdef\@PBtmpUsedRight{0pt} +\makeatother + +\usepackage{libertinus} +\usepackage[scale=.85]{sourcecodepro} + +%%\EnableCrossrefs +%%\CodelineIndex +%%\RecordChanges + +%%\OnlyDescription + +\begin{document} +\DocInput{pbalance.dtx} +\end{document} + +%</driver> +% +% \fi +% +% \CheckSum{0} +% +% \changes{v1.0}{2020/09/14}{Initial version} +% +% \GetFileInfo{pbalance.sty} +% +% \title{The \textsf{pbalance} (poor man's balance) package\thanks{This +% document corresponds to \textsf{pbalance}~\fileversion, dated~\filedate.}} +% +% \author{ +% Nelson Lago\\ +% \texttt{lago@ime.usp.br}\\ +% ~\\ +% \url{https://gitlab.com/lago/pbalance} +%} +% +% \maketitle +% +% \begin{abstract} +% +% This package tries to \emph{safely} make the columns on the last page +% of a two-column document have approximately the same height. It should +% ``just work'' without user intervention, which is particularly useful +% for class authors, but also offers the user a command to control the +% result. There are, however, three caveats: +% +% \begin{enumerate} +% \item Results are adequate, but often not optimal; +% \item In some cases, the package may give up (the document is +% generated correctly but the last page is not balanced); +% \item The package demands additional \LaTeX{} passes. +% \end{enumerate} +% +% \end{abstract} +% +% \section{Introduction and usage} +% +% First things first: this package is a \textbf{hack}. It is also +% \textbf{beta} quality. However, I believe it is \emph{safe}, +% i.e., it should not generate incorrect output. +% +% In a two-column document, it is desirable for the columns in the last +% page to have the same height (the columns should be ``balanced''). Two +% packages (\texttt{balance} and \texttt{flushend}) try to provide this +% in \LaTeX, but both may generate defective output in some circumstances. +% This package tries to solve this problem. +% +% Balancing is often not possible: imagine a document with an odd number +% of lines on the last page, or with ten lines of text together with an +% image that takes the height of fifteen lines of text, or a document where +% making columns the same height would leave a sectioning comand at the +% bottom of the first column etc. Since such cases happen often, we consider +% it enough for the columns to be ``not too unequal'', even when equal +% heights might be possible (the algorithm cannot deliver better results +% and, in fact, striving to make columns exactly the same height with it +% may sometimes yield poor results, so we do not even try). +% +% To use the package, add \verb|\usepackage{pbalance}| to the preamble +% and things should ``just work''. However, the document will take more +% \LaTeX{} passes to compile, and minute changes to it that would normally +% need one additional \LaTeX{} pass will likely need three. +% +% If the last page does not have floats, footnotes, or marginpars, the +% package simply uses \verb|\balance|, from the \texttt{balance} package; +% otherwise it uses its own algorithm. You may instead use +% \verb|\shrinkLastPage{some-measurement}| to manually define how much +% shorter the first column of the last page should be. This, however, will +% not eliminate the need for extra passes (the package needs to find out +% which page is the last). +% +% \section{How does it work} +% +% This package works on two fronts: +% +% \begin{enumerate} +% +% \item It uses a \LaTeX{} pass to gather information: which is the last page? +% Does it have floats? Footnotes? How much free space in each column? +% +% \item It uses this information in a subsequent pass to balance the last +% page. If there are no floats or footnotes, it uses the balance +% package; otherwise it uses the measurements collected to shrink +% the first column (using \verb|\enlargethispage|) by a +% ``reasonable'' amount. +% +% \end{enumerate} +% +% If measurements change between passes, this means the document was +% edited, so we forget the previously gathered data and start over. +% However, the package cannot detect changes that happen between the +% ``collect data'' pass and the ``balance for the first time'' pass: +% it has to assume that any change at this point is caused by the fact +% that we started balancing (it can, however, detect if the last text +% page changed; if this happens, we give up balancing). If the change is +% small, that is acceptable: we are not aiming at balancing perfectly +% anyway. If it is a big change (for example, the addition of the +% bibliography block), results will suffer. +% +% To prevent this, it would be ideal to only start the process after all +% passes needed to stabilize the document have run. In practice, however, +% this would be too hard to detect. What we do instead is simply wait for +% two consecutive passes to result in columns of the same size in the +% last page. When this happens, we proceed to balance in the next pass +% and assume (quite reasonably) that, as long as the document is not +% modified, the effect of further passes in the last page is small, so +% ignoring it disturbs the balancing only slightly\footnote{Like I said +% on the introduction: this package is a hack.}. +% +% \section{TODO / wish list} +% +% Some useful stuff we should consider implementing: +% +% \begin{itemize} +% +% \item add \verb|\if@twocolumn| tests to prevent crashes and +% other problems; +% +% \item add options to (1) allow using the balance package on pages with +% floats and (2) completely disable the balance package; +% +% \item make the mechanism able to work with any page, not just the last, +% and with more than one page at once. This is useful for book +% chapters, for example; +% +% \item on top of that: +% \begin{enumerate} +% \item create command to manually indicate the page numbers to balance +% \item create command similar to \verb|\balance| from the +% \texttt{balance} package +% \item allow user to set manual balancing for each page independently +% \end{enumerate} +% +% \end{itemize} +% +% \StopEventually{} +% +% \section{The implementation} +% +% We define \verb|\@PBlastPage| using \texttt{zref-abspage}; It is the page +% number for the last page of text. This might \emph{not} be the last page +% of the document: there may be one or more float pages after it. Still, +% it \emph{is} the page we want to balance. We do not act on it, however; +% instead, we save its value to the aux file as \verb|\@PBprevLastPage| and +% proceed to balance on the next \LaTeX{} pass. +% +% We also define two toggles: +% +% \begin{description} +% \item[\texttt{@PBstabilized}] There have been a few passes +% already and the document has apparently stabilized. +% \item[\texttt{@PBimpossible}] We tried to balance on a +% previous pass and failed. +% \end{description} +% +% \subsection{Initialization, required packages etc.} +% +% Besides some other required packages, here we load the \texttt{balance} +% package; if there are no floats or footnotes involved, we will simply use +% it. We need to load it after we modify \verb|\@outputdblcol|, so we use +% \verb|\AtEndOfPackage|. +% +% We also define the \texttt{draft} option, which disables processing. +% +% \begin{macrocode} + +\RequirePackage{etoolbox} +\RequirePackage{expl3} +\RequirePackage{atbegshi} % Manipulate the page output routine +\RequirePackage{atveryend} % Write to the aux file after processing ends +\RequirePackage{zref-abspage} % Figure out the current page + +\AtEndOfPackage{\RequirePackage{balance}} + +% See https://github.com/latex3/latex2e/issues/399#issuecomment-703081793 +\gdef \@reinserts{% + \ifvbox\@kludgeins\insert\@kludgeins + {\unvbox\@kludgeins}\fi + \ifvoid\footins\else\insert\footins{\unvbox\footins}\fi +} + +\providetoggle{@PBstabilized} +\providetoggle{@PBimpossible} + +\newtoggle{@PBdraft} +\DeclareOption{draft}{\toggletrue{@PBdraft}} +\ProcessOptions\relax + +% \end{macrocode} + +% \subsection{The balancing front} +% +% The basic balancing process is reasonably simple: right before each +% new page, check whether it should be balanced. If so, add the adequate +% code to the top of the page (either call \verb|\balance|, from the +% \texttt{balance} package, or add \verb|\enlargethispage|). +% +% \begin{macrocode} + +% Right before the first page +\AtBeginDocument{ + \@PBifShouldBalanceNascentPage + {\@PBStartBalancing} + {} +} + +% Right before all other pages +\AtBeginShipout{ + \@PBifShouldBalanceNascentPage + {\@PBStartBalancing} + {} +} + +\newcommand\@PBifShouldBalanceNascentPage[2]{ + \ifboolexpr + { + togl {@PBdraft} + or not togl {@PBstabilized} + or test {\ifdefvoid{\@PBprevLastPage}} + } + {#2} + { + % abspage refers to the finished page, not the nascent page + \ifnumcomp{\@PBprevLastPage - 1}{=}{\value{abspage}} + {#1} + {#2} + } +} + +% Let's give the user a chance to manually define what to do +\newcommand\shrinkLastPage[1]{\dimgdef\@PBslack{#1}} + +\newcommand\@PBStartBalancing{ + \ifdefvoid{\@PBslack} + {\@PBautomaticBalance} + {\@PBshrinkPage} +} + +\newcommand\@PBautomaticBalance{ + \@PBifBalancePossible + { + \@PBifBalancePkgPossible + {\balance} % easy peasy, use the balance package + { + \@PBifPoorBalancePossible + { + % use the poor man's balance algorithm + \@PBcalculateShrinkage + \@PBshrinkPage + } + {} + } + } + {} +} + +% Calculate how much we should shrink the left column of the last +% page based on the measurements we took on previous LaTeX passes. +\newcommand\@PBcalculateShrinkage{ + + % Let's use some shorter names, please + \dimdef\@PBtmpH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}Height}} + \dimdef\@PBtmpUL{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}UsedLeft}} + \dimdef\@PBtmpUR{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}UsedRight}} + + \dimdef\@PBtmpLH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}LeftHeight}} + \dimdef\@PBtmpLFH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}LeftFloatsHeight}} + \dimdef\@PBtmpRH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}RightHeight}} + \dimdef\@PBtmpRFH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}RightFloatsHeight}} + + % Figure out the amount of unused space on the last page (both + % cols); we will reduce the first column height by half that amount + \dimgdef\@PBslack{2\dimexpr\@PBtmpH\relax - \@PBtmpUL - \@PBtmpUR} + \dimgdef\@PBslack{.5\dimexpr\@PBslack\relax} + + % Actually, I lied. While half, as said above, sometimes yields + % perfectly balanced columns, that is often not the case (the page + % may have an odd number of lines, or textheight may not be a + % multiple of baselineskip, or there may be glues etc.). Sometimes + % the difference is very small, less than a line, which does not + % look good at all, and sometimes the right column is slightly + % higher than the left, which is also not ideal. Here, we force + % the left column to always be somewhat taller than the left; the + % result is often ``better''. + \dimgdef\@PBslack{\@PBslack - 1.5\baselineskip} + + % If necessary, reduce @PBslack to prevent LaTeX from adding a page + \@PBsafetyCheck +} + +\def\@PBshrinkPage{ + % Zero obviously means ``do nothing''. We could + % ``enlarge'' by 0, but IDK, maybe that would + % trigger another LaTeX pass or something. + \ifdimcomp{\@PBslack}{=}{0pt} + {} + { + % Shrink the first column + \enlargethispage{-\@PBslack} + + % Modify the behavior of \@outputdblcol + % to raise footnotes in the second column. + \global\let\@PBbalanceSecondColumn\@PBrealBalanceSecondColumn + } +} + +% If there are footnotes on the right column, it is better to +% ``push'' them up to be aligned with the end of the left column, +% especially if there are footnotes on the left column too. We +% do this by manipulating \@textbottom in \@outputdblcol. +\pretocmd{\@outputdblcol}{\@PBbalanceSecondColumn}{}{} + +\let\@PBbalanceSecondColumn\relax + +\def\@PBrealBalanceSecondColumn{ + \if@firstcolumn + % Affects the next column + \global\let\@PBorigtextbottom\@textbottom + \gdef\@textbottom{\vskip \@PBslack plus .0001fil minus 10000fill\relax \@PBorigtextbottom} + \else + % Back to normal before starting the next page + \global\let\@textbottom\@PBorigtextbottom + \global\let\@PBbalanceSecondColumn\relax + \fi +} + +% \end{macrocode} + +% \subsection{The measuring front} +% For the mechanism above to work, we need to know: +% +% \begin{enumerate} +% \item The page number of the last page; +% \item Whether the last page has floats, footnotes etc.; +% \item The available height in the last page; +% \item The height of the columns in the last page. +% \end{enumerate} +% +% Let's collect this info for every page. +% +% \subsubsection{Are there floats, footnotes, or marginpars in the last page?} +% +% \begin{macrocode} + +% To check whether there are floats etc. in the last page, +% we will copy the fancyhdr package: +% https://tex.stackexchange.com/questions/56673/is-there-a-way-to-determine-if-there-is-a-float-on-a-page +% But we add the tests to a different place. +\newcommand\@PBifmidfloat[2]{\ifx\@midlist\empty #2\else #1\fi} +\newcommand\@PBiftopfloat[2]{\ifx\@toplist\empty #2\else #1\fi} +\newcommand\@PBifbotfloat[2]{\ifx\@botlist\empty #2\else #1\fi} +\newcommand\@PBiffootnote[2]{\ifvoid\footins #2\else #1\fi} +\newcommand\@PBiffloatcol[2]{\if@fcolmade #1\else #2\fi} +% dbltop floats are apparently ok with the balance package +%%%\newcommand\@PBifdbltopfloat[2]{\ifx\@dbltoplist\empty #2\else #1\fi} +% dblbotlist only exists with package stfloats +\newcommand\@PBifdblbotfloat[2]{% + \ifdefvoid{\@dblbotlist} + {#2} + {\ifx\@dblbotlist\empty #2\else #1\fi}% +} + +% If there are footnotes, floats or marginpars in +% the page, we should not use the balance package +\newtoggle{@PBtmpHasFootnotes} +\newtoggle{@PBtmpHasFloats} +\newtoggle{@PBtmpHasMarginpars} + +\def\@PBcollectPageInfo{ + + \ifboolexpr + { + test {\@PBifmidfloat} or test {\@PBiftopfloat} or + test {\@PBifbotfloat} or test {\@PBifdblbotfloat} + } + {\global\toggletrue{@PBtmpHasFloats}} + {} + + \@PBiffootnote + {\global\toggletrue{@PBtmpHasFootnotes}} + {} +} + +\pretocmd{\@makecol}{\@PBcollectPageInfo}{}{} + +% This only gets called if there is a marginpar, so that's easy +\pretocmd{\@addmarginpar}{\global\toggletrue{@PBtmpHasMarginpars}}{}{} + +% \end{macrocode} +% +% \subsubsection{What are the sizes of the floats in the left column?} +% +% \begin{macrocode} + +\ExplSyntaxOn +\seq_gclear_new:N \@PBtmpLeftFloatHeights +\seq_gclear_new:N \@PBtmpLeftFloatSpacesBelow +\def\@PBcollectFloatHeights{ + \if@firstcolumn + \dimdef\@PBfloatHeight{\@textfloatsheight - \@PBtmpHeightBefore} + \ifdimcomp{\@PBfloatHeight}{>}{0pt} + { + \global\seq_put_right:NV \@PBtmpLeftFloatHeights \@PBfloatHeight + + \dimdef\@PBspaceBelow{\@colroom - \@pageht - \@PBfloatHeight} + % within \@addtocurcol, \@pageht corresponds to all + % currently used space in the column, including footnotes + % (check \@specialoutput in the LaTeX kernel). Let's + % exclude the footnotes, we only want the space below + % the float. + \ifvoid\footins\else + \dimdef\@PBspaceBelow{\@PBspaceBelow + \ht\footins + \skip\footins + \dp\footins} + \fi + + % No idea why, but this can happen; maybe because of the page depth? + \ifdimcomp{\@PBspaceBelow}{<}{0pt} + {\dimdef\@PBspaceBelow{0pt}} + {} + + \global\seq_put_right:NV \@PBtmpLeftFloatSpacesBelow \@PBspaceBelow + } + {} + \fi +} +\ExplSyntaxOff + +\pretocmd{\@addtocurcol}{\dimgdef{\@PBtmpHeightBefore}{\@textfloatsheight}}{}{} +\apptocmd{\@addtocurcol}{\@PBcollectFloatHeights}{}{} + +% \end{macrocode} +% +% \subsubsection{What is the size of each column? Is there a float column / floatpage?} +% +% \begin{macrocode} + +% If there is a float column in the page, it makes no sense to try to +% balance; if both columns are float columns, this page cannot be the +% final text page, i.e., we want to balance some other page. +\newtoggle{@PBtmpHasFloatcol} +\newtoggle{@PBtmpIsFloatPage} + +\newcommand\@PBcollectColumnInfo{ + + \if@firstcolumn + + \@PBiffloatcol + {\global\toggletrue{@PBtmpHasFloatcol}} + {} + + % Available vertical space excluding + % dblfloats; the same for both columns + \dimgdef\@PBtmpHeight{\@colht} + + % Available vertical space excluding top/bottom floats + \dimgdef\@PBtmpLeftHeight{\@colroom} + + % Space used by \texttt{here} floats + \dimgdef\@PBtmpLeftFloatsHeight{\@textfloatsheight} + + \else + + \@PBiffloatcol + { + \iftoggle{@PBtmpHasFloatcol} + {\global\toggletrue{@PBtmpIsFloatPage}} + {\global\toggletrue{@PBtmpHasFloatcol}} + } + {} + + \dimgdef\@PBtmpRightHeight{\@colroom} + \dimgdef\@PBtmpRightFloatsHeight{\@textfloatsheight} + + \setbox\@tempboxa=\copy\@leftcolumn + \setbox\@tempboxa=\vbox{\unvbox\@tempboxa\unskip\unskip} + \dimgdef\@PBtmpUsedLeft{\ht\@tempboxa\relax} + + \setbox\@tempboxa=\copy\@outputbox + \setbox\@tempboxa=\vbox{\unvbox\@tempboxa\unskip\unskip} + \dimgdef\@PBtmpUsedRight{\ht\@tempboxa\relax} + \fi +} + +% The default LaTeX macro +\pretocmd{\@outputdblcol}{\@PBcollectColumnInfo}{}{} + +% The version from the balance package +\AtEndOfPackage{ + \pretocmd{\@BAdblcol}{\@PBcollectColumnInfo}{}{} + % Revert \balance before next page if it exists (it might be a float page) + \patchcmd{\@BAdblcol}{\endgroup}{\endgroup\nobalance}{}{} +} + +% \end{macrocode} +% +% \subsubsection{Analyze the obtained information} +% +% We have inserted the hooks we need to collect data during +% the processing of each page and column. Now we inspect the +% collected data after each page is finalized. +% +% \begin{macrocode} + +\ExplSyntaxOn +\AtBeginShipout{ + \ifboolexpr + { + % A float page is not the last text page, ignore + togl {@PBtmpIsFloatPage} + or + % A page with a float column on the left and an empty + % column on the right is not the last text page, ignore + ( + togl {@PBtmpHasFloatcol} + and + test {\ifdimcomp{\@PBtmpUsedRight}{=}{\topskip}} + ) + } + {} + { + % This page is a candidate to be the last page + \xdef\@PBlastPage{\the\value{abspage}} + \@PBcopyPageData{tmp}{candidate} + } + + % Reset before processing next page + \global\togglefalse{@PBtmpHasFloats} + \global\togglefalse{@PBtmpHasFloatcol} + \global\togglefalse{@PBtmpHasFootnotes} + \global\togglefalse{@PBtmpHasMarginpars} + \global\togglefalse{@PBtmpIsFloatPage} + + \seq_gclear_new:N \@PBtmpLeftFloatHeights + \seq_gclear_new:N \@PBtmpLeftFloatSpacesBelow +} +\ExplSyntaxOff + +% \end{macrocode} +% +% All pages processed, the last candidate page is actually the last page. +% Instead of continuing to use the \texttt{@PBcandidate*} macros we already +% have, we will keep the information we just gathered associated with the +% page number. We do this because, in the future, we may want to add suport +% for balancing multiple pages (such as the last page of each chapter) and +% not only the last one. This also makes it easier to handle the situation +% where the document was modified and the last page has changed. +% +% \begin{macrocode} + +\AfterLastShipout{\@PBcopyPageData{candidate}{pg\@Roman{\@PBlastPage}}} + +% \end{macrocode} +% +% \subsubsection{Saving measurements} +% The whole document has already been processed and data about the last +% page has been collected in macros with names \texttt{@PBpgNUM*}. We now +% process this collected data and save whatever is relevant in the aux +% file for the next \LaTeX{} pass. +% +% In the first \LaTeX{} pass, we will save data to the aux file in macros +% with names \texttt{@PBunbalpgNUM*}. That is all we need for balancing. +% However, to detect whether the document has changed and, therefore, +% whether we should re-balance, we will also record the same data after +% balancing (during the second pass) in macros with names +% \texttt{@PBbalpgNUM*}. If, in the third or later \LaTeX{} passes, +% \texttt{@PBbalpgNUM*} differ from \texttt{@PBpgNUM*}, the document +% has been changed and we need to re-balance. +% +% So, we have: +% +% \begin{description} +% \item[\texttt{@PBpgNUM*}] data collected during the current pass; +% \item[\texttt{@PBunbalpgNUM*}] data collected in the first pass; +% \item[\texttt{@PBbalpgNUM*}] data collected in the second pass (after +% balancing); in subsequent passes, these should always be equal +% to \texttt{@PBpgNUM*}. +% \end{description} +% +% The last two are saved to / read from the aux file. +% +% \begin{macrocode} + +% Make sure the document has stabilized before using \@PBmanageBalancingPasses +\AfterLastShipout{ + % Reset \@PBimpossible if we are in draft mode, + % so the user can start over if they want to. + \iftoggle{@PBdraft}{\togglefalse{@PBimpossible}}{} + + \ifboolexpr{togl {@PBdraft} or togl {@PBimpossible}} + {} + { + \ifdefvoid{\@PBprevLastPage} + {\@PBsaveUnbalancedInfo\@PBnotifyRerun} % First pass + { + \iftoggle{@PBstabilized} + {\@PBmanageBalancingPasses} + { + \@PBifSomethingChanged[unbal] + % Document still changing; discard data from previous pass + {\@PBsaveUnbalancedInfo[update]\@PBnotifyRerun} + % No changes, so let's balance from now on (once @PBstabilized + % becomes true, it never reverts back to false, unless the aux + % file is deleted). + {\toggletrue{@PBstabilized}\@PBsaveUnbalancedInfo\@PBnotifyRerun} + } + } + \@PBsaveToggle{@PBstabilized} + } + + \@PBsaveToggle{@PBimpossible} + \immediate\write\@mainaux{\gdef\string\@PBprevLastPhysPage{\the\value{abspage}}} +} + +\newcommand\@PBmanageBalancingPasses{ + % This is the second pass or later after we started balancing + + \providetoggle{@PBpg\@Roman{\@PBlastPage}alreadyBalanced} + + \iftoggle{@PBpg\@Roman{\@PBlastPage}alreadyBalanced} + { + % Third pass or later; document was already + % balanced, so there should be no more changes + \@PBifSomethingChanged + % The document has changed, so we need to measure + % stuff again; clear aux file and start over. + {\@PBnotifyRerun} + % Nothing changed, just write the same info down + {\@PBsaveUnbalancedInfo\@PBsaveBalancedInfo} + } + % Second pass, which means something did change: we have just + % balanced the columns for the first time. + { + \xdef\@tempa{\the\value{abspage}} + \ifboolexpr + { + test {\ifdefstrequal{\@PBprevLastPage}{\@PBlastPage}} + and + test {\ifdefstrequal{\@PBprevLastPhysPage}{\@tempa}} + } + % All is well; save the new info + {\@PBsaveUnbalancedInfo\@PBsaveBalancedInfo} + % Oh, no! As we attempted to balance, we actually changed + % the number of pages (maybe we added a float page, maybe + % we turned the last float page into a text page etc.). If + % we continue, the document might never converge (endless + % ``please rerun LaTeX'' messages). Let's give up balancing. + {\toggletrue{@PBimpossible}} + } +} + +% \end{macrocode} +% +% \subsection{Auxiliary macros} +% +% \begin{macrocode} + +\newcommand\@PBifBalancePossible[2]{ + \iftoggle{@PBunbalpg\@Roman{\@PBprevLastPage}HasFloatcol} + {#2} + {#1} +} + +% The balance package may fail badly in the presence of footnotes: +% they may end up in the middle of the text. Marginpars may end up +% on the wrong side of the page. Floats are usually ok, but in some +% cases the columns may become very badly balanced. For ordinary +% text, it works perfectly. +\newcommand\@PBifBalancePkgPossible[2]{ + \ifboolexpr + { + togl {@PBunbalpg\@Roman{\@PBprevLastPage}HasFloats} + or + togl {@PBunbalpg\@Roman{\@PBprevLastPage}HasFootnotes} + or + togl {@PBunbalpg\@Roman{\@PBprevLastPage}HasMarginpars} + } + {#2} + {#1} +} + +% \end{macrocode} +% +% We only want to balance if: +% +% \begin{enumerate} +% +% \item The left column is higher than the right one. If that is not the +% case, either things are already balanced or the right column is +% higher, which means \LaTeX{} was forced into a bad solution already. +% +% \item This difference is ``large enough'' (6 lines), which means two things: +% \begin{enumerate} +% +% \item There will be an actual improvement in the result. +% +% \item There is plenty of free space on the right column, so even if +% our changes happen to take more space than before, \LaTeX{} will +% hopefully not add a new page (but it might make the right +% column higher than the left, which is not so great). +% \end{enumerate} +% +% \end{enumerate} +% +% If there is material only on the left column, we want to balance even +% if the difference in height is not ``large enough'', as long as there +% are more than 4 lines to split among columns. This is actually quite +% unlikely: if there is so little material, the page probably has only +% normal text and we will use the balance package. +% +% \begin{macrocode} + +\newcommand\@PBifPoorBalancePossible[2]{ + + % Let's use some shorter names, please + \dimdef\@PBtmpH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}Height}} + \dimdef\@PBtmpUL{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}UsedLeft}} + \dimdef\@PBtmpUR{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}UsedRight}} + + \dimdef\@PBtmpLH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}LeftHeight}} + \dimdef\@PBtmpLFH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}LeftFloatsHeight}} + \dimdef\@PBtmpRH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}RightHeight}} + \dimdef\@PBtmpRFH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}RightFloatsHeight}} + + \ifboolexpr{ + % Plenty of space on right column, difference is worth it + test {\ifdimcomp{\@PBtmpUL}{>}{\@PBtmpUR + 6\baselineskip}} + or + ( + test {\ifdimcomp{\@PBtmpUL}{>}{4\baselineskip}} + and + test {\ifdimcomp{\@PBtmpUR}{=}{\topskip}} % column is empty + ) + }{#1}{#2} +} + +% \end{macrocode} +% +% If there are floats, things can get messy: imagine that we want to +% shrink by 50pt, but there is a 250pt float near the end of the left +% column that won't fit after we shrink, so it will move to the right. +% What if there is no room for such a large float there? Here, we try +% to prevent this situation and shrink by a smaller amount if there +% is a float that won't fit. Note that, if the float goes to the +% right column, there will be more room for text in the left column. +% Therefore, a float will not fit only if the floats on the right +% column take too much space. So, what we do here is, starting from +% the bottom, check whether the accumulated height of the floats +% from the left column fit in the right. If we find a float that +% will not fit, we know that we should limit shrinking to the space +% below that float. +% +% In the same vein, we do not want top/bottom floats from the left +% column that were deferred from a previous page to be pushed to +% the right column. To that end, we limit the shrinking amount to +% the space on the left column not taken by top/bottom floats. + +% \textbf{TODO:} Top and bottom floats may appear in two cases: (1) they +% were generated on a previous page and deferred or (2) the user +% chose placement options \texttt{t} or \texttt{b}, without +% \texttt{h}. Deferred floats are not a problem: we do not reduce +% the space available for top/bottom floats, so they should +% continue to fit. Floats on the left column with placement +% options \texttt{t} and \texttt{b} without \texttt{h} can be +% a problem: \LaTeX{} only considers putting a float on the +% top/bottom of the current column if, at the point of its +% definition, there is enough space for it. Since we are shrinking +% the column, it may not fit and be deferred - something the +% algorithm does not take into consideration. However, not all is +% lost: most likely, the user will only use such placement +% options when trying to manually adjust the page, which +% means they will be able to detect and work around eventual +% problems. +% +% \begin{macrocode} + +\ExplSyntaxOn +\def\@PBsafetyCheck{ + + % Let's use some shorter names, please + \dimdef\@PBtmpH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}Height}} + \dimdef\@PBtmpUL{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}UsedLeft}} + \dimdef\@PBtmpUR{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}UsedRight}} + + \dimdef\@PBtmpLH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}LeftHeight}} + \dimdef\@PBtmpLFH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}LeftFloatsHeight}} + \dimdef\@PBtmpRH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}RightHeight}} + \dimdef\@PBtmpRFH{\csuse{@PBunbalpg\@Roman{\@PBprevLastPage}RightFloatsHeight}} + + + % Make sure we do not push top/bottom floats out of the left col + \dimdef\@PBmaxSlack{\@PBtmpLH} + + \dimdef\@PBtmpdim{0pt} + + % Start from the bottom + \numdef{\@PBtmpcnt}{\seq_count:c {@PBunbalpg\@Roman{\@PBprevLastPage}LeftFloatHeights}} + \int_while_do:nNnn {\@PBtmpcnt} > {0} + { + + \dimdef\@PBcurrentFloatHeight + {\seq_item:cn {@PBunbalpg\@Roman{\@PBprevLastPage}LeftFloatHeights} {\@PBtmpcnt}} + + \dimdef\@PBcurrentFloatSpaceBelow + {\seq_item:cn {@PBunbalpg\@Roman{\@PBprevLastPage}LeftFloatSpacesBelow} {\@PBtmpcnt}} + + \dimdef{\@PBtmpdim}{\@PBtmpdim + \@PBcurrentFloatHeight} + + \ifboolexpr + { + % Does the float fit in the second column? + test {\ifdimcomp{\@PBtmpdim}{<}{\@PBtmpRH - \@PBtmpRFH}} + and + % Is there something above this float, i.e., if we move + % this float to the next column, will anything remain? + test + { + \ifdimcomp + {\@PBcurrentFloatSpaceBelow + \@PBcurrentFloatHeight} + {<} + {\@PBtmpLH - 6\baselineskip} + } + } + {\numdef{\@PBtmpcnt}{\@PBtmpcnt - 1}} % this one is ok; check next + { + % this one should not be moved, we found the limit + \ifdimcomp{\@PBmaxSlack}{>}{\@PBcurrentFloatSpaceBelow} + {\dimdef{\@PBmaxSlack}{\@PBcurrentFloatSpaceBelow}} + {} + \numdef{\@PBtmpcnt}{0} % exit the loop + } + } + + \ifdimcomp{\@PBmaxSlack}{<}{\@PBslack} + {\dimgdef{\@PBslack}{\@PBmaxSlack}} + {} +} +\ExplSyntaxOff + +% Compare current measurements with previous ``bal'' or ``unbal'' measurements +\newcommand\@PBifSomethingChanged[3][bal]{ + \ifdefstrequal{\@PBprevLastPage}{\@PBlastPage} + { + \@PBifPageDataMatches + {pg\@Roman{\@PBlastPage}} + {#1pg\@Roman{\@PBlastPage}} + {#3} + {#2} + } + {#2} +} + +\newcommand\@PBifPageDataMatches[4]{ + \ifboolexpr{ + test {\@PBifTogglesMatch{@PB#1HasFloats}{@PB#2HasFloats}} + and test {\@PBifTogglesMatch{@PB#1HasFloatcol}{@PB#2HasFloatcol}} + and test {\@PBifTogglesMatch{@PB#1HasFootnotes}{@PB#2HasFootnotes}} + and test {\@PBifTogglesMatch{@PB#1HasMarginpars}{@PB#2HasMarginpars}} + and test {\@PBifDimensionsMatch{@PB#1Height}{@PB#2Height}} + and test {\@PBifDimensionsMatch{@PB#1LeftHeight}{@PB#2LeftHeight}} + and test {\@PBifDimensionsMatch{@PB#1RightHeight}{@PB#2RightHeight}} + and test {\@PBifDimensionsMatch{@PB#1UsedLeft}{@PB#2UsedLeft}} + and test {\@PBifDimensionsMatch{@PB#1UsedRight}{@PB#2UsedRight}} + } + {#3} + {#4} +} + +% https://en.wikipedia.org/wiki/XNOR_gate +\newcommand\@PBifTogglesMatch[4]{ + \ifboolexpr{(togl{#1} or not togl{#2}) and (togl{#2} or not togl{#1})} + {#3} + {#4} +} + +\newcommand\@PBifDimensionsMatch[4]{ + \ifdimcomp{\csuse{#1}}{=}{\csuse{#2}} + {#3} + {#4} +} + +\newcommand\@PBsaveUnbalancedInfo[1][]{ + + \immediate\write\@mainaux{\gdef\string\@PBprevLastPage{\@PBlastPage}} + + \ifboolexpr{test {\ifdefvoid{\@PBprevLastPage}} or test {\ifstrequal{#1}{update}}} + % Columns are currently unbalanced, either because this is + % the first pass (we do not have any previous information) + % or because the document has not stabilized yet, so any + % previous information is unreliable. Save the information + % we just collected as ``unbalanced''. + {\@PBsavePageDataAs{pg\@Roman{\@PBlastPage}}{unbalpg\@Roman{\@PBlastPage}}} + % Not the first pass, so columns are possibly balanced; + % to save the unbalanced information, repeat what was + % gathered during the first pass. + {\@PBsavePageData{unbalpg\@Roman{\@PBprevLastPage}}} +} + +\def\@PBsaveBalancedInfo{ + \global\toggletrue{@PBpg\@Roman{\@PBlastPage}alreadyBalanced} + \@PBsaveToggle{@PBpg\@Roman{\@PBlastPage}alreadyBalanced} + + \@PBsavePageDataAs{pg\@Roman{\@PBlastPage}}{balpg\@Roman{\@PBlastPage}} +} + +\def\@PBnotifyRerun{ + \AtVeryEndDocument{ + \PackageWarningNoLine{pbalance} + {Last page columns not balanced. Rerun LaTeX} + } +} + +\ExplSyntaxOn +\newcommand\@PBcopyPageData[2]{ + + \global\providetoggle{@PB#2HasFloats} + \global\providetoggle{@PB#2HasFloatcol} + \global\providetoggle{@PB#2HasFootnotes} + \global\providetoggle{@PB#2HasMarginpars} + + \iftoggle{@PB#1HasFloats} + {\global\toggletrue{@PB#2HasFloats}} + {\global\togglefalse{@PB#2HasFloats}} + + \iftoggle{@PB#1HasFloatcol} + {\global\toggletrue{@PB#2HasFloatcol}} + {\global\togglefalse{@PB#2HasFloatcol}} + + \iftoggle{@PB#1HasFootnotes} + {\global\toggletrue{@PB#2HasFootnotes}} + {\global\togglefalse{@PB#2HasFootnotes}} + + \iftoggle{@PB#1HasMarginpars} + {\global\toggletrue{@PB#2HasMarginpars}} + {\global\togglefalse{@PB#2HasMarginpars}} + + \csdimgdef{@PB#2Height}{\csuse{@PB#1Height}} + \csdimgdef{@PB#2LeftHeight}{\csuse{@PB#1LeftHeight}} + \csdimgdef{@PB#2LeftFloatsHeight}{\csuse{@PB#1LeftFloatsHeight}} + \csdimgdef{@PB#2RightHeight}{\csuse{@PB#1RightHeight}} + \csdimgdef{@PB#2RightFloatsHeight}{\csuse{@PB#1RightFloatsHeight}} + \csdimgdef{@PB#2UsedLeft}{\csuse{@PB#1UsedLeft}} + \csdimgdef{@PB#2UsedRight}{\csuse{@PB#1UsedRight}} + + \seq_gclear_new:c {@PB#2LeftFloatHeights} + \seq_gclear_new:c {@PB#2LeftFloatSpacesBelow} + \seq_gset_eq:cc {@PB#2LeftFloatHeights} {@PB#1LeftFloatHeights} + \seq_gset_eq:cc {@PB#2LeftFloatSpacesBelow} {@PB#1LeftFloatSpacesBelow} +} +\ExplSyntaxOff + +\newcommand\@PBsavePageDataAs[2]{ + \@PBsaveDimAs{@PB#1Height}{@PB#2Height} + \@PBsaveDimAs{@PB#1LeftHeight}{@PB#2LeftHeight} + \@PBsaveDimAs{@PB#1LeftFloatsHeight}{@PB#2LeftFloatsHeight} + \@PBsaveDimAs{@PB#1RightHeight}{@PB#2RightHeight} + \@PBsaveDimAs{@PB#1RightFloatsHeight}{@PB#2RightFloatsHeight} + \@PBsaveDimAs{@PB#1UsedLeft}{@PB#2UsedLeft} + \@PBsaveDimAs{@PB#1UsedRight}{@PB#2UsedRight} + + \@PBsaveToggleAs{@PB#1HasFloats}{@PB#2HasFloats} + \@PBsaveToggleAs{@PB#1HasFloatcol}{@PB#2HasFloatcol} + \@PBsaveToggleAs{@PB#1HasFootnotes}{@PB#2HasFootnotes} + \@PBsaveToggleAs{@PB#1HasMarginpars}{@PB#2HasMarginpars} + + \@PBsaveSeqAs{@PB#1LeftFloatHeights}{@PB#2LeftFloatHeights} + \@PBsaveSeqAs{@PB#1LeftFloatSpacesBelow}{@PB#2LeftFloatSpacesBelow} +} + +\newcommand\@PBsavePageData[1]{ + \@PBsavePageDataAs{#1}{#1} +} + +\newcommand\@PBsaveToggleAs[2]{ + \immediate\write\@mainaux{\providetoggle{#2}} + \iftoggle{#1} + {\immediate\write\@mainaux{\global\toggletrue{#2}}} + {\immediate\write\@mainaux{\global\togglefalse{#2}}} +} + +\newcommand\@PBsaveToggle[1]{ + \@PBsaveToggleAs{#1}{#1} +} + +\newcommand\@PBsaveDimAs[2]{ + \immediate\write\@mainaux{\csdimgdef{#2}{\csuse{#1}}} +} + +\newcommand\@PBsaveDim[1]{ + \@PBsaveDimAs{#1}{#1} +} + +\ExplSyntaxOn +\newcommand\@PBsaveSeqAs[2]{ + \immediate\write\@mainaux{\string\ExplSyntaxOn} + \immediate\write\@mainaux{\string\seq_gclear_new:c {#2}} + \seq_map_inline:cn {#1} + {\immediate\write\@mainaux{\string\global\string\seq_put_right:cn {#2}{##1}}} + \immediate\write\@mainaux{\string\ExplSyntaxOff} +} +\ExplSyntaxOff + +\newcommand\@PBsaveSeq[1]{ + \@PBsaveSeqAs{#1}{#1} +} + +% \end{macrocode} +% \Finale diff --git a/macros/latex/contrib/pbalance/pbalance.ins b/macros/latex/contrib/pbalance/pbalance.ins new file mode 100644 index 0000000000..2e7d2a2db8 --- /dev/null +++ b/macros/latex/contrib/pbalance/pbalance.ins @@ -0,0 +1,49 @@ +%% Copyright 2020 Nelson Lago <lago@ime.usp.br> +%% +%% This work may be distributed and/or modified under the conditions of the +%% LaTeX Project Public License, either version 1.3 of this license or (at +%% your option) any later version. The latest version of this license can be +%% found at http://www.latex-project.org/lppl.txt and version 1.3 or later +%% is part of all distributions of LaTeX version 2005/12/01 or later. +%% +%% This work has the LPPL maintenance status `maintained'. +%% +%% The Current Maintainer of this work is Nelson Lago <lago@ime.usp.br>. + +\input docstrip.tex +\keepsilent +\usedir{tex/latex/pbalance} + +\preamble +Copyright 2020 Nelson Lago <lago@ime.usp.br> + +This work may be distributed and/or modified under the conditions of the +LaTeX Project Public License, either version 1.3 of this license or (at +your option) any later version. The latest version of this license can be +found at http://www.latex-project.org/lppl.txt and version 1.3 or later +is part of all distributions of LaTeX version 2005/12/01 or later. + +This work has the LPPL maintenance status `maintained'. + +The Current Maintainer of this work is Nelson Lago <lago@ime.usp.br>. + +\endpreamble + +\generate{\file{pbalance.sty}{\from{pbalance.dtx}{package}}} + +\obeyspaces +\Msg{****************************************************} +\Msg{* *} +\Msg{* To finish the installation you have to move the *} +\Msg{* following file into a directory searched by TeX: *} +\Msg{* *} +\Msg{* pbalance.sty *} +\Msg{* *} +\Msg{* To produce the documentation run the file *} +\Msg{* pbalance.dtx through LaTeX. *} +\Msg{* *} +\Msg{* Happy TeXing! *} +\Msg{* *} +\Msg{****************************************************} + +\endbatchfile diff --git a/macros/latex/contrib/pbalance/pbalance.pdf b/macros/latex/contrib/pbalance/pbalance.pdf Binary files differnew file mode 100644 index 0000000000..319570a8dd --- /dev/null +++ b/macros/latex/contrib/pbalance/pbalance.pdf diff --git a/macros/latex/contrib/rest-api/README.md b/macros/latex/contrib/rest-api/README.md index a092c4b8dd..92a058bdd6 100644 --- a/macros/latex/contrib/rest-api/README.md +++ b/macros/latex/contrib/rest-api/README.md @@ -3,4 +3,4 @@ LaTeX package for describing rest apis Developed by Dennis Klein <dennis94.kl@gmail.com> -This material is subject to the LATEX Project Public License 1.3c.
\ No newline at end of file +This material is subject to the LATEX Project Public License 1.3c. diff --git a/macros/latex/contrib/rest-api/rest-api.dtx b/macros/latex/contrib/rest-api/rest-api.dtx index db450b183b..955f744c7e 100644 --- a/macros/latex/contrib/rest-api/rest-api.dtx +++ b/macros/latex/contrib/rest-api/rest-api.dtx @@ -22,7 +22,7 @@ %<package>\NeedsTeXFormat{LaTeX2e}[1999/12/01] %<package>\ProvidesPackage{rest-api} %<*package> - [2020/06/23 v1.3 rest-api package] + [2020/12/04 v1.4 rest-api package] %</package> % %<*driver> @@ -40,7 +40,7 @@ %</driver> % \fi % -% \CheckSum{424} +% \CheckSum{514} % % \CharacterTable % {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z @@ -59,6 +59,7 @@ % Right brace \} Tilde \~} % % +% \changes{v1.4}{2020/12/04}{Add xml syntax highlighting support for request and response body} % \changes{v1.3}{2020/06/23}{Fix typo and table coloring after apiRoute block} % \changes{v1.2}{2020/03/22}{Corrections for CTAN, rename to rest-api} % \changes{v1.1}{2020/03/21}{Add documentation and usage} @@ -270,6 +271,34 @@ %</example> % \fi +% \paragraph{DELETE Request xml} \ +% \iffalse +%<*example> +% \fi +\begin{lstlisting}[frame=single,breaklines=true] + \begin{apiRoute}{delete}{/api/storage/\{id\}}{remove a storage with id} + \begin{routeParameter} + \routeParamItem{id}{id of storage} + \end{routeParameter} + \begin{routeResponse}{application/xml} + \begin{routeResponseItem}{200}{ok} + + \end{routeResponseItem} + \begin{routeResponseItem}{404}{error: storage not found} + \begin{routeResponseItemBody} + <message> + storage with id '11' not found! + </message> + \end{routeResponseItemBody} + \end{routeResponseItem} + \end{routeResponse} + \end{apiRoute} +\end{lstlisting} +% \iffalse +%</example> +% \fi + + % % % @@ -363,7 +392,10 @@ % Return the border color of the current route. % % \DescribeMacro{\methodJson} -% Set the with \\lstset color scheme for the listings of the current route. +% Set the color scheme and language with \\lstset for the listings of the current route to json. +% +% \DescribeMacro{\methodXml} +% Set the color scheme and language with \\lstset for the listings of the current route to xml. % % @@ -504,6 +536,7 @@ % \end{macrocode} % \end{macro} % +% \begin{macro}{color definitions for json} % \begin{macrocode} \colorlet{punct}{black} \definecolor{background}{HTML}{EEEEEE} @@ -515,6 +548,11 @@ \colorlet{numbPut}{delimPut} \colorlet{delimDelete}{\getDeleteColor} \colorlet{numbDelete}{delimDelete} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{json language defintions} +% \begin{macrocode} \lstdefinelanguage{jsonGet}{ basicstyle=\small\ttfamily\color{black}, numberstyle=\small\color{black}, @@ -655,6 +693,105 @@ {[}{{{\color{delimDelete}{[}}}}{1} {]}{{{\color{delimDelete}{]}}}}{1}, } +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{xml language defintions} +% \begin{macrocode} +\lstdefinelanguage{xmlGet}{ + basicstyle=\small\ttfamily\color{black}, + numberstyle=\small\color{numbGet}, + keywordstyle=\color{black}, + commentstyle=\color{black}, + stringstyle=\color{black}, + frame=L, + framesep=8pt, + framerule=2pt, + xleftmargin=50pt, + xrightmargin=0pt, + rulesep=0pt, + backgroundcolor=\color{white}, + numbersep=8pt, + tabsize=4, + showstringspaces=false, + breaklines=false, + morestring=[b]", + morestring=[s]{>}{<}, + morecomment=[s]{<?}{?>}, + identifierstyle=\color{numbGet}, +} +\lstdefinelanguage{xmlPost}{ + basicstyle=\small\ttfamily\color{black}, + numberstyle=\small\color{numbPost}, + keywordstyle=\color{black}, + commentstyle=\color{black}, + stringstyle=\color{black}, + frame=L, + framesep=8pt, + framerule=2pt, + xleftmargin=50pt, + xrightmargin=0pt, + rulesep=0pt, + backgroundcolor=\color{white}, + numbersep=8pt, + tabsize=4, + showstringspaces=false, + breaklines=false, + morestring=[b]", + morestring=[s]{>}{<}, + morecomment=[s]{<?}{?>}, + identifierstyle=\color{numbPost}, +} +\lstdefinelanguage{xmlPut}{ + basicstyle=\small\ttfamily\color{black}, + numberstyle=\small\color{numbPut}, + keywordstyle=\color{black}, + commentstyle=\color{black}, + stringstyle=\color{black}, + frame=L, + framesep=8pt, + framerule=2pt, + xleftmargin=50pt, + xrightmargin=0pt, + rulesep=0pt, + backgroundcolor=\color{white}, + numbersep=8pt, + tabsize=4, + showstringspaces=false, + breaklines=false, + morestring=[b]", + morestring=[s]{>}{<}, + morecomment=[s]{<?}{?>}, + stringstyle=\color{black}, + identifierstyle=\color{numbPut}, +} +\lstdefinelanguage{xmlDelete}{ + basicstyle=\small\ttfamily\color{black}, + numberstyle=\small\color{numbDelete}, + keywordstyle=\color{black}, + commentstyle=\color{black}, + stringstyle=\color{black}, + frame=L, + framesep=8pt, + framerule=2pt, + xleftmargin=50pt, + xrightmargin=0pt, + rulesep=0pt, + backgroundcolor=\color{white}, + numbersep=8pt, + tabsize=4, + showstringspaces=false, + breaklines=false, + morestring=[b]", + morestring=[s]{>}{<}, + morecomment=[s]{<?}{?>}, + identifierstyle=\color{numbDelete}, +} +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{apiRoute frame style defintion} +% \begin{macrocode} \mdfdefinestyle{mystyle}{ linecolor=\methodBorderColor, backgroundcolor=\methodLightColor, @@ -670,6 +807,15 @@ nobreak=true } % \end{macrocode} +% \end{macro} +% +% +% \begin{macro}{\bodyFormat} +% \begin{macrocode} +\newcommand{\bodyFormat}{json} +% \end{macrocode} +% \end{macro} +% % % \begin{macro}{\routeBreakValue} % \begin{macrocode} @@ -733,21 +879,21 @@ { \lstset{language=jsonGet,rulecolor=\color{\methodColor}} } -\begin{comment} - \newcommand{\apiCorner}{notround} - - \newcommand{\apiCornerRound} - { - \renewcommand{\apiCorner}{round} - } - - \newcommand{\apiCornerNotRound} - { - \renewcommand{\apiCorner}{notround} - } -\end{comment} % \end{macrocode} % +% \begin{macro}{\methodXml} +% \begin{macrocode} +\newcommand{\methodXml} +% \end{macrocode} +% \end{macro} +% +% \begin{macrocode} +{ + \lstset{language=xmlGet,rulecolor=\color{\methodColor}} +} +% \end{macrocode} +% +% % \begin{environment}{apiRoute} % \begin{macrocode} \newenvironment{apiRoute}[3] @@ -768,6 +914,12 @@ \colorlet{numbGet}{delimGet} \lstset{language=jsonGet,rulecolor=\color{\methodColor}} } + \renewcommand{\methodXml} + { + \colorlet{delimGet}{\methodColor} + \colorlet{numbGet}{delimGet} + \lstset{language=xmlGet,rulecolor=\color{\methodColor}} + } } {} @@ -782,6 +934,12 @@ \colorlet{numbPost}{delimPost} \lstset{language=jsonPost,rulecolor=\color{\methodColor}} } + \renewcommand{\methodXml} + { + \colorlet{delimPost}{\methodColor} + \colorlet{numbPost}{delimPost} + \lstset{language=xmlPost,rulecolor=\color{\methodColor}} + } } {} @@ -796,6 +954,12 @@ \colorlet{numbPut}{delimPut} \lstset{language=jsonPut,rulecolor=\color{\methodColor}} } + \renewcommand{\methodXml} + { + \colorlet{delimPut}{\methodColor} + \colorlet{numbPut}{delimPut} + \lstset{language=xmlPut,rulecolor=\color{\methodColor}} + } } {} \ifthenelse{\equal{\method}{delete}} @@ -809,6 +973,12 @@ \colorlet{numbDelete}{delimDelete} \lstset{language=jsonDelete,rulecolor=\color{\methodColor}} } + \renewcommand{\methodXml} + { + \colorlet{delimDelete}{\methodColor} + \colorlet{numbDelete}{delimDelete} + \lstset{language=xmlDelete,rulecolor=\color{\methodColor}} + } } {} @@ -857,6 +1027,8 @@ % \begin{macrocode} \newenvironment{routeRequest}[1] { + \renewcommand{\bodyFormat}{#1} + \arrayrulecolor{\methodColor} \begin{tabularx}{\textwidth}{X l} @@ -874,7 +1046,7 @@ } \lstnewenvironment{routeRequestBody} { - \methodJson + \IfSubStr{\bodyFormat}{xml}{\methodXml}{\methodJson} } {} % \end{macrocode} @@ -915,6 +1087,8 @@ % \begin{macrocode} \newenvironment{routeResponse}[1] { + \renewcommand{\bodyFormat}{#1} + \newcommand{\noRouteResponse}[1] { \begin{tabularx}{\textwidth}{X} @@ -954,7 +1128,7 @@ \lstnewenvironment{routeResponseItemBody} { \vspace{-1.4em} - \methodJson + \IfSubStr{\bodyFormat}{xml}{\methodXml}{\methodJson} } {} \endinput diff --git a/macros/latex/contrib/rest-api/rest-api.ins b/macros/latex/contrib/rest-api/rest-api.ins index eba18ecd93..92070c1572 100644 --- a/macros/latex/contrib/rest-api/rest-api.ins +++ b/macros/latex/contrib/rest-api/rest-api.ins @@ -16,7 +16,7 @@ \Msg{************************************************************************} \Msg{* Installation *} -\Msg{* Package: rest-api 2020/03/22 v1.2 *} +\Msg{* Package: rest-api 2020/12/04 v1.4 *} \Msg{************************************************************************} \keepsilent @@ -58,4 +58,4 @@ version 2008/05/04 or later. \Msg{* *} \Msg{*************************************************************} -\endbatchfile
\ No newline at end of file +\endbatchfile diff --git a/macros/latex/contrib/rest-api/rest-api.pdf b/macros/latex/contrib/rest-api/rest-api.pdf Binary files differindex 90ddffa791..0d3fb391f7 100644 --- a/macros/latex/contrib/rest-api/rest-api.pdf +++ b/macros/latex/contrib/rest-api/rest-api.pdf |